Commit b3c2bda4 authored by liyan's avatar liyan
parents df42fae3 e818189d
Pipeline #16838 canceled with stage
...@@ -5,7 +5,7 @@ author: 张顺 ...@@ -5,7 +5,7 @@ author: 张顺
--- ---
## 什么是Worker?他有什么作用? ## 什么是Worker?他有什么作用?
因为js是单线程运行的,在遇到一些需要处理大量耗时的数据时,可能会阻塞页面的加载,造成面的假死。针对这个问题,H5出了新的功能worker,支持多线程,这时我们可以使用worker来开辟一个独立于主线程的子线程来进行一些耗时的大量运算。就可以用来解决因为大量耗时运算是造成页面假死的问题,详细内容查看![Worker](https://developer.mozilla.org/zh-CN/docs/Web/API/Worker) 因为js是单线程运行的,在遇到一些需要处理大量耗时的数据时,可能会阻塞页面的加载,造成面的假死。针对这个问题,H5出了新的功能worker,支持多线程,这时我们可以使用worker来开辟一个独立于主线程的子线程来进行一些耗时的大量运算。就可以用来解决因为大量耗时运算是造成页面假死的问题,详细内容查看[Worker](https://developer.mozilla.org/zh-CN/docs/Web/API/Worker)
### 如何使用Worker ### 如何使用Worker
...@@ -146,7 +146,7 @@ worker 线程无法读取本地文件,即不能打开本机的文件系统(f ...@@ -146,7 +146,7 @@ worker 线程无法读取本地文件,即不能打开本机的文件系统(f
``` ```
![](/yifu-study-front-share/images/wk/nostop.png)<br />![](/yifu-study-front-share/images/wk/wk_pic.png) ![](/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,详细内容查看[SharedWorker](https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorker)
sharedWorker 顾名思义,是 worker 的一种,可以由所有同源的页面共享。同Worker的api一样,传入js的url,就可以注册一个 sharedWorker 实例, sharedWorker 顾名思义,是 worker 的一种,可以由所有同源的页面共享。同Worker的api一样,传入js的url,就可以注册一个 sharedWorker 实例,
sharedWorker通过port来发送和接收消息 sharedWorker通过port来发送和接收消息
......
**reduce()** 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
reduce() 方法有两个参数,
- 第一个是回调函数,
- 第二个是初始值。
回调函数
回调函数在数组的每个元素上执行。回调函数的返回值是累加结果,并作为下一次调用回调函数的参数提供。回调函数带有四个参数。
accumulator 累计器 ——累加器累加回调函数的返回值。
currentValue 当前值——处理数组的当前元素。
currentIndex 当前索引 (可选)
array 数组 (可选)
初始值
如果指定了初始值,则将累加器设置为 initialValue 作为初始元素。否则,将累加器设置为数组的第一个元素作为初始元素。
arr.reduce(callback(accumulator, currentValue[,index[,array]])[, initialValue])
下面的代码片段中,第一个累加器(accumulator)被分配了初始值0。currentValue 是正在处理的 numbersArr 数组的元素。在这里,currentValue 被添加到累加器,在下次调用回调函数时,会将返回值作为参数提供。
```
const numbersArr = [67, 90, 100, 37, 60];
const total = numbersArr.reduce(function(accumulator, currentValue){
console.log("accumulator is " + accumulator + " current value is " + currentValue);
return accumulator + currentValue;
}, 0);
console.log("total : "+ total);
输出:
accumulator is 0 current value is 67
accumulator is 67 current value is 90
accumulator is 157 current value is 100
accumulator is 257 current value is 37
accumulator is 294 current value is 60
total : 354
```
**案例**
对数组的所有值求和
```
const studentResult = [67, 90, 100, 37, 60];
const total = studentResult.reduce((accumulator, currentValue) => accumulator +currentValue, 0);
console.log(total); // 354
```
代码中,studentResult 数组具有5个数字。使用 reduce() 方法,将数组减少为单个值,该值将 studentResult 数组的所有值和结果分配给 total。
对象数组中的数值之和
```
const studentResult = [
{ subject: '数学', marks: 78 },
{ subject: '物理', marks: 80 },
{ subject: '化学', marks: 93 }
];
const total = studentResult.reduce((accumulator, currentValue) => accumulator + currentValue.marks, 0);
console.log(total); // 251
```
我们从后端获取数据作为对象数组,因此,reduce() 方法有助于管理我们的前端逻辑。在下面的代码中,studentResult 对象数组有三个科目,这里,currentValue.marks 取了 studentResult 对象数组中每个科目的分数。
展平数组(去扁平化)
```
const twoDArr = [ [1,2], [3,4], [5,6], [7,8] , [9,10] ];
const oneDArr = twoDArr.reduce((accumulator, currentValue) => accumulator.concat(currentValue));
console.log(oneDArr);
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
```
“展平数组”是指将多维数组转换为一维。在下面的代码中,twoDArr 2维数组被转换为 oneDArr 一维数组。此处,第一个 [1,2] 数组分配给累加器 accumulator,然后 twoDArr 数组的其余每个元素都连接到累加器。
按属性分组对象
```
const result = [
{subject: '物理', marks: 41},
{subject: '化学', marks: 59},
{subject: '高等数学', marks: 36},
{subject: '应用数学', marks: 90},
{subject: '英语', marks: 64},
];
let initialValue = {
pass: [],
fail: []
}
const groupedResult = result.reduce((accumulator, current) => {
(current.marks >= 50) ? accumulator.pass.push(current) : accumulator.fail.push(current);
return accumulator;
}, initialValue);
console.log(groupedResult);
```
根据对象的属性,我们可以使用 reduce() 方法将对象数组分为几组。通过下面的代码片段,你可以清楚地理解这个概念。这里,result 对象数组有五个对象,每个对象都有 subject 和 marks 属性。如果分数大于或等于50,则该主题通过,否则,主题失败。reduce() 用于将结果分组为通过和失败。首先,将 initialValue 分配给累加器,然后 push() 方法在检查条件之后将当前对象添加到 pass 和 fail 属性中作为对象数组。
输出:
```
{
pass: [
{ subject: ‘化学’, marks: 59 },
{ subject: ‘应用数学’, marks: 90 },
{ subject: ‘英语’, marks: 64 }
],
fail: [
{ subject: ‘物理’, marks: 41 },
{ subject: ‘高等数学’, marks: 36 }
]
}
```
删除数组中的重复项
```
const duplicatedsArr = [1, 5, 6, 5, 7, 1, 6, 8, 9, 7];
const removeDuplicatedArr = duplicatedsArr.reduce((accumulator, currentValue) => {
if(!accumulator.includes(currentValue)){
accumulator.push(currentValue);
}
return accumulator;
}, []);
console.log(removeDuplicatedArr);
// [ 1, 5, 6, 7, 8, 9 ]
```
在下面的代码片段中,删除了 plicatedArr 数组中的重复项。首先,将一个空数组分配给累加器作为初始值。accumulator.includes() 检查 duplicatedArr 数组的每个元素是否已经在累加器中可用。如果 currentValue 在累加器中不可用,则使用 push() 将其添加。
\ No newline at end of file
--- ---
title: TypeScript泛型 title: TypeScript装饰器
date: 2022/08/04 17:00:00 date: 2022/08/04 17:00:00
tags: tags:
- 前端 - 前端
......
---
title: 前端图片压缩
date: 2022/8/31 22:00:00
tags:
- 前端
- JavaScript
- 压缩
author: 王许晓
---
> 使用VUE或者React框架加载HTML大图时极容易导致前端页面卡死报out of memory错误,报出这个错误还好,如果没有报错浏览器可能就无法操作了。
<!-- more -->
# 前端压缩
为保前端页面正常渲染html大图,需要在html加载前压缩图片。前端解决这个问题有两种方案,一个是上传前压缩,一个是上传后压缩。上传前可以保证当前图片无论在哪里显示大小已经被压缩过,无需再去管理。上传后压缩可以保证服务器端拥有高清原图,显示前需要做一次压缩。
无论那种压缩,压缩方式基本一致
1. 获取图片尺寸
2. 计算压缩比率
3. 通过canvas.toDataUrl 参数转化压缩图片
4. 将base64字符转转成文件或者直接显示
## 上传前压缩
获取到本地文件后,通过FileReader将本地文件写入到内存中
```
// 获取文件图片的宽高
function getImageWHFromFile(file, fn, err) {
try {
// 读取上传之后的文件对象
const imageReader = new FileReader()
// 将图片的内存url地址添加到FileReader中
imageReader.readAsDataURL(file)
// 当图片完全加载到FileReader对象中后,触发下面的事件
imageReader.addEventListener('loadend', function(e) {
// 获取上传到本机内存中的图片的url地址
const imageSrc = e.target.result
// 调用计算图片大小的方法
calculateImageSize(imageSrc).then(function({ image, width, height }) {
// 得到图片的宽和高
fn(image, width, height)
}).catch(e => {
err()
})
})
// 可以做一些其他状态监听
} catch (e) {
err(file)
}
}
```
计算内存中图片的宽高
```
// 根据图片地址获取图片的宽和高
function calculateImageSize(src) {
return new Promise(function(resolve, reject) {
const image = new Image()
image.addEventListener('load', function(e) {
resolve({
image: image,
width: e.target.width,
height: e.target.height,
})
})
image.addEventListener('error', function(e) {
reject(Error('获取尺寸失败'))
})
// 将图片的url地址添加到图片地址中
image.setAttribute('crossOrigin', 'Anonymous')
image.src = src
})
}
```
得到图片宽高后就可以计算压缩比率,通过图片真水的宽高和期望的宽高计算压缩率,基本上可以得到合适的图片尺寸
```
// 计算压缩比率
function getRatio(oriW, oriH, width, height) {
const proW = (oriW / width).toFixed(5)
const proH = (oriH / height).toFixed(5)
return proW < proH ? proW : proH
}
```
得到压缩比率就可以执行压缩,压缩使用canvas实现
```
// 缩放img对象中的图片
function compressImageToBase64(image, quality) {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const imageWidth = image.width * quality
const imageHeight = image.height * quality
canvas.width = imageWidth
canvas.height = imageHeight
context.drawImage(image, 0, 0, imageWidth, imageHeight)
return canvas.toDataURL('image/jpeg', quality)
}
```
压缩完得到图片的base64(DataURL)数据,可以用这个数据直接输出到img标签显示,或者继续加工生成图片文件
将base64转成blob对象
```
// DataURL转Blob对象
function dataURLToBlob(dataurl) {
const arr = dataurl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
```
将Blob转成File,调用接口用FormData的形式上传文件
```
// 对图片进行压缩并返回文件格式
function compressToFile(image, quality) {
const blob = dataURLToBlob(compressImageToBase64(image, quality))
return new File([blob], new Date().getTime() + '.jpg')
}
```
到此,就得到了一个压缩后的文件对象
## 上传后压缩
上传后压缩的意思是,服务器返回一个图片url后,如果这个url指向的图片尺寸很大,比如:10M,这时候在浏览器中显示这个图片会导致页面卡死。我的解决方案是压缩后再显示,压缩方式大概与上传前压缩类似,从calculateImageSize方法开始计算图片尺寸,然后进行压缩,得到base64数据后回调显示这个图片。这里存在一个问题:压缩开始后chrome的控制台无法打开。原因则是image加载图片后会在控制台的Network中输出base64格式的日志,这个日志占用了太大的内存。
# 服务器压缩
服务器压缩是最好的且能满足产品对大图要求的解决方案。前端上传原图,服务器压缩后生成两个地址,一个缩略图一个原图,缩略图供前端显示,原图供下载。
> 附全部代码供参考
```
/**
* @param {待压缩图片文件} file
* @param {异步回调} fn
* @param {压缩期望尺寸} size [width ,height]
*/
const fileCompressToFile = function(file, fn, size) {
const _size = getComperssSize(size)
if (_size === null) {
fn(file)
return
}
// 开启后执行压缩
getImageWHFromFile(file, (image, width, height) => {
const pro = getRatio(_size[0], _size[1], width, height)
if (pro >= 1) {
fn(file)
return
}
// 开始压缩
fn(compressToFile(image, pro))
}, () => {
fn(file)
})
}
// 通过url压缩图片,压缩成功返回base64
const urlCompressToBase64 = function(url, fn, size) {
const _size = getComperssSize(size)
if (_size === null) {
fn(url)
return
}
calculateImageSize(url).then(function({ image, width, height }) {
const pro = getRatio(_size[0], _size[1], width, height)
if (pro >= 1) {
fn(url)
return
}
// 开始压缩
fn(compressImageToBase64(image, pro))
}).catch(e => {
console.error(e)
fn(url)
})
}
// 格式化压缩尺寸
function getComperssSize(size) {
if (!size) {
return null
}
const defaultSize = 500
if (Array.isArray(size)) {
return [size[0] || defaultSize, size[1] || defaultSize]
}
const pf = parseFloat(size)
return [pf || defaultSize, pf || defaultSize]
}
// 计算压缩比率
function getRatio(oriW, oriH, width, height) {
const proW = (oriW / width).toFixed(5)
const proH = (oriH / height).toFixed(5)
return proW < proH ? proW : proH
}
// 缩放img对象中的图片
function compressImageToBase64(image, quality) {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const imageWidth = image.width * quality
const imageHeight = image.height * quality
canvas.width = imageWidth
canvas.height = imageHeight
context.drawImage(image, 0, 0, imageWidth, imageHeight)
return canvas.toDataURL('image/jpeg', quality)
}
// 对图片进行压缩并返回文件格式
function compressToFile(image, quality) {
const blob = dataURLToBlob(compressImageToBase64(image, quality))
return new File([blob], new Date().getTime() + '.jpg')
}
// DataURL转Blob对象
function dataURLToBlob(dataurl) {
const arr = dataurl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
// 获取文件图片的宽高
function getImageWHFromFile(file, fn, err) {
try {
// 读取上传之后的文件对象
const imageReader = new FileReader()
// 将图片的内存url地址添加到FileReader中
imageReader.readAsDataURL(file)
// 当图片完全加载到FileReader对象中后,触发下面的事件
imageReader.addEventListener('loadend', function(e) {
// 获取上传到本机内存中的图片的url地址
const imageSrc = e.target.result
// 调用计算图片大小的方法
calculateImageSize(imageSrc).then(function({ image, width, height }) {
// 得到图片的宽和高
fn(image, width, height)
}).catch(e => {
err()
})
})
// 可以做一些其他状态监听
} catch (e) {
err(file)
}
}
// 根据图片地址获取图片的宽和高
function calculateImageSize(src) {
return new Promise(function(resolve, reject) {
const image = new Image()
image.addEventListener('load', function(e) {
resolve({
image: image,
width: e.target.width,
height: e.target.height,
})
})
image.addEventListener('error', function(e) {
reject(Error('获取尺寸失败'))
})
// 将图片的url地址添加到图片地址中
image.setAttribute('crossOrigin', 'Anonymous')
image.src = src
})
}
export {
fileCompressToFile,
urlCompressToBase64,
}
```
\ No newline at end of file
---
title: 异步编程中的Promise
date: 2022/9/21 8:48:00
tags:
- 异步编程
- Promise
author: 张顺
---
## 简介
`Javascript`异步编程先后经历了四个阶段,分别是`Callback`阶段,`Promise`阶段,`Generator`阶段和`Async/Await`阶段。`Callback`很快就被发现存在回调地狱和控制权问题,`Promise`就是在这个时间出现,用以解决这些问题,`Promise`并非一个新事务,而是按照一个规范实现的类,这个规范有很多,如 `Promise/A``Promise/B``Promise/D`以及 `Promise/A` 的升级版 `Promise/A+`,最终 `ES6` 中采用了 `Promise/A+` 规范。后来出现的`Generator`函数以及`Async`函数也是以`Promise`为基础的进一步封装,可见`Promise`在异步编程中的重要性。
### Promise 过程
#### 1、 Promise有三种状态pending,fulfilled和rejected。(fulfilled状态即为resolved状态)
- 状态转换只能是pending到resolved或者pending到rejected;
- 状态一旦转换完成,不能再次转换。
#### 2、 Promise拥有一个then方法,用以处理resolved或rejected状态下的值。
- then方法接收两个参数onFulfilled和onRejected,这两个参数变量类型是函数,如果不是函数将会被忽略,并且这两个参数都是可选的。
- then方法必须返回一个新的promise,记作promise2,这也就保证了then方法可以在同一个promise上多次调用。(ps:规范只要求返回promise,并没有明确要求返回一个新的promise,这里为了跟ES6实现保持一致,我们也返回一个新promise)
- onResolved/onRejected有返回值则把返回值定义为x,并执行[[Resolve]](promise2, x);
- onResolved/onRejected运行出错,则把promise2设置为rejected状态;
- onResolved/onRejected不是函数,则需要把promise1的状态传递下去。
#### 3、不同的promise实现可以的交互。
- 规范中称这一步操作为promise解决过程,函数标示为[[Resolve]](promise, x),promise为要返回的新promise对象,x为onResolved/onRejected的返回值。如果x有then方法且看上去像一个promise,我们就把x当成一个promise的对象,即thenable对象,这种情况下尝试让promise接收x的状态。如果x不是thenable对象,就用x的值来执行 promise。
- [[Resolve]](promise, x)函数具体运行规则:
- 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise;
- 如果 x 为 Promise ,则使 promise 接受 x 的状态;
- 如果 x 为对象或者函数,取x.then的值,如果取值时出现错误,则让promise进入rejected状态,如果then不是函数,说明x不是thenable对象,直接以x的值resolve,如果then存在并且为函数,则把x作为then函数的作用域this调用,then方法接收两个参数,resolvePromise和rejectPromise,如果resolvePromise被执行,则以resolvePromise的参数value作为x继续调用[[Resolve]](promise, value),直到x不是对象或者函数,如果rejectPromise被执行则让promise进入rejected状态;
- 如果 x 不是对象或者函数,直接就用x的值来执行promise。
### 代码
第1条,代码实现:
``` js
class Promise {
// 定义Promise状态,初始值为pending
status = 'pending';
// 状态转换时携带的值,因为在then方法中需要处理Promise成功或失败时的值,所以需要一个全局变量存储这个值
data = '';
// Promise构造函数,传入参数为一个可执行的函数
constructor(executor) {
// resolve函数负责把状态转换为resolved
function resolve(value) {
this.status = 'resolved';
this.data = value;
}
// reject函数负责把状态转换为rejected
function reject(reason) {
this.status = 'rejected';
this.data = reason;
}
// 直接执行executor函数,参数为处理函数resolve, reject。因为executor执行过程有可能会出错,错误情况需要执行reject
try {
executor(resolve, reject);
} catch(e) {
reject(e)
}
}
}
```
第2条,代码实现:
``` js
/**
* 拥有一个then方法
* then方法提供:状态为resolved时的回调函数onResolved,状态为rejected时的回调函数onRejected
* 返回一个新的Promise
*/
then(onResolved, onRejected) {
// 设置then的默认参数,默认参数实现Promise的值的穿透
onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return e };
onRejected = typeof onRejected === 'function' ? onRejected : function(e) { throw e };
let promise2;
promise2 = new Promise((resolve, reject) => {
// 如果状态为resolved,则执行onResolved
if (this.status === 'resolved') {
try {
// onResolved/onRejected有返回值则把返回值定义为x
const x = onResolved(this.data);
// 执行[[Resolve]](promise2, x)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}
// 如果状态为rejected,则执行onRejected
if (this.status === 'rejected') {
try {
const x = onRejected(this.data);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}
});
return promise2;
}
```
上述代码很明显是有问题的,问题如下:
1. resolvePromise未定义;
2. then方法执行的时候,promise可能仍然处于pending状态,因为executor中可能存在异步操作(实际情况大部分为异步操作),这样就导致onResolved/onRejected失去了执行时机;
3. onResolved/onRejected这两个函数需要异步调用(官方Promise实现的回调函数总是异步调用的)。
解决办法:
1. 根据规范解读第3条,定义并实现resolvePromise函数;
2. then方法执行时如果promise仍然处于pending状态,则把处理函数进行储存,等resolve/reject函数真正执行的的时候再调用。
3. promise.then属于微任务,这里我们为了方便,用宏任务setTiemout来代替实现异步
代码进一步完善:
``` js
class Promise {
// 定义Promise状态变量,初始值为pending
status = 'pending';
// 因为在then方法中需要处理Promise成功或失败时的值,所以需要一个全局变量存储这个值
data = '';
// Promise resolve时的回调函数集
onResolvedCallback = [];
// Promise reject时的回调函数集
onRejectedCallback = [];
// Promise构造函数,传入参数为一个可执行的函数
constructor(executor) {
// resolve函数负责把状态转换为resolved
function resolve(value) {
this.status = 'resolved';
this.data = value;
for (const func of this.onResolvedCallback) {
func(this.data);
}
}
// reject函数负责把状态转换为rejected
function reject(reason) {
this.status = 'rejected';
this.data = reason;
for (const func of this.onRejectedCallback) {
func(this.data);
}
}
// 直接执行executor函数,参数为处理函数resolve, reject。因为executor执行过程有可能会出错,错误情况需要执行reject
try {
executor(resolve, reject);
} catch(e) {
reject(e)
}
}
/**
* 拥有一个then方法
* then方法提供:状态为resolved时的回调函数onResolved,状态为rejected时的回调函数onRejected
* 返回一个新的Promise
*/
then(onResolved, onRejected) {
// 设置then的默认参数,默认参数实现Promise的值的穿透
onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return e };
onRejected = typeof onRejected === 'function' ? onRejected : function(e) { throw e };
let promise2;
promise2 = new Promise((resolve, reject) => {
// 如果状态为resolved,则执行onResolved
if (this.status === 'resolved') {
setTimeout(() => {
try {
// onResolved/onRejected有返回值则把返回值定义为x
const x = onResolved(this.data);
// 执行[[Resolve]](promise2, x)
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
// 如果状态为rejected,则执行onRejected
if (this.status === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.data);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
// 如果状态为pending,则把处理函数进行存储
if (this.status = 'pending') {
this.onResolvedCallback.push(() => {
setTimeout(() => {
try {
const x = onResolved(this.data);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallback.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.data);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
// [[Resolve]](promise2, x)函数
resolvePromise(promise2, x, resolve, reject) {
}
}
```
第3条,代码实现:
``` js
// [[Resolve]](promise2, x)函数
resolvePromise(promise2, x, resolve, reject) {
let called = false;
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
// 如果x仍然为Promise的情况
if (x instanceof Promise) {
// 如果x的状态还没有确定,那么它是有可能被一个thenable决定最终状态和值,所以需要继续调用resolvePromise
if (x.status === 'pending') {
x.then(function(value) {
resolvePromise(promise2, value, resolve, reject)
}, reject)
} else {
// 如果x状态已经确定了,直接取它的状态
x.then(resolve, reject)
}
return
}
if (x !== null && (Object.prototype.toString(x) === '[object Object]' || Object.prototype.toString(x) === '[object Function]')) {
try {
// 因为x.then有可能是一个getter,这种情况下多次读取就有可能产生副作用,所以通过变量called进行控制
const then = x.then
// then是函数,那就说明x是thenable,继续执行resolvePromise函数,直到x为普通值
if (typeof then === 'function') {
then.call(x, (y) => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
}, (r) => {
if (called) return;
called = true;
reject(r);
})
} else { // 如果then不是函数,那就说明x不是thenable,直接resolve x
if (called) return ;
called = true;
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
```
### Promise其他方法实现
##### 1、catch方法
catch方法是对then方法的封装,只用于接收reject(reason)中的错误信息。因为在then方法中onRejected参数是可不传的,不传的情况下,错误信息会依次往后传递,直到有onRejected函数接收为止,因此在写promise链式调用的时候,then方法不传onRejected函数,只需要在最末尾加一个catch()就可以了,这样在该链条中的promise发生的错误都会被最后的catch捕获到。
``` js
catch(onRejected) {
return this.then(null, onRejected);
}
```
##### 2、done方法
catch在promise链式调用的末尾调用,用于捕获链条中的错误信息,但是catch方法内部也可能出现错误,所以有些promise实现中增加了一个方法done,done相当于提供了一个不会出错的catch方法,并且不再返回一个promise,一般用来结束一个promise链。
``` js
done() {
this.catch(reason => {
console.log('done', reason);
throw reason;
});
}
```
##### 3、finally方法
finally方法用于无论是resolve还是reject,finally的参数函数都会被执行。
``` js
finally(fn) {
return this.then(value => {
fn();
return value;
}, reason => {
fn();
throw reason;
});
};
```
##### 4、Promise.all方法
Promise.all方法接收一个promise数组,返回一个新promise2,并发执行数组中的全部promise,所有promise状态都为resolved时,promise2状态为resolved并返回全部promise结果,结果顺序和promise数组顺序一致。如果有一个promise为rejected状态,则整个promise2进入rejected状态。
``` js
static all(promiseList) {
return new Promise((resolve, reject) => {
const result = [];
let i = 0;
for (const p of promiseList) {
p.then(value => {
result[i] = value;
if (result.length === promiseList.length) {
resolve(result);
}
}, reject);
i++;
}
});
}
```
##### 5、Promise.race方法
Promise.race方法接收一个promise数组, 返回一个新promise2,顺序执行数组中的promise,有一个promise状态确定,promise2状态即确定,并且同这个promise的状态一致。
``` js
static race(promiseList) {
return new Promise((resolve, reject) => {
for (const p of promiseList) {
p.then((value) => {
resolve(value);
}, reject);
}
});
}
```
##### 6、Promise.resolve方法/Promise.reject
Promise.resolve用来生成一个rejected完成态的promise,Promise.reject用来生成一个rejected失败态的promise。
``` js
static resolve(value) {
let promise;
promise = new Promise((resolve, reject) => {
this.resolvePromise(promise, value, resolve, reject);
});
return promise;
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
```
常用的方法基本就这些,Promise还有很多扩展方法,这里就不一一展示,基本上都是对then方法的进一步封装,只要你的then方法没有问题,其他方法就都可以依赖then方法实现。
\ No newline at end of file
```
title: 浅聊Set
date: 2022/9/8 15:00:00
author: 张润
```
ES6 新增的一种新的数据结构,类似于数组,但成员是唯一且无序的,没有重复的值。
Set 本身是一种构造函数,用来生成 Set 数据结构。
```javascript
new Set([iterable])
```
举个例子:
```javascript
const s = new Set()
[1, 2, 3, 4, 3, 2, 1].forEach(x => s.add(x))
for (let i of s) {
console.log(i) // 1 2 3 4
}
// 去重数组的重复对象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)] // [1, 2, 3]复制代码
```
Set 对象允许你储存任何类型的唯一值,无论是原始值或者是对象引用。
向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是NaN等于自身,而精确相等运算符认为NaN不等于自身。
```javascript
let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}
let set1 = new Set()
set1.add(5)
set1.add('5')
console.log([...set1]) // [5, "5"]复制代码
```
Set 实例属性
```javascript
constructor 构造函数
size:元素数量
let set = new Set([1, 2, 3, 2, 1])
console.log(set.length) // undefined
console.log(set.size) // 3复制代码
```
Set 实例方法-操作方法
```javascript
add(value):新增,相当于 array里的push
delete(value):存在即删除集合中value复制代码
has(value):判断集合中是否存在 value复制代码
clear():清空集合复制代码
```
```javascript
let set = new Set()
set.add(1).add(2).add(1)
set.has(1) // true
set.has(3) // false
set.delete(1)
set.has(1) // false复制代码
```
Array.from 方法可以将 Set 结构转为数组
```javascript
const items = new Set([1, 2, 3, 2])
const array = Array.from(items)
console.log(array) // [1, 2, 3]
// 或
const arr = [...items]
console.log(arr) // [1, 2, 3]复制代码
```
遍历方法(遍历顺序为插入顺序)
1、keys():返回一个包含集合中所有键的迭代器复制代
2、values():返回一个包含集合中所有值得迭代器复制代码
3、ntries():返回一个包含Set对象中所有元素得键值对迭代器复制代码
4、forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果
提供了 thisArg 参数,回调中的this会是这个参数,没有返回值复制代
```javascript
let set = new Set([1, 2, 3])
console.log(set.keys()) // SetIterator {1, 2, 3}
console.log(set.values()) // SetIterator {1, 2, 3}
console.log(set.entries()) // SetIterator {1, 2, 3}
for (let item of set.keys()) {
console.log(item);
} // 1 2 3
for (let item of set.entries()) {
console.log(item);
} // [1, 1] [2, 2] [3, 3]
set.forEach((value, key) => {
console.log(key + ' : ' + value)
}) // 1 : 1 2 : 2 3 : 3
console.log([...set]) // [1, 2, 3]复制代码
```
Set 可默认遍历,默认迭代器生成函数是 values() 方法
```javascript
Set.prototype[Symbol.iterator] === Set.prototype.values // true
```
所以, Set可以使用 map、filter 方法
```javascript
let set = new Set([1, 2, 3])
set = new Set([...set].map(item => item * 2))
console.log([...set]) // [2, 4, 6]
set = new Set([...set].filter(item => (item >= 4)))
console.log([...set]) //[4, 6]
```
因此,Set 很容易实现交集(Intersect)、并集(Union)、差集(Difference)
```javascript
let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])
let intersect = new Set([...set1].filter(value => set2.has(value)))
let union = new Set([...set1, ...set2])
let difference = new Set([...set1].filter(value => !set2.has(value)))
console.log(intersect) // Set {2, 3}
console.log(union) // Set {1, 2, 3, 4}
console.log(difference) // Set {1}
```
\ No newline at end of file
---
title: 浅谈发布自己的npm包(一)
date: 2022/9/20 16:53:00
tags:
- npm
- webpack
author: 张芳利
---
<a name="Hq4wx"></a>
## 前景
在开发过程中,你可能会遇到不同的项目但是用到了相同的组件或者方法等,如果把代码再复制一遍显示不是很好的做法,而推荐的做法是把自己经常用到的组件或方法发布成npm包。平时我们也会使用各种各样的npm包,正好这次我们一起尝试一下如何去发布一下自己的包~
<a name="D701t"></a>
## 注册
先去[官网](https://www.npmjs.com/signup)注册一个npm 的账号,这里几乎是傻瓜式的操作,我们添加名称、密码、即可。<br />我主要说一个有点意思的:<br />注册成功,登录进去后,点击**Account,**看到 Two-Factor Authentication,这就是我们要说的东西。<br />解释一下就是 【双重身份验证】,这个东西你开不开都行,官方是建议开的,如果开启后面发布更新之类的就输入这个就行了。<br />![1.png](/yifu-study-front-share/images/npm/npm1.1.png)
1. 我们点击Enable 2FA 进到校验页面,有两个校验选项,显然我们要选择第二种。他的意思就是使用应用程序获取验证码来进行验证。这个软件就是**Authenticator,**因为我是苹果手机直接在应用商场搜索下载的,安卓的话应该也有这个app,你可以试试
![2.png](/yifu-study-front-share/images/npm/npm1.2.png)
2. 点击上图的继续,我们会看到一个二维码的页面,用我们刚才下载的软件,选择扫描二维码,你的手机上就会出现6位数字,不用记,它是不停变化的。
![3.png](/yifu-study-front-share/images/npm/npm1.3.png)
3. 把我们看到的数字输入进去,继续点击确定,就可以了。
![4.png](/yifu-study-front-share/images/npm/npm1.4.png)
<a name="nnJtO"></a>
## 发布第一个包
好了,我们来尝试发布一个简单的js函数
<a name="gY86M"></a>
#### 1. 创建
```jsx
$ mkdir zfl-npm-js
$ cd zfl-npm-js
$ npm init -y //默认设置,如果不默认自己设置去掉 -y
```
<a name="FE23f"></a>
#### 2. package.json
这时我们创建的zfl-npm-js 文件里有一个package.json 文件
```jsx
{
"name": "zfl-npm-js",
"version": "1.0.0", //版本
"description": "",
"main": "index.js", //入口,可根据实际情况更改
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
```
<a name="jEJDI"></a>
#### 3. 函数
在项目中创建src/index.js,记得在上面的package.json 中也要改一下入口哦,我们写一个简单的加法函数
```jsx
export function add(a,b){
// 判断a,b是否为数字
if(typeof a !== 'number' || typeof b !== 'number'){
throw new Error('a and b must be number')
}
return a + b
}
```
<a name="i0msZ"></a>
#### 4. webpack 打包
```jsx
npm install --save-dev webpack webpack-cli
```
根目录创建一个webpack.config.js,进行配置
```jsx
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: 'zfl-npm-js', // 全局挂载包的引用名
libraryTarget: 'umd', //通用模式:支持用户通过es、common.js、AMD的方式引入npm包
globalObject: 'this', // node 等环境运行时需要设置为 this
libraryExport:'default' //默认导出
}
}
```
<a name="CLJHm"></a>
#### 5. 登录npm
```jsx
npm login //输入用户名 密码 邮箱 opt等
```
关于opt 如果你上面没有去配置身份双重检验,邮箱里会有收到,输入进来就可以了。<br />**这里可能会遇见报错:403**<br />![5.png](/yifu-study-front-share/images/npm/npm1.5.png)<br />这是因为平时开发用的是cnpm,这里需要执行一下
```jsx
npm config set registry https://registry.npmjs.org/
```
就可以了。
<a name="BzMze"></a>
#### 6. 上传发布包
```jsx
npm publish
```
![6.png](/yifu-study-front-share/images/npm/npm1.6.png)<br/>![7.png](/yifu-study-front-share/images/npm/npm1.7.png)<br />一个简陋的js包好了,我们来使用验证一下。
<a name="bFvOv"></a>
#### 7. 使用
就和正常的npm 安装使用一样。
```jsx
npm i zfl-npm-js
```
![image.png](/yifu-study-front-share/images/npm/npm1.8.png)<br />![image.png](/yifu-study-front-share/images/npm/npm1.9.png)<br />好了我们一个简陋的npm包可以了,你可以升级(升级一定记得把版本号改一下!!!不能同版本号)、删除(好像是24小时候可以删除,超过就不行了)等操作。<br />缺点:没有说明文件,过于简陋,下次我们在尝试一下发布正常一点的包 🌻 🌻 🌻
...@@ -9,9 +9,11 @@ tags: ...@@ -9,9 +9,11 @@ tags:
author: 张芳利 author: 张芳利
--- ---
<a name="XDoI3"></a>
## 一、什么是跨域 ## 一、什么是跨域
要知道什么是跨域,需要先知道另一个名词【**同源政策**】。<br />同源政策:是一个重要的安全政策,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。<br />而什么才是同源呢?同源就是两个页面具有相同的协议,域名和端口 均一致。所以根据上面的了解,**源=协议+主机/域名+端口,当源不同的时候也就是跨域了**<br />例如:<br /> 要知道什么是跨域,需要先知道另一个名词【**同源政策**】。
同源政策:是一个重要的安全政策,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
而什么才是同源呢?同源就是两个页面具有相同的协议,域名和端口 均一致。所以根据上面的了解,**源=协议+主机/域名+端口,当源不同的时候也就是跨域了**
例如:
![crossDomain](/yifu-study-front-share/images/crossDomain/img1.png) ![crossDomain](/yifu-study-front-share/images/crossDomain/img1.png)
| **当前页面url** | **被请求页面url** | **是否跨域** | **原因** | | **当前页面url** | **被请求页面url** | **是否跨域** | **原因** |
...@@ -25,25 +27,27 @@ author: 张芳利 ...@@ -25,25 +27,27 @@ author: 张芳利
**注意:** **注意:**
> 在默认情况下 http 可以省略端口 80, https 省略 443。 > 在默认情况下 http 可以省略端口 80, https 省略 443。
<a name="C9fUE"></a>
## 二、跨域的限制 ## 二、跨域的限制
- Cookie,LocalStorage,IndexDB 等存储性内容无法读取 - Cookie,LocalStorage,IndexDB 等存储性内容无法读取
- DOM 节点无法访问 - DOM 节点无法访问
- Ajax 请求发出去了,但是响应被浏览器拦截了 - Ajax 请求发出去了,但是响应被浏览器拦截了
基本我们说的跨域是在第三点请求上面。<br />ajax请求报错: 基本我们说的跨域是在第三点请求上面。
ajax请求报错:
```jsx ```jsx
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been block by CORS, Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been block by CORS,
policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
``` ```
<a name="HSQ39"></a>
## 三、解决跨域 ## 三、解决跨域
<a name="uTL8Z"></a>
### 方法一:JSONP ### 方法一:JSONP
JSONP 是 JSON with Padding 的缩写,主要就是利用了 script 标签没有跨域限制的这个特性来完成的。<br />**思路:网页通过添加一个<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。**<br />缺点:只支持get请求,不支持post请求。<br />(下面的接口都是聚合数据里面的免费接口,如果想实验测试一下可以自己更换一下接口地址哈~)<br />**1、原生js** JSONP 是 JSON with Padding 的缩写,主要就是利用了 script 标签没有跨域限制的这个特性来完成的。
```jsx 思路:网页通过添加一个`<script>`元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
缺点:只支持get请求,不支持post请求。
(下面的接口都是聚合数据里面的免费接口,如果想实验测试一下可以自己更换一下接口地址哈~)
**1、原生js**
```html
<script src="http://test.com/data.php?callback=dosomething"></script> <script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字 // 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
...@@ -67,7 +71,8 @@ script> ...@@ -67,7 +71,8 @@ script>
type="text/javascript" type="text/javascript"
>script> >script>
``` ```
**2、vue.js ** <br />2.1 安装jsonp,利用jsonp插件 **2、vue.js **
2.1 安装jsonp,利用jsonp插件
```jsx ```jsx
yarn add jsonp yarn add jsonp
...@@ -160,9 +165,12 @@ export const getTest = () => { ...@@ -160,9 +165,12 @@ export const getTest = () => {
} }
``` ```
<a name="LNDy8"></a>
### 方法二:CORS ### 方法二:CORS
CORS是基于http1.1的一种跨域解决方案,它的全称是**C**ross-**O**rigin **R**esource **S**haring,跨域资源共享。<br />**思路:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许**<br />缺点:当你使用 IE<=9, Opera<12, or Firefox<3.5 或者更加老的浏览器,就不适合 <br />在 cors 中会有 简单请求 和 复杂请求的概念。简单请求:当请求**同时满足**以下条件时,浏览器会认为它是一个简单请求:<br />情况一: 使用以下方法(意思就是以下请求意外的都是非简单请求) CORS是基于http1.1的一种跨域解决方案,它的全称是**C**ross-**O**rigin **R**esource **S**haring,跨域资源共享。
**思路:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许**
缺点:当你使用 IE<=9, Opera<12, or Firefox<3.5 或者更加老的浏览器,就不适合
在 cors 中会有 简单请求 和 复杂请求的概念。简单请求:当请求**同时满足**以下条件时,浏览器会认为它是一个简单请求:
情况一: 使用以下方法(意思就是以下请求意外的都是非简单请求)
- GET - GET
- HEAD - HEAD
...@@ -186,7 +194,10 @@ CORS是基于http1.1的一种跨域解决方案,它的全称是**C**ross-**O** ...@@ -186,7 +194,10 @@ CORS是基于http1.1的一种跨域解决方案,它的全称是**C**ross-**O**
- multipart/form-data - multipart/form-data
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
情况四:<br />请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。<br />情况五:<br />请求中没有使用 ReadableStream 对象。 情况四:
请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
情况五:
请求中没有使用 ReadableStream 对象。
```jsx ```jsx
// 简单请求 // 简单请求
fetch('http://crossdomain.com/api/news'); fetch('http://crossdomain.com/api/news');
...@@ -219,7 +230,9 @@ fetch('http://crossdomain.com/api/news', { ...@@ -219,7 +230,9 @@ fetch('http://crossdomain.com/api/news', {
``` ```
不满足上面的就是复杂请求。一般CORS的设置都是在后端的。 不满足上面的就是复杂请求。一般CORS的设置都是在后端的。
**1、**普通跨域请求:只需服务器端设置Access-Control-Allow-Origin<br />2、带cookie跨域请求:前后端都需要进行设置<br />**【前端设置】根据xhr.withCredentials字段判断是否带有cookie** **1、**普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
**【前端设置】根据xhr.withCredentials字段判断是否带有cookie**
```jsx ```jsx
// 原生ajax // 原生ajax
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容 var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
...@@ -258,9 +271,9 @@ axios.defaults.withCredentials = true ...@@ -258,9 +271,9 @@ axios.defaults.withCredentials = true
``` ```
<a name="EBefT"></a>
### 方法三:代理 ### 方法三:代理
**思路:利用服务端请求不会跨域的特性,让接口和当前站点同域。**<br />代理适用的场景是:生产环境不发生跨域,但开发环境发生跨域。<br /> **思路:利用服务端请求不会跨域的特性,让接口和当前站点同域。**
代理适用的场景是:生产环境不发生跨域,但开发环境发生跨域。
![crossDomain](/yifu-study-front-share/images/crossDomain/img3.png) ![crossDomain](/yifu-study-front-share/images/crossDomain/img3.png)
像是vue中,一般是这么处理。现在常用的vue等框架都有解决方案。 像是vue中,一般是这么处理。现在常用的vue等框架都有解决方案。
...@@ -303,12 +316,13 @@ proxyTable: { ...@@ -303,12 +316,13 @@ proxyTable: {
``` ```
<a name="qs5RQ"></a>
### 方法四:Nginx 反向代理 ### 方法四:Nginx 反向代理
Nginx 实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。<br />使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。<br />主要是后端进行nginx配置,就不讲了。 Nginx 实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。
<a name="OoScp"></a> 使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。
主要是后端进行nginx配置,就不讲了。
### 方法五:websocket ### 方法五:websocket
Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。什么是全双工通信 ?简单来说,就是在建立连接之后,server 与 client 都能主动向对方发送或接收数据。<br />以第**三方库 ws** 为例: Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。什么是全双工通信 ?简单来说,就是在建立连接之后,server 与 client 都能主动向对方发送或接收数据。
以第**三方库 ws** 为例:
```jsx ```jsx
........ ........
const WebSocket = require('ws'); const WebSocket = require('ws');
...@@ -324,9 +338,9 @@ ws.on('message', function incoming(data) { ...@@ -324,9 +338,9 @@ ws.on('message', function incoming(data) {
}); });
... ... ... ...
``` ```
<a name="BuN2e"></a>
### 方法六:window.postMessage() ### 方法六:window.postMessage()
此方法可以安全地实现跨源通信。window.postMessage() 方法允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本,而不用管是否跨域,可以用这种消息传递技术来实现安全的通信。这项技术称为“**跨文档消息传递**”,又称为“**窗口间消息传递**”或者“**跨域消息传递**”。<br />它可用于解决以下方面的问题: 此方法可以安全地实现跨源通信。window.postMessage() 方法允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本,而不用管是否跨域,可以用这种消息传递技术来实现安全的通信。这项技术称为“**跨文档消息传递**”,又称为“**窗口间消息传递**”或者“**跨域消息传递**”。
它可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递 - 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递 - 多窗口之间消息传递
...@@ -341,7 +355,9 @@ otherWindow.postMessage(message, targetOrigin, [transfer]); ...@@ -341,7 +355,9 @@ otherWindow.postMessage(message, targetOrigin, [transfer]);
> message:要发送的消息。它将会被结构化克隆算法序列化,所以无需自己序列化,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化。 > message:要发送的消息。它将会被结构化克隆算法序列化,所以无需自己序列化,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化。
> **targetOrigin:**“目标域“。URI(包括:协议、主机地址、端口号)。若指定为”*“,则表示可以传递给任意窗口,指定为”/“,则表示和当前窗口的同源窗口。当为URI时,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。 > **targetOrigin:**“目标域“。URI(包括:协议、主机地址、端口号)。若指定为”*“,则表示可以传递给任意窗口,指定为”/“,则表示和当前窗口的同源窗口。当为URI时,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。
**接收消息**<br />如果指定的源匹配的话,那么当调用 postMessage() 方法的时候,在目标窗口的Window对象上就会触发一个 message 事件。<br />获取postMessage传来的消息:为页面添加onmessage事件。 **接收消息**
如果指定的源匹配的话,那么当调用 postMessage() 方法的时候,在目标窗口的Window对象上就会触发一个 message 事件。
获取postMessage传来的消息:为页面添加onmessage事件。
```jsx ```jsx
window.addEventListener('message',function(e) { window.addEventListener('message',function(e) {
var origin = event.origin; var origin = event.origin;
......
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