Vue 2 服務端渲染初探

寫這篇文章, Vue 2 還在 Beta 呢...vue

參考資料

官方文檔寫得很清楚node

彷佛 Vue 1 有看到過經過 jsdom 作後端渲染的例子, 性能不佳.
Vue 2 開始將 Virtual DOM 做爲底層實現, 因而模塊分離開始支持 SSR.git

渲染步驟

4 步走戰略~緩存

安裝 hackernews 的例子, 完整的 app 渲染的例子包括:app

  1. 用 Webpack 的 node 模式把整個應用單獨打一個包

  2. Node 環境經過 API 將這個包加載到 vm 環境當中

  3. 應用在 vm 內部啓動 HTTP 請求抓取當前路由依賴的數據

  4. 生成網頁模板, 將 HTML 和初始數據嵌在中間

若是網頁依賴的數據少或者不依賴, 能夠簡化一點,
好比中間抓取 HTTP 的步驟去掉, 能夠簡化很多,
也許還能夠去掉 vm 那步, 直接經過引用文件來生成 HTML.

渲染 API

兩套 API 哦... 好像只用帶 bundle 那套...

https://github.com/vuejs/vue/...

  • createRenderer([rendererOptions])

  • renderer.renderToString(vm, cb)

  • renderer.renderToStream(vm)

  • createBundleRenderer(code, [rendererOptions])

  • bundleRenderer.renderToString([context], cb)

  • bundleRenderer.renderToStream([context])

後面三個 API 都帶上了 bundle, 此外看上去和前面的同樣,
bundle 是經過 Node.js 的 vm 模塊運行的, 每次的都從新啓動一遍代碼,
做者解釋這樣能清空整個 app 的狀態,
我推測這是由於用了 Vuex 以後, 數據會被緩存在內部沒法清理,
若是是單純經過 props 傳遞數據, 應該是能夠用前一套 API.

服務端渲染原理

有了 Virtual DOM 就好辦了

VNode 定義 https://github.com/vuejs/vue/...

HTML 渲染的代碼, 經過 write 同時支持到了 Stream 輸出:
https://github.com/vuejs/vue/...
https://github.com/vuejs/vue/...

若是用 bundle 模式, 注意每次都會運行 vm.runInNewContext 新建環境.
https://github.com/vuejs/vue/...
https://github.com/vuejs/vue/...

最後返回用戶的 HTML 實際上是拼接出來的,
注意首屏的動態數據, 也經過 window.__INITIAL_STATE__ 發送到瀏覽器,
https://github.com/vuejs/vue-...

緩存

速度快是由於緩存呢吧...

文檔 https://github.com/vuejs/vue/...

大體就是若是組件能夠根據一個 key 來肯定, 就能夠進行緩存,
靜態的組件固然是有固定的 key, 動態的組件根據 id 等數據生成 key,

serverCacheKey: props => props.item.id + '::' + props.item.last_updated

若是組件能夠找到緩存, 就直接返回緩存內容:
https://github.com/vuejs/vue/...

這也就意味着頂層的組件總之就是不能緩存的, 性能開銷免不了.
hackernews 的例子本地用 ab 壓了一下, Mac Pro 到 130+qps 了,

Concurrency Level:      100
Time taken for tests:   3.013 seconds
Complete requests:      400
Failed requests:        0
Total transferred:      11545200 bytes
HTML transferred:       11506000 bytes
Requests per second:    132.77 [#/sec] (mean)
Time per request:       753.205 [ms] (mean)
Time per request:       7.532 [ms] (mean, across all concurrent requests)
Transfer rate:          3742.21 [Kbytes/sec] received

可是這個 Demo 是用了緩存的, 破壞掉緩存性能落差很大,
我本身作的 Demo, 實際上加上緩存性能還不到這個一半...
看來跟應用的類型是有關的, 特別是節點偏多的應用影響更大.

數據策略

想象一下後端有個瀏覽器...

對於依賴數據, 目前的方案是在組件定義上提供 preFetch 函數,
服務端渲染時會主動查找掛載的部分, 調用進行數據抓取:
https://github.com/vuejs/vue-...
https://github.com/vuejs/vue-...

官方的例子當中 App 是帶了 Vuex 跟 vue-router 的,
因此 preFetch 方案整個集成在這些庫當中.
從實驗看, 內部嵌套的 preFetch 是不會被調用的, 只能從路由開始,
同時中間要用到 Promise.all 合併請求, 腦補一下.

好吧我以爲這是一個至關簡單粗暴的獲取數據的辦法,
但其實也很難解耦, 否則就要從路由直接推算數據才行,
主要以爲仍是不夠清晰, 限制挺多, 實際操做能犯錯的地方很多.

性能影響

反正比不上模板引擎

編譯後大體還能看到 Virtual DOM 的影子, 會有一些性能開銷,
不過話說回來 Virtual DOM 原本就很慢, 能優化一點已經不容易了...

module.exports={render:function(){with(this) {
  return _h('li', {
    staticClass: "news-item"
  }, [_h('span', {
    staticClass: "score"
  }, [_s(item.score)]), " ", _h('span', {
    staticClass: "title"
  }, [(item.url) ? [_h('a', {
    attrs: {
      "href": item.url,
      "target": "_blank"
    }
  }, [_s(item.title)]), " ", _h('span', {
    staticClass: "host"
  }, ["(" + _s(_f("host")(item.url)) + ")"])] : [_h('router-link', {
    attrs: {
      "to": '/item/' + item.id
    }

另外 vm.runInNewContext 有潛在的性能問題,
http://stackoverflow.com/q/98...
不清楚用在生產環境是怎樣, 我我的對此沒有多少經驗..

小結

愈來愈像 React...

Vue 2 算是把這麼多內容整合在一塊兒至關不容易,
不過服務端渲染 React 那麼久了, 仍是沒普及開, 性能是大問題,
相比較而言, Vue 2 增長了 cache 機制, 這能夠提升性能,
可是依賴數據時會帶來啓動 vm 開銷, 要是代碼量不小在麼辦?
具體效果仍是要等正式發佈後, 等有權威的評測...

此外服務端抓取數據的策略須要挖一挖, 找找更漂亮的策略,我我的但願能更好地解耦, 梳理出更加清晰的依賴,那樣也能夠適應更多的場景, 靈活地使用, 而不是限定死了這樣用.固然也是由於服務端渲染, 這個原本存在的問題顯得更明確了.

相關文章
相關標籤/搜索