Vue SSR的渲染性能

一.前言

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

爲了提升首屏渲染速度 緩存+直出 是必不可少的。在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後端

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

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

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

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

下面來看看模板代碼被編譯後的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性能如何

實測循環 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 https://github.com/jialunguo/vue-server-render-jscHelper

 

步驟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)標籤閉合

 

步驟2:

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

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

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

7、寫在最後

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

 相關的issue https://github.com/vuejs/vue/issues/5176 ,但願能在Vue.js將來版本中看到更好的渲染實現。

相關文章
相關標籤/搜索