这是个人第一篇文章,也是我工做一年后的新征程。做者是 2019 年刚刚毕业的,出身贫寒(普通二本)。亲眼目击校招神仙打架,不幸流落凡尘(我不配)。如今之外包的形式,在一家金融公司工做。前端
前端项目为 vue 技术栈, 业务中遇到这样一个情景,有一个输入框,能够键入或者复制粘贴进一大段带有某种格式的文本,根据格式符号对文本进行分割处理(例如根据‘;’分割对象,根据‘,’分割属性),最终将他们处理成某种格式的对象集合,同时生成预览。效果大概是这个样子代码以下vue
// index.vue
import { sectionSplice, contentSplice } from '@/utils/handleInput';
...
onInput() {
this.loading = true;
const temp = sectionSplice(this.text);
this.cardList = contentSplice(temp).data;
this.loading = false;
},
// @/utils/handleInput
export function sectionSplice(val) {
const breakSymbol = '\n';
let cards = val.split(breakSymbol);
return cards.filter((item) => item != '');
}
export function contentSplice(dataArr, cardId) {
const splitSymbol = ',';
const length = dataArr.length;
const result = {
data: [],
cardId,
};
let item = null;
let time = new Date().getTime();
function maxLength(text) {
if (text && text.length > 1000) return text.substring(0, 1000);
return text;
}
for (let i = 0; i < length; i++) {
item = dataArr[i].split(splitSymbol);
if (item != '') {
result.data.push({
title: maxLength(item[0]),
desc: maxLength(item.slice(1).join(splitSymbol)),
key: time + i,
keydef: time + i + 'keydef',
});
}
}
return result;
}
但随着输入内容的增多,以及操做的频繁,很快会遇到性能问题,致使页面卡死。这是一段 2082080 字数键入后执行的状况这是当输入内容比较多的执行状况,由于再多就卡死了,能够看到整个 input 回调执行至关耗时,形成性能低下,同时频繁触发 vue 更新让本来就就已经低效的性能雪上加霜。webpack
既然 input 回调高耗时,阻塞后续事件的执行,那咱们就引用 web-worker 开辟新的线程,来执行这部分耗时操做就行了。在这个过程当中,由于 web-worker 的加载方式使得在 webpack 工程化的项目中形成了困难。我尝试使用 worker-loader 等方式,可是太多坑了。最终使用了vue-worker,之因此使用 this.$worker.run()方法是由于这种方式执行完成后 worker 会自行销毁。这里附带上git
// main.js
import VueWorker from 'vue-worker';
Vue.use(VueWorker);
// index.js
onInput() {
this.loading = true;
const option = [this.text];
this.workerInput = this.$worker
.run(sectionSplice, option)
.then((res) => {
this.handleCards(res);
})
.catch((e) => console.log(e));
},
handleCards(data) {
this.workerCards = this.$worker
.run(contentSplice, [data])
.then((res) => {
this.cardList = res.data;
this.loading = false;
})
.catch((e) => console.log(e));
},
可是现实很是残酷的开辟 1 个新线程以后,这一套处理过程仍是很是繁重,只不过阻塞的位置从页面渲染线程换到了新线程。因而我想到了 React Fiber 的理念,我也去搞个分片吧。因而将原有的逻辑拆分红两步。github
想了一下我想起了代理模式 设计一个 Cards 类,有 4 个属性web
export default class Cards {
constructor(id, length) {
this.SL = length;
this.count = 0;
this.CardId = id;
}
list = [];
addCards(sid, section) {
if (this.CardId == sid) {
this.count++;
this.list = this.list.concat(section);
}
if (this.count == this.SL) {
return this.list;
} else {
return [];
}
}
empty() {
this.list = [];
}
get() {
return this.list;
}
}
这个问题很是重要,可是我并非科班出身,我百度了很久都没有找到相关说明的文章,只能试着说明了。
这就设计到计算机基础了,最先 cpu 只有一个核心,一个线程,同时只能同时完成一件事情,一心不可二用。可是随着技术的发展,如今的消费级 cpu 都有 16 核 32 线程了,能够理解为三头六臂,同时能够作不少事情。
可是并不是有多少线程,就只能开多少线程。以今年热销的英特尔 i5 10400 为例,这颗 cup 是 6 核 12 线程,12 线程指的是最大并行执行的线程数量。实际上是能够开辟多余 12 的线程数,这时 cpu 就有一个相似 js eventloop 的调度机制,用于切换任务在空闲线程执行。在这个过程当中要消耗物理资源的,若是线程过多,在线程间来回切换的损耗会很是巨大。所以线程开辟,不超过 cpu 线程数为宜。而且为什使用了 vue-worker 就能够绕过那么多在 vue 环境下使用 web worker 的坑呢?因而我去看了一下 vue-worker 的源码。数组
// https://github.com/israelss/vue-worker/blob/master/index.js
import SimpleWebWorker from 'simple-web-worker';
export default {
install: function (Vue, name) {
name = name || '$worker';
Object.defineProperty(Vue.prototype, name, { value: SimpleWebWorker });
},
};
这。。。。居然只是把 SimpleWebWorker 注册成 vue 插件,好吧,看来 vue-worker 也大可没必要了。因而我基于 SimpleWebWorker 写了一个 worker 的执行队列,经过 window.navigator.hardwareConcurrency 获取 cpu 线程信息限制开放线程数不超过 cpu 线程数,若是获取不到就默认上线是 4 个线程,毕竟如今都 2020 年了,在老的机器也都是 2 核 4 线程以上的配置了。可是这种线程的限制方式并不严谨,由于还有不少其余应用程序在占用线程,可是相对不会多开辟新线程.浏览器
import SimpleWebWorker from 'simple-web-worker';
export default class WorkerQueue {
constructor() {
try {
this.hardwareConcurrency = window.navigator.hardwareConcurrency;
} catch (error) {
console.log(
'Set 4 Concurrency,because can`t get your hardwareConcurrency.'
);
this.concurrency = 4;
}
this.concurrency = 4;
this._worker = SimpleWebWorker;
this.workerCont = 0;
this.queue = [];
}
push(fn, callback, ...args) {
this.queue.push({ fn, callback, args });
this.run();
}
run() {
while (this.queue.length && this.concurrency > this.workerCont) {
this.workerCont++;
const { fn, callback, args } = this.queue.shift();
this._worker
.run(fn, args)
.then((res) => {
callback(res);
this.workerCont--;
this.run();
})
.catch((e) => {
throw e;
});
}
}
}
虽然引入了 worker 开辟线程,必定程度上减轻了阻塞的问题,可是频繁触发 Input 回调,以及频繁的 vue 更新仍是会影响性能,所以这里引入防抖控制回调执行的频率。给 cup 一点喘息的时间,让他可以一直跑起来。if (this.timer) { clearTimeout(this.timer); this.timer = setTimeout(() => { clearTimeout(this.timer); this.timer = null; }, 2000); return }微信
极端状况这是一次性键入的 1278531 字数的内容,当一次性输入这么多内容时,即使是浏览器的 textInput 都吃不消了,反而成为了最耗时的事件,而咱们的处理过程并未形成卡顿。也就是说理论上当内容足够多,浏览器都吃不消时,咱们的事件处理也不会形成卡顿,已经可以知足咱们的需求了。异步
正常大数据量状况,仍是使用开头 2082080 字数文字键入后的执行状况,与优化前进行对比。
附上 demo 地址https://github.com/liubon/vue-worker-demo)
第一次尝试写文章,不足之处请见谅,存在问题欢迎指正~
最后
欢迎加我微信(winty230),拉你进技术群,长期交流学习...
欢迎关注「前端Q」,认真学前端,作个专业的技术人...