用 Web Worker 提升30% Vue 組件性能【實例講解】

關注公眾號 前端人,回復“加群”
添加無廣告優(yōu)質學習群
有時開發(fā)者需要和一些“很重”的組件打交道 -- 這通常是指由于執(zhí)行了復雜的任務,所以創(chuàng)建和渲染開銷都很大的組件。
比方說,我昨天就在使用“StoryBlok”工具庫創(chuàng)建富文本編輯器的時候遇到了麻煩。
事情是這樣的,從 StoryBlok API 獲取富文本內容的時候,得到的數據有其獨有的結構。為了將這種數據渲染到 HTML 中,就必須使用 storyblok-js-client 庫提供的 richTextResolver.render(content) 方法。
我們可以將這個功能封裝到一個 RichText.vue 組件中。一種基本的實現可能是:
<template>
<div v-html="contentHtml"></div>
</template>
<script>
export default {
props: ["content"],
computed: {
contentHtml() {
// $storyapi 是來自于 StoryBlok Nuxt.js 模塊中的一個實例
return this.$storyapi.richTextResolver.render(content);
}
}
};
</script>
至此似乎平淡無奇,但...驚喜雷??不期而至。
看上去,把這些內容渲染出來可是個繁重的工作,這在 StoryBlok 的各種組件開始渲染包含大量內容的數據時尤為明顯。
現在再想象這樣的場景:你的頁面上有個包含富文本組件的列表,以及一個下拉篩選器。當你改變篩選項時,將重新請求符合篩選的所有內容,再把列表項都重新渲染一遍。
實際運行后你還將看到 richTextResolver.render 帶來的渲染負擔:篩選下拉框在被選擇值后的關閉動作非常遲緩。
原因就在于默認的 JavaScript 運行在主線程,也就是被稱作 UI-blocking 的問題。
問題是理解了,但...如何解決呢?其實也很簡單:為富文本渲染任務使用一個 Web Worker 就行了。
如果要對 JS 單線程和 Web Worker 有所了解,請閱讀:
Web Worker 運行在一個獨立的線程中,且不會造成 UI 阻塞,非常適于我們的用例。
worker-loader 插件
更多的技術細節(jié)請閱讀上面的鏈接,這里只要知道 Web Worker 運行在自有的上下文中,并且默認情況無法訪問外部上下文就行了。但本例中我們要訪問到 storyblok-js-client npm 模塊。對此,可以用 Webpack 中的 worker-loader 解決。
首先用 npm install -D worker-loader 安裝依賴。然后需要對其配置,比如在本例中的 Nuxt.js 中像這樣配置 nuxt.config.js :
build: {
extend(config, { isDev, isClient }) {
config.module.rules.push({
test: /\.worker\.js$/,
use: { loader: "worker-loader" }
});
}
}
這樣一來,所有 .worker.js 結尾的文件都將被 worker-loader 注冊為 Web Worker。
下面創(chuàng)建一個 render-html.worker.js:
import StoryblokClient from "storyblok-js-client";
let storyClient = new StoryblokClient({});
self.addEventListener("message", ({ data }) => {
const result = storyClient.richTextResolver.render(data);
self.postMessage(result);
});
這就是一個 worker 的基礎實現。需要監(jiān)聽 message 事件,這也正是與你的 Vue.js 應用通訊的方式。當你從事件中得到 data 后,用 storyblok-js-client 渲染該數據,并將得到的結果用 self.postMessage 回傳。
接著來升級一下 RichText.vue 組件,以使用以上 worker :
<template>
<div v-html="contentHtml"></div>
</template>
<script>
import Worker from "./render-html.worker.js";
const worker = new Worker();
export default {
props: ["content"],
data: () => ({
contentHtml: ""
}),
mounted() {
// 等待處理好的 HTML 內容,并更新到狀態(tài)中
worker.onmessage = ({ data }) => {
this.contentHtml = data;
};
// 將原始內容傳遞給 worker 渲染
worker.postMessage(this.content);
}
};
</script>
結果
你肯定很好奇,經過這一番折騰,性能上有何改善呢?看看就知道了。
在 main.js 等處設置 Vue.config.performance = true 后,在 Chrome DevTools 里的 performance 選項卡中可查看性能監(jiān)測數據。
結果分別是:組件渲染(創(chuàng)建 VDom 結構的時間)快了 20.65 倍、patch(將 VDom 結構應用到 DOM 上的時間)快了 1.39 倍。

回復 資料包領取我整理的進階資料包回復 加群,加入前端進階群console.log("文章點贊===文章點在看===你我都快樂")Bug離我更遠了,下班離我更近了
