Vue SSR 即時(shí)編譯技術(shù)的實(shí)現(xiàn)
當(dāng)我們?cè)诜?wù)端渲染 Vue 應(yīng)用時(shí),無論服務(wù)器執(zhí)行多少次渲染,大部分 VNode 渲染出的字符串是不變的,它們有一些來自于模板的靜態(tài) html,另一些則來自模板動(dòng)態(tài)渲染的節(jié)點(diǎn)(雖然在客戶端動(dòng)態(tài)節(jié)點(diǎn)有可能會(huì)變化,但是在服務(wù)端它們是不變的)。將這兩種類型的節(jié)點(diǎn)提取出來,僅在服務(wù)端渲染真正動(dòng)態(tài)的節(jié)點(diǎn)(serverPrefetch 預(yù)取數(shù)據(jù)相關(guān)聯(lián)的節(jié)點(diǎn)),可以顯著的提升服務(wù)端的渲染性能。
提取模板中靜態(tài)的 html 只需在編譯期對(duì)模板結(jié)構(gòu)做解析,而判斷動(dòng)態(tài)節(jié)點(diǎn)在服務(wù)端渲染階段是否為靜態(tài),需在運(yùn)行時(shí)對(duì) VNode 做 Diff,將動(dòng)態(tài)節(jié)點(diǎn)轉(zhuǎn)化成靜態(tài) html 需要修改渲染函數(shù)的源代碼,我們將這種在運(yùn)行時(shí)優(yōu)化服務(wù)端渲染函數(shù)的技術(shù)稱作 SSR 即時(shí)編譯技術(shù)(JIT)。
GitHub:vue-ssr-jit
JIT Diff 算法
首要面對(duì)的問題是如何 Diff,完成這項(xiàng)工作需要兩個(gè) VNode,其中一個(gè)通過 serverPrefetch / asyncData 載入動(dòng)態(tài)數(shù)據(jù),我們稱之為 Dynamic VNode,另一個(gè)未載入任何數(shù)據(jù),我們稱之為 Static VNode。我們做了一個(gè)大膽的假設(shè),對(duì)任何用戶來說,Static VNode 渲染出的 html 是一致的,并且 Static VNode 是 Dynamic VNode 的子集,不同用戶的差異點(diǎn)在 Static VNode 相對(duì) Dynamic VNode 的補(bǔ)集當(dāng)中。
以上假設(shè)對(duì)絕大部分的 Web 應(yīng)用都是成立的,某些意料之外的情況將在文末做討論
Diff 的核心在于從 Staitc VNode 中標(biāo)記 Dynamic VNode,下一次僅渲染被標(biāo)記的 Dynamic VNode,Diff 算法的技術(shù)示意圖如下所示
優(yōu)化前的 Dynamic VNode 渲染流程圖如下
優(yōu)化后的 Dynamic VNode 渲染流程圖如下
如何修改渲染函數(shù)的源代碼
修改渲染函數(shù)的難點(diǎn)在于如何建立 VNode 與源代碼的對(duì)應(yīng)關(guān)系,否則我們無從得知需要優(yōu)化的節(jié)點(diǎn)是哪段代碼生成的,這看起來非常困難。幸運(yùn)的是 Vue 的模板語法提供了很不錯(cuò)的約束,內(nèi)置的編譯引擎也確保了渲染函數(shù)代碼結(jié)構(gòu)可預(yù)測(cè)。
如下模板代碼編譯生成的渲染函數(shù)結(jié)構(gòu)是有章可循的
<template> <div> <static-view/> <dynamic-view/> </div> </template>
_c("div", [ _c("static-view"), _c("dynamic-view") ], 1)
執(zhí)行 _c(xxx)
會(huì)生成一個(gè) VNode 節(jié)點(diǎn),解析 _c(xxx)
會(huì)生成一個(gè)固定結(jié)構(gòu)的 AST,將 AST 與 VNode 做綁定,如果當(dāng)前 VNode 為靜態(tài)節(jié)點(diǎn),則修改對(duì)應(yīng)的 AST,VNode 樹遍歷結(jié)束后再將 AST 轉(zhuǎn)化成可執(zhí)行的代碼,代碼里便有了我們對(duì) VNode 做的優(yōu)化。詳細(xì)的技術(shù)實(shí)現(xiàn)可參考項(xiàng)目中的 patch.js 和 patch-context.js 文件。
如下流程圖演示了修改渲染函數(shù)源代碼的過程
一個(gè)簡(jiǎn)單的例子如下
<template> <div> <router-link to="/">{{name}}</router-link> <router-view></router-view> </div> </template> <script> export default { data() { return { name: 'vue-ssr-jit' } } } </script>
官方編譯器生成的代碼:
_c("div", [ _c("router-link", {attrs: { to: "/" }}, [ _vm._v(_vm._s(_vm.name)) ]), _c("router-view") ], 1)
使用 SSR 即時(shí)編譯生成的代碼:
_c("div", [ _vm._ssrNode( "<a href=\"/\" class=\"router-link-active\">vue-ssr-jit</a>" ), _c("router-view") ], 1);
用法
npm install --save vue-ssr-jit
const { createBundleRenderer } = require('vue-ssr-jit')
createBundleRenderer
與官方同名函數(shù)接口一致,參考 vue ssr 指南
推薦使用 serverPrefetch 預(yù)取數(shù)據(jù),也支持使用 asyncData 預(yù)取數(shù)據(jù),參考 demo
哪些場(chǎng)景會(huì)導(dǎo)致優(yōu)化失敗
cookie
不要在服務(wù)端渲染周期內(nèi)使用 cookie,除非你確定此數(shù)據(jù)與用戶無關(guān)。可以在 serverPrefetch / asyncData 方法內(nèi)使用 cookie,服務(wù)端渲染周期結(jié)束后也可以被使用,例如:mounted
,updated
等等。
不推薦用法
data() { let cookie = cookie; try { cookie = document.cookie; } catch(e) { cookie = global.xxx.cookie; } return { cookie }; },
推薦用法
mounted() { this.cookie = document.cookie; },
v-for
v-for 指令建議用 dom 元素單獨(dú)包裹,不建議和其他組件并排使用,由于 for 循環(huán)會(huì)擾亂抽象語法樹與 VNode 節(jié)點(diǎn)的對(duì)應(yīng)關(guān)系,除非 v-for 指令所在的整個(gè)節(jié)點(diǎn)層級(jí)全為靜態(tài),否則將不會(huì)對(duì)包含 v-for 指令的層級(jí)及子級(jí)做優(yōu)化。
不推薦用法
<template> <div> <div v-for="item in items" :key="item.id">{{item.value}}</div> <static-view></static-view> </div> </template>
推薦用法
<template> <div> <div> <div v-for="item in items" :key="item.id">{{item.value}}</div> </div> <static-view></static-view> </div> </template>
閉包
某些場(chǎng)景下,渲染函數(shù)引用了閉包變量,同時(shí)這個(gè)閉包變量又影響著一個(gè)動(dòng)態(tài)的節(jié)點(diǎn),通過 ast 逆向生成的渲染函數(shù)暫時(shí)無法追蹤到之前的閉包引用,執(zhí)行時(shí)會(huì)因找不到變量而報(bào)錯(cuò),碰到這種情況,解析引擎將放棄當(dāng)前組件的 ast 優(yōu)化,轉(zhuǎn)而使用優(yōu)化前的渲染函數(shù)。
不推薦用法:
<template> <img :src="require(`@/assets/${img}`)" > </template>
推薦用法:
<template> <img :src="getImgUrl(img)" > </template>
到此這篇關(guān)于Vue SSR 即時(shí)編譯技術(shù)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Vue SSR 即時(shí)編譯 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Egg Vue SSR 服務(wù)端渲染數(shù)據(jù)請(qǐng)求與asyncData
- 15分鐘學(xué)會(huì)vue項(xiàng)目改造成SSR(小白教程)
- vue ssr+koa2構(gòu)建服務(wù)端渲染的示例代碼
- Vue使用預(yù)渲染代替SSR的方法
- 如何構(gòu)建 vue-ssr 項(xiàng)目的方法步驟
- vuecli項(xiàng)目構(gòu)建SSR服務(wù)端渲染的實(shí)現(xiàn)
- vue的ssr服務(wù)端渲染示例詳解
- vue中vue-router的使用說明(包括在ssr中的使用)
- 關(guān)于VueSSR的一些理解和詳細(xì)配置
- Vue.js?狀態(tài)管理及?SSR解析
相關(guān)文章
Vue.js中數(shù)組變動(dòng)的檢測(cè)詳解
這篇文章給大家主要介紹了Vue.js中數(shù)組變動(dòng)的檢測(cè),文中給出了詳細(xì)的示例代碼和過程介紹,相信會(huì)對(duì)大家的理解和學(xué)習(xí)很有幫助,有需要的朋友們下面來一起看看吧。2016-10-10對(duì)vuex中g(shù)etters計(jì)算過濾操作詳解
今天小編就為大家分享一篇對(duì)vuex中g(shù)etters計(jì)算過濾操作詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-11-11vue elementUI table表格自定義樣式滾動(dòng)效果
這篇文章主要介紹了vue elementUI table表格自定義樣式滾動(dòng)效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-08-08利用Vue實(shí)現(xiàn)簡(jiǎn)易播放器的完整代碼
這篇文章主要給大家介紹了關(guān)于如何利用Vue實(shí)現(xiàn)簡(jiǎn)易播放器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12vue中實(shí)現(xiàn)滾動(dòng)加載更多的示例
下面小編就為大家?guī)硪黄獀ue中實(shí)現(xiàn)滾動(dòng)加載更多的示例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11el-dialog關(guān)閉再打開后窗口內(nèi)容不刷新問題及解決
這篇文章主要介紹了el-dialog關(guān)閉再打開后窗口內(nèi)容不刷新問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02