Commit bb8b5d15 authored by liyan's avatar liyan
parents 4768b615 a11c2216
Pipeline #16611 failed with stage
---
title: JavaScript中Worker初探
date: 2022/8/30 17:20:00
author: 张顺
---
## 什么是Worker?他有什么作用?
因为js是单线程运行的,在遇到一些需要处理大量耗时的数据时,可能会阻塞页面的加载,造成面的假死。针对这个问题,H5出了新的功能worker,支持多线程,这时我们可以使用worker来开辟一个独立于主线程的子线程来进行一些耗时的大量运算。就可以用来解决因为大量耗时运算是造成页面假死的问题,详细内容查看![Worker](https://developer.mozilla.org/zh-CN/docs/Web/API/Worker)
### 如何使用Worker
```js
//首先创建 Worker 对象,第一个参数 worker 需要执行的 JS 文件的 URL。相对地址 URL 是相对于创建 Worker 的脚本所在文档(宿主文档)的访问地址。绝对地址 URL 必须要和宿主文档同源。
//第二个参数是可选参数,可以是一个对象,name属性,可以给当前Worker定义一个名称,可用来区分其他的worker
const worker = new Worker("worker.js",{name:'自定义的名称'})
//通过onmessage ,监听子线程的消息
worker.onmessage((e)=>`接收worker线程传递值:${e.data}`)
//通过postMessage ,向子线程发送消息
worker.postMessage('向子线程发送消息')
//worker.js 文件
//通过self.onmessage,来接收主线程传递的信息
self.onmessage((e)=>{
console.log(e)
})
//通过self.postMessage,来向主线程传递信息
self.postMessage('要传递的值')
```
### 方法和属性
主线程worker()对象,用来供主线程操作 Worker。worker 线程对象的属性和方法如下:
- worker.onerror:指定 error 事件的监听函数。
- worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
- worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
- worker.postMessage():向 Worker 线程发送消息。
- worker.terminate():立即终止 Worker 线程。
worker 子线程全局属性和方法:
- self.onmessage:指定message事件的监听函数。
- self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
- self.close():关闭 worker 线程。
- self.postMessage():向产生这个 worker 线程发送消息。
- self.importScripts():加载 JS 脚本。
- self.name: worker 的名字。该属性只读,由构造函数指定。
### worker 使用注意点
1. 同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
2. DOM 限制
worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
3. 通信联系
worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息传递完成。
4. 脚本限制
worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
5. 文件限制
worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
### 简单使用demo
模拟页面中插入耗时处理数据然后渲染元素,暂时不考虑页面渲染性能问题
```js
(() => {
let arrList = []  
let indexNum = 1000
for (let index = 0; index < indexNum; index++) {
let obj = {
name: index + 1
}
arrList.push(obj)
if (arrList.length == indexNum) {
//在子线程中,self代表worker子线程的全局对象,也可以用this代替self,或者省略也行
//this.postMessage(arrList);
//self.postMessage(arrList);
postMessage(arrList); //向主线程发送消息
}
onmessage = function(e) {
console.log(e.data)
console.log("我是worker线程接收到了你的信息,不客气,我就是做耗时操作的")
console.log("2妙后我还会给你发送个信息")
setTimeout(() => {
postMessage('如果还有耗时的操作,你还可以在开启一个worker线程呦')
}, 2000)
}
//worker自己关闭
// self.close()
}
})()
```
``` html
<!DOCTYPE html>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<html>
<body>
<div id="bt">
<p style="color:red">模拟元素1</p>
<p style="color:red">模拟元素2</p>
<p style="color:red">模拟元素3</p>
<p style="color:red">模拟元素4</p>
<div class="ld" style="color:green">数据加载中。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。</div>
<div class="main"></div>
<p style="color:red">模拟元素5</p>
<p style="color:red">模拟元素6</p>
<p style="color:red">模拟元素7</p>
</div>
<script>
// 引入的js文件必须和当前页面同源,不可跨域
let w = new Worker("./worker2.js");
$(".ld").show()
// console.log("Worker线程处理完耗时操作后,主线程接会收到处理数据")
w.onmessage = function(e) {
let listData = e.data
console.log(listData)
let strs = ''
for (let j = 0; j < listData.length; j++) {
const element = listData[j];
strs += '<p>' + '这是接收到的数据' + j + '</p>'
}
$(".main").append(strs)
$(".ld").hide()
}
w.postMessage('你发送的数据我收到了,辛苦了')
//主线程关闭子线程
// w.terminate()
</script>
</body>
</html>
```
![](/yifu-study-front-share/images/wk/nostop.png)<br />![](/yifu-study-front-share/images/wk/wk_pic.png)
### SharedWorker一种特殊的worker,详细内容查看![SharedWorker](https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorker)
sharedWorker 顾名思义,是 worker 的一种,可以由所有同源的页面共享。同Worker的api一样,传入js的url,就可以注册一个 sharedWorker 实例,
sharedWorker通过port来发送和接收消息
```js
let myWorker = new SharedWorker('worker.js');
//主线程和共享线程都是通过 postMessage() 方法来发送消息
myWorker.port.postMessage('发送消息');
//接收消息都是使用onmessage = (e)=>{},或者 addEventListener('message', (e)=>{})
myWorker.port.onmessage = (e)=>{console.log(e.data)} ;
//关闭启动和worker是有点区别的
线程通过 worker.port.start() 启动
线程通过 worker.port.close() 关闭
SharedWorker使用注意要点同worKer一样
```
### 简单使用Demo
- shareWk.js 共享线程,里面存储计数器counter
- A.html 刷新页面,通知counter+1
- B.html 刷新页面,查看counter
``` shareWk.js
// 计时器
let counter = 0
// 监听连接
self.addEventListener('connect', (e) => {
const port = e.ports[0]
port.onmessage = (res) => {
console.log('A,B页面共享的信息:', res.data)
switch (res.data) {
case 'add':
counter++
break
}
console.log('counter:', counter)
port.postMessage(counter)
}
})
```
``` A.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>A页面</title>
</head>
<body>
<h1>A页面</h1>
<script>
// 兼容性判断
if (!SharedWorker) {
throw new Error('当前浏览器不支持SharedWorker')
}
// 创建共享线程
const worker = new SharedWorker('shareWk.js')
// 启动线程端口
worker.port.start()
// 向共享线程发送消息
worker.port.postMessage('add')
// 线程监听消息 2种方式都可以
// worker.port.addEventListener('message', (e) => {
// console.log('A页面共享线程counter值:', e.data)
// })
worker.port.onmessage = (e) => {
console.log('A页面取到counter值:', e.data)
}
console.log(worker.port)
</script>
</body>
</html>
```
``` B.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>B页面</title>
</head>
<body>
<h1>B页面</h1>
<script>
// 兼容性判断
if (!SharedWorker) {
throw new Error('当前浏览器不支持SharedWorker')
}
// 创建共享线程
const worker = new SharedWorker('shareWk.js')
// 启动线程端口
worker.port.start()
// 向共享线程发送消息
worker.port.postMessage('get counter')
// 线程监听消息
// 线程监听消息 2种方式都可以
// worker.port.addEventListener('message', (e) => {
// console.log('B页面共享线程counter值:', e.data)
// })
worker.port.onmessage = (e) => {
console.log('B页面取到counter值:', e.data)
}
</script>
</body>
</html>
```
![](/yifu-study-front-share/images/wk/shwk.png)
***SharedWorker调试注意点***
SharedWorker不能直接在页面调试面板查看,需要浏览器输入 chrome://inspect
然后点击inspect查看
![](/yifu-study-front-share/images/wk/point.png)
\ No newline at end of file
---
title: WebSocket
date: 2022/7/29 15:00:00
tags:
- WebSocket
author: 张芳利
---
<a name="d3db997e"></a>
## 一、什么是WebSocket
- WebSocket是一种通信协议,可在单个TCP连接上进行全双工通信。WebSocket使得客户端和服务器之间的数据交<br />换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。
1. WebSocket是一种通信协议
2. 区别于HTTP协议,HTTP协议只能实现客户端请求,服务端响应的这种单项通<br />而WebSocket可以实现客户端与服务端的双向通讯,,最大也是最明显的区别就是可以做到服务端主动将消息推送给客户端。
<a name="554bf4d4"></a>
## 二、为什么需WebSocket?
- 因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
> 由于http协议只能由客户端发起通信,如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。Websocket在解决这样的需求上非常方便。<br />如果不使用WebSocket与服务器实时交互,一般有两种方法。AJAX轮询和Long Polling长轮询。
- 上边这两种方式都有个致命的弱点,开销太大,被动性。假设并发很高的话,这对服务端是个考验。<br />而WebSocket一次握手,持久连接,以及主动推送的特点可以解决上边的问题,又不至于损耗性能。
1. AJAX轮询
- AJAX轮询也就是定时发送请求,也就是普通的客户端与服务端通信过程,只不过是无限循环发送,这样,可以保证服务端一旦有最新消息,就可以被客户端获取。
2. Long Polling长轮询
- Long Polling长轮询是客户端和浏览器保持一个长连接,等服务端有消息返回,断开。<br />然后再重新连接,也是个循环的过程,无穷尽也。。。<br />客户端发起一个Long Polling,服务端如果没有数据要返回的话,<br />会hold住请求,等到有数据,就会返回给客户端。客户端又会再次发起一次Long Polling,再重复一次上面的过程。
<a name="bed9b70f"></a>
## 三、WebSocket特点
- 最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
![websocket](/yifu-study-front-share/images/websocket/WebSocket1.png)
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
<a name="b15a1ba5"></a>
![websocket](/yifu-study-front-share/images/websocket/WebSocket2.png)
<a name="367b4b6d"></a>
## 四、WebSocket的API
1、WebSocket构造函数: 新建WebSocket实例后,会创建客户端与服务端的WebSocket连接
```jsx
let ws = new WebSocket(url)
```
2、 WebSocket实例的当前状态:
```jsx
ws.readyState
// 0: 'CONNECTING',表示正在连接
// 1: 'OPEN', 表示连接成功,可以通信了
// 2: 'CLOSING', 表示连接正在关闭
// 3: 'CLOSED', 表示连接已经关闭,或者打开连接失败。
```
3、WebSocket实例的事件:
```jsx
ws.onopen = function () {
// 指定连接成功后的回调函数
}
ws.onclose = function () {
// 指定连接关闭后的回调函数
}
ws.onmessage = function () {
// 指定收到服务器数据后的回调函数
}
ws.onerror = function () {
// 指定报错时的回调函数
}
```
4、WebSocket实例向后台推送消息的方法:
```jsx
ws.send(message) 向服务器发送数据
ws.close() 关闭当前连接
```
<a name="502a7fcf"></a>
## 五、WebSocket的解读
1、申请一个WebSocket对象,参数是需要连接的服务器端的地址,同http协议使用http://开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。
2、WebSocket对象一共支持四个消息 onopen, onmessage, onclose和onerror,<br />所有的操作都是采用消息的方式触发的,这样就不会阻塞UI,使得UI有更快的响应时间,得到更好的用户体验。<br />当Browser接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据;
```jsx
ws.onmessage = function(evt) {
};
```
3、WebSocket与TCP、HTTP的关系WebSocket与http协议一样都是基于TCP的,所以他们都是可靠的协议,Web开发者调用的WebSocket的send函数在browser的实现中最终都是通过TCP的系统接口进行传输的。
4、WebSocket和Http协议一样都属于应用层的协议,那么他们之间有没有什么关系呢?答案是肯定的,WebSocket在建立握手连接时,数据是通过http协议传输的,但是在建立连接之后,真正的数据传输阶段是不需要http协议参与的
![websocket](/yifu-study-front-share/images/websocket/WebSocket3.png)
<a name="14106cbc"></a>
## 六、WebSocket通讯详细解读
![websocket](/yifu-study-front-share/images/websocket/WebSocket4.png)
从图可以明显的看到,分三个阶段:
- 打开握手
- 数据传递
- 关闭握手
---
title: TypeScript泛型
date: 2022/08/04 17:00:00
tags:
- 前端
- typescript
author: 杨靖
---
#### ts装饰器是什么
装饰器是通过添加标注的方式,来对类型进行扩展的一种方式。
- 只能在类中使用
- 减少冗余代码量
- 提高代码扩展性
#### ts装饰器的语法
装饰器的使用非常简单,装饰器本质就是一个函数,在特定的位置调用装饰器函数即可对数据(类、方法、甚至参数等)进行扩展。下面例子演示给 MyTestableClass 类添加静态属性 isTestable:
```typescript
@testable
class MyTestableClass {}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
```
#### 装饰器的分类
装饰器 是一个函数,它可以通过 @funName 在类、方法、访问符、属性、参数上,对它们进行包装,然后返回一个包装后的目标对象(类、方法 、访问符、属性、参数)
1. 类装饰器
类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。下面例子演示给 MyTestableClass 类添加静态属性 isTestable:
```typescript
@testable
class MyTestableClass {}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
```
2. 类方法装饰器
方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
成员的名字。
成员的属性描述符。
下面的例子演示让类的方法变为只读。
(1)实例方法
```typescript
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Cat {
@readonly
say() {
console.log("meow ~");
}
}
```
(2)静态方法
```typescript
class MyTestableClass {
@testable
static sleep() {}
}
function testable(target, name, descriptor) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
```
3. 类属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
成员的名字。
属性描述符不会做为参数传入属性装饰器,这与TypeScript是如何初始化属性装饰器的有关。官方文档有说明。
```typescript
class MyTestableClass {
@testable
name: string;
constructor(name: string) {
this.name = name;
}
}
function testable(target, name) {
console.log(arguments)
}
```
4. 参数装饰器
参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
成员的名字。
参数在函数参数列表中的索引。
注意  参数装饰器只能用来监视一个方法的参数是否被传入。参数装饰器的返回值会被忽略。
```typescript
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(@required name: string) {
return "Hello " + name + ", " + this.greeting;
}
}
```
#### 装饰器的执行顺序
实例装饰器:属性装饰 -> 访问器装饰 -> 参数装饰 => 方法装饰
静态装饰器: 属性 => 访问器 => 参数 => 方法
类装饰器
#### 复合装饰器
```typescript
@d1
@d2
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
function d1(target: Function) {}
function d2(target: Function) {}
```
#### 装饰器工厂
如果我们需要给装饰器执行过程中传入一些参数的时候,就可以使用装饰器工厂来实现,它返回一个装饰器函数。
```typescript
@Controller('user')
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
function Controller(path: string) {
return function (target: Function) {
(target as any).path = path;
}
}
```
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment