標籤:

Vue v2.3.0 ssr 升級手冊

不久前,vue 升級至了 2.3.0 版本,是一個 minor 的版本。該版本除了一些組件功能的優化之外,主要是升級 vue 的 ssr 功能,甚至於為之建立了一個獨立的 Git Book。

我的博客之前用的就是 ssr,這次升級自然也是要嘗試一把。ssr 的優勢和實現在這裡就不再贅述了,不太了解的可以看之前的文章,這裡主要還是來看看升級的變化之處。

升級的第一件事自然就是先升級依賴,將 vue, vue-server-renderer 等依賴的版本升級至最新 npm up -S(作者 vue 的版本為 v2.3.3)。升級之後,直接啟動服務看看,應該是沒有問題的,文檔也提到可以使用之前的配置,但建議改為新版本的方式。

雖然,依賴升級之後同樣能運行,但還是來看看有哪些提升或變化的地方?

Renderer Create Options

更新之後,在創建 renderer 時可以為它添加配置,其中的 template 屬性可以為我們省去之前的許多繁雜的小工作,比如:

  • 在 html 中使用 <!--vue-ssr-outlet-->,renderer 會自動將 app 生成的 html 插入此處,而不用自己再進行替換操作
  • 將 context.state 插入到 html 中,並自動使用 serialize-javascript 進行轉義來防止 XSS 攻擊
  • 直接通過 cache 屬性配置組件緩存

以上這些都是在之前版本中常被使用到的,剩下一些 clientManifest, inject, runInNewContext 等新增的東西後面會再提到。

Lifesycle & data prefetch

由於在 ssr 階段不會有一系列的變更,所以更新之後 vue 在 ssr 階段只會執行 beforeCreate 和 created 這個兩個生命周期函數。

相信你一定會問那如果遇到非同步請求該怎麼辦哪?這裡同之前並沒有變化,仍舊是通過設置組件的自定義方法來獲取數據,最終通過 vuex 將數據傳遞迴客戶端。沒什麼變化就不展開了,不清楚的可以看一下文檔,寫得已經相當詳細了。

不過此處有一點優化,由於數據已經在伺服器端已準備完成,客戶端就無需再像伺服器端發送非同步請求,而是可以直接從 store 中獲取數據。

代碼結構與同構

文檔的這一節在內容上和之前的文檔基本沒有區別,不過其中提到一點指出了我原有代碼的不足之處,也給了我不少啟發。

通常大家的 app.js 會是這樣

// 省略其他依賴...import store from ./vuex;import router from ./router;sync(store, router);const app = new Vue({ store, router, render: h => h(/* ... */)});export {app, router, store};

這看上去並沒有任何問題。在平時的瀏覽器環境中,每次刷新頁面都會重新載入一次文件,是一個全新的環境(或沙盒)。但當同構了代碼之後,伺服器端同樣運行這段代碼時,就可能出現問題。

因為 node 端服務啟動後,vue 的實例就被初始化完成,所有的請求會公用這同一個實例,這就可能造成混亂。所以為每個請求返回一個新的 vue 的實例是一個比較好的處理方法,router 和 store 同樣適用這個道理。

// 省略其他依賴...import createStore from ./vuex;import createRouter from ./router;const createApp = () => { const store = createStore(); const router = createRouter(); sync(store, router); const app = new Vue({ store, router, render: h => h(/* ... */) }); return {app, router, store};};export default createApp;

雖然,我至今還沒有遇到過實例衝突的問題,不過我還是覺得文檔說的很有道理,可能會發生這樣的情況。多個實例會克服衝突的問題,但它同時也增加伺服器的負擔。

這樣處理之後,就可能將之前提到的 runInNewContext 配置設為 false,默認為 true 會為每個 bundle 創建新的上下文。

Webpack build plugin

升級的最大變化在於對 webpack 提供更強大的支持,在 vue-server-renderer 包中新增了兩個 webpack plugin: server-plugin 和 client-plugin,分別用於伺服器端和客戶端。

server-plugin

server-plugin 會默認創建一個名為 vue-ssr-server-bundle.json 的文件作為 createBundleRenderer 的第一個參數。

這裡 webpack 的 output.filename 設置還是要定義的,不然打包的時候會報錯。

上面這點上一個版本就能做到,使用 server-plugin 的好處是在於,它提供了服務端的 source-map功能,這可是開發利器。另一大好處是,支持 hot-reload,不過我之前使用的是 webpack-middleware 就已經支持該特性了。

熟悉 webpack 的都知道,webpack-middleware 是將文件放在內存里的,而這裡的 createBundleRenderer 用的是文件訪問,所以,直接傳路徑是有問題的。不過,它也支持傳一個對象,所以記得每次服務端代碼更新之後要重新創建 renderer,還有讀文件之後要將 string 轉換為 object 傳給 createBundleRenderer。

// 省略...const updateRenderer = () => { try { const options = { clientManifest: JSON.parse(expressDevMiddleware.fileSystem.readFileSync(clientManifestFilePath, utf-8)) }; createRenderer(JSON.parse(mfs.readFileSync(serverBundleFilePath, utf-8)), options); } catch(e) { createRenderer(JSON.parse(mfs.readFileSync(serverBundleFilePath, utf-8))); } console.log(Renderer is updated.);};// watch and update server rendererconst serverCompiler = webpack(serverConfig);serverCompiler.outputFileSystem = mfs;serverCompiler.watch({}, (err, stats) => { if (err) throw err; stats = stats.toJson(); stats.errors.forEach(err => console.error(err)); stats.warnings.forEach(err => console.warn(err)); updateRenderer();});

client-plugin

如果你使用過升級之前 vue ssr 的功能,那你肯定會對一系列有關 html 的操作有映象,比如替換 html,插入 state 等。現在,有了 client-plugin 它就能代替原有的 html-webpack-plugin 來生成 html,並把之前那些繁雜的事都替你處理了。

上面這些對已經實現 ssr 的你可能不是很有吸引力,不過,下面這點可能會讓你感興趣。這個插件還自帶為你的 ccs 或 js 添加 preload 和 prefetch 功能,它可以加快你網站的載入速度,如果你還不清楚 prefetch 和 preload 是什麼的話,可以先讀一下這篇文章。

如果你使用的是 webpack-server,那麼,你按文檔上的例子來應該沒什麼問題。但如果你和我一樣使用的是 webpack-middleware,那麼,這裡還是有些彆扭的,需要和之前一樣每次 plugin 生成後去重新構建 renderer。

// 省略...clientCompiler.plugin(done, updateRenderer);

同 server-plugin 一樣文件讀出來的是 string,你要將它轉換為對象。其他基本的配置按文檔上的來就行,遇到問題的可以參考下我的代碼。

吹了這麼多,不足之處還是得指出來,client-plugin 還不能像 html-webpack-plugin 監聽 html 文件,每次修改 html 都得手動重啟服務有點麻煩,可以優化一波...

升級所要注意的就差不多就這些了。還有一點,之前 vue 推薦使用 renderToStream 來返回頁面,如果組件生命周期中有請求的話,使用 stream 可能導致組件還未構建完成就已經發送。所以,更新之後 vue 推薦使用 renderToString。

結尾

vue 的確是非常緊跟潮流,就像這次加入的 preload 和 prefetch 功能,但因開發團隊人員太少(相對於 react 和 angular),導致版本並不是很穩定。

如果,你問我 vue 好不好?我會說,好。

如果,你問我要不要學 vue?我會說,學。

如果,你問我 vue 能不能上生產?我的建議是,不如咋們半年後再談...


推薦閱讀:

前端日刊-2017.12.15
前端日刊-2018.02.22
前端日刊-2018.01.06
前端日刊-2018.01.18
前端日刊-2017.12.30

TAG:前端開發 |