Commit b3c2bda4 authored by liyan's avatar liyan
parents df42fae3 e818189d
Pipeline #16838 canceled with stage
......@@ -5,7 +5,7 @@ author: 张顺
---
## 什么是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
......@@ -146,7 +146,7 @@ worker 线程无法读取本地文件,即不能打开本机的文件系统(f
```
![](/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通过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
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
This diff is collapsed.
```
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 />缺点:没有说明文件,过于简陋,下次我们在尝试一下发布正常一点的包 🌻 🌻 🌻
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