實測 Vue SSR 的渲染性能:避開 20 倍耗時

導語:Vue SSR是Vue.js 2.0引入的直出渲染方案,本文將全面解析virtual-dom-based 及 string-based 的原理並對其進行對比。 1、前言

前端技術年年有新寵,Vue.js 2.0以其輕量級、漸進式、簡潔的語法在MVVM框架中脫穎而出,一經推出便很受業界青睞。前端

QQ空間Hybrid業務也在積極推進MVVM框架重構H5頁面。爲了提升首屏渲染速度,wns緩存+直出 是必不可少的。在Vue 1× 時代,沒有 server-side-render 方案,直出須要專門給寫一份首屏非Vue語法的模板。Vue2.0 server-side-render(簡稱Vue SSR)的推出,成功地讓先後端渲染模板代碼同構。vue

不過對於海量PV級的業務,直出模板的渲染效率直接影響服務端的壓力,在對業務代碼重構的同時,直出模板的性能是須要衡量的關鍵指標。node

當前經常使用的模板渲染方案能夠歸結成兩類:git

  • a類:string-based (基於字符串拼接)github

  • b類:virtual-dom-based(基於虛擬dom對象)正則表達式

Vue SSR的模板是virtual-dom-based,因此QQ空間Hybrid業務作Vue 2.0的改造的同時,模板類型也從以前的a類轉換成b類。後端

本文是在實際業務場景中對Vue SSR的渲染性能作測試,並解析渲染步驟,給出嘗試優化的方案和最終結論。緩存

2、經常使用string-based模板渲染原理介紹

仍是先介紹下經常使用的string-based模板的寫法和編譯後的樣子, 幫助不太熟悉HTML模板原理的同窗能夠更容易理解文章後面優化的過程。微信

首先看一下H5頁面經常使用的HTML模板寫法:markdown

上面的模板應該是前端開發者很熟悉的一種模板語法,經過<%%>來包裹邏輯,<%=%>來賦值。

下面來看看模板代碼被編譯後的JS是什麼樣的:

JS中執行 tmpl.test(data) 便可拿到渲染出來的HTML片斷。tmpl.test 就至關因而一個test模板的render函數。

那麼這個render函數是如何經過上面的script模板轉換過來的呢,此時須要一個模板的編譯工具來實現,編譯工具的核心是一個正則表達式:

let jscReg = new RegExp(/(?:(?:\r\n|\r|\n)\s*?)?<%([=-]?)([\w\W\r\n]*?)%>(?:\r\n|\r|\n)?/gmi);

用這個正則表達式,能夠將模板代碼中的 JS邏輯與JS屬性值 分割出來,再經過'引號來將靜態dom串包裹,拼接,最後加上函數的頭尾,就能獲得一個完整的render函數。編譯過程能夠經過預編譯在線下實現,也能夠在線上實時動態完成。

看到如今應該都理解了string-based HTML模板在JS中渲染的方式了吧,這種類型的模板渲染效率實際上是最高的,由於它的render函數是不存在任何冗餘邏輯,completely字符串拼接。

3、Vue SSR性能如何

首先在QQ空間禮物商城列表首屏來作個實驗,使用手機QQ掃描下方二維碼便可前往查看(建議手機QQ掃一掃,微信中打開只是靜態展現):

Vue SSR渲染調用代碼以下:

傳統模板渲染調用

實測循環 1w 次渲染耗時對比:

  • 傳統模板:143ms

  • Vue SSR:2685ms

Vue SSR的耗時是傳統模板的近20倍!

4、20倍差距是如何產生的?

20倍差距是如何產生的?咱們來分析一下Vue SSR完整的渲染過程。

Vue模板片斷:

Vue模板語法大部分都是指令試的僞代碼,既不是HTML語法,也不是JS語法,這也是virtual-dom類模板渲染較複雜的緣由之一。

步驟1:模板字符串經過正則解析成virtual-dom對象:

步驟2:生成with綁定上下文對象的render函數( 該函數被緩存):

步驟3:_h ----> _createElement    執行render函數返回 包含了業務數據 的vnode對象:

步驟4:遍歷dom對象屬性 拼接字符串 HTML渲染完成:

4、性能瓶頸分析

在上文中能夠看到,步驟1(模板字符串經過正則解析成virtual-dom對象)與步驟2(生成with綁定上下文對象的render函數)都已經被緩存,在本次對比中直接忽略其耗時,問題只能出在步驟三、4:

  • 步驟3:執行render函數生成vnode-tree對象;

  • 步驟4:遞歸遍歷vnode-tree將其轉化拼接成HTML;

相比傳統string-based模板以最直接的方式拼接HTML,邏輯包含了 with、call、對象建立、遞歸拼接 是制約性能的關鍵,因爲vnode對象是包含了業務數據,不能經過緩存vnode來解決,即使是緩存了vnode,步驟4的拼接耗時也是瓶頸所在。

5、緩存沒有用,直接上大招!

既然咱們已經知道什麼樣的render函數是最快的,那麼就作個工具直接把Vue模板編譯成string-based類的render函數便可,目標:

引入jscHelper github.com/jialunguo/v…

步驟1:

將vue-server-render在 第1步 生成的 virtual-dom對象 拼成 string-based語法的模板字符串,genEl是先將Vue模板語法轉換成jsc語法:

1)值的轉換

2)for循環轉換

3)if判斷轉換

4)處理屬性

5)Vue指令轉換

6)樣式class和內聯代碼轉換

7)遞歸處理子dom

8)標籤閉合

這樣Vue模板就被jscHelper轉換成了傳統的string-based類模板。

步驟2:

再經過文章開頭介紹的正則表達式:

let jscReg = new RegExp(/(?:(?:\r\n|\r|\n)\s*?)?<%([=-]?)([\w\W\r\n]*?)%>(?:\r\n|\r|\n)?/gmi);

將模板編譯成render函數,加以緩存。詳細過程請看GitHub上的jscHelper源碼。

6、最終優化效果

最終優化效果很是明顯:

  • 傳統模板:                    160    ms

  • Vue SSR :                     2963 ms

  • Vue SSR + jscHelper:   210   ms

注:因爲jscHelper須要對Vue的語法作轉換,複雜的語法會增長耗時,因此耗時仍是略高於傳統模板的。

7、寫在最後

經過jscHelper對vue-server-render的性能作提高,須要持續地維護對Vue語法的兼容,並且目前並不支持相似<transition>, <keep-alive>, <router-view>等高級語法,對組件的渲染方式也須要兼容。做爲使用方,咱們更但願Vue做者自己能多提供一種簡單的string-based渲染方式來做爲高性能的直出渲染方案。

我已經在GitHub上面提了相關的issue github.com/vuejs/vue/i… ,但願能在Vue.js將來版本中看到更好的渲染實現。

相關文章
相關標籤/搜索