這是個人第一篇文章,也是我工做一年後的新徵程。做者是 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」,認真學前端,作個專業的技術人...