真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Vue中SSR如何實現(xiàn)-創(chuàng)新互聯(lián)

這篇文章主要為大家展示了“Vue中SSR如何實現(xiàn)”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Vue中SSR如何實現(xiàn)”這篇文章吧。

商州網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、自適應(yīng)網(wǎng)站建設(shè)等網(wǎng)站項目制作,到程序開發(fā),運營維護。成都創(chuàng)新互聯(lián)自2013年創(chuàng)立以來到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)。

思路

Vue 提供了一個官方 Demo,該 Demo 優(yōu)點是功能大而全,缺點是對新手不友好,容易讓人看蒙。因此,今天我們來寫一個更加容易上手的 Demo。總共分三步走,循序漸進。

  1. 寫一個簡單的前端渲染 Demo(不包含 Ajax 數(shù)據(jù));

  2. 將前端渲染改成后端渲染(仍然不包含 Ajax 數(shù)據(jù));

  3. 在后端渲染的基礎(chǔ)上,加上 Ajax 數(shù)據(jù)的處理;

第一步:前端渲染 Demo

這部分比較簡單,就是一個頁面中包含兩個組件:Foo 和 Bar。




 
  // app.js,也是 webpack 打包入口 import Vue from 'vue'; import App from './App.vue'; var app = new Vue({  el: '#app',  components: {  App  } });
// App.vue

// Foo.vue

// Bar.vue

最終渲染結(jié)果如下圖所示,源碼請參考這里。

Vue中SSR如何實現(xiàn)

第二步:后端渲染(不包含 Ajax 數(shù)據(jù))

第一步的 Demo 雖不包含任何 Ajax 數(shù)據(jù),但即便如此,要把它改造成后端渲染,亦非易事。該從哪幾個方面著手呢?

  1. 拆分 JS 入口;

  2. 拆分 Webpack 打包配置;

  3. 編寫服務(wù)端渲染主體邏輯。

1. 拆分 JS 入口

在前端渲染的時候,只需要一個入口 app.js?,F(xiàn)在要做后端渲染,就得有兩個 JS 文件:entry-client.js 和 entry-server.js 分別作為瀏覽器和服務(wù)器的入口。

先看 entry-client.js,它跟第一步的 app.js 有什么區(qū)別嗎? → 沒有區(qū)別,只是換了個名字而已,內(nèi)容都一樣。

再看 entry-server.js,它只需返回 App.vue 的實例。

// entry-server.js
export default function createApp() {
 const app = new Vue({
 render: h => h(App)
 });
 return app; 
};

entry-server.js 與 entry-client.js 這兩個入口主要區(qū)別如下:

  1. entry-client.js 在瀏覽器端執(zhí)行,所以需要指定 el 并且顯式調(diào)用 $mount 方法,以啟動瀏覽器的渲染。

  2. entry-server.js 在服務(wù)端被調(diào)用,因此需要導(dǎo)出為一個函數(shù)。

2. 拆分 Webpack 打包配置

在第一步中,由于只有 app.js 一個入口,只需要一份 Webpack 配置文件?,F(xiàn)在有兩個入口了,自然就需要兩份 Webpack 配置文件:webpack.server.conf.js 和 webpack.client.conf.js,它們的公共部分抽象成 webpack.base.conf.js。

關(guān)于 webpack.server.conf.js,有兩個注意點:

  1. libraryTarget: 'commonjs2' → 因為服務(wù)器是 Node,所以必須按照 commonjs 規(guī)范打包才能被服務(wù)器調(diào)用。

  2. target: 'node' → 指定 Node 環(huán)境,避免非 Node 環(huán)境特定 API 報錯,如 document 等。

3. 編寫服務(wù)端渲染主體邏輯

Vue SSR 依賴于包 vue-server-render,它的調(diào)用支持兩種入口格式:createRenderer 和 createBundleRenderer,前者以 Vue 組件為入口,后者以打包后的 JS 文件為入口,本文采取后者。

// server.js 服務(wù)端渲染主體邏輯
// dist/server.js 就是以 entry-server.js 為入口打包出來的 JS 
const bundle = fs.readFileSync(path.resolve(__dirname, 'dist/server.js'), 'utf-8'); 
const renderer = require('vue-server-renderer').createBundleRenderer(bundle, {
 template: fs.readFileSync(path.resolve(__dirname, 'dist/index.ssr.html'), 'utf-8')
});

server.get('/index', (req, res) => {
 renderer.renderToString((err, html) => {
 if (err) {
  console.error(err);
  res.status(500).end('服務(wù)器內(nèi)部錯誤');
  return;
 }
 res.end(html);
 })
});

server.listen(8002, () => {
 console.log('后端渲染服務(wù)器啟動,端口號為:8002');
});

這一步的最終渲染效果如下圖所示,從圖中我們可以看到,組件已經(jīng)被后端成功渲染了。源碼請參考這里。

Vue中SSR如何實現(xiàn)

第三步:后端渲染(預(yù)獲取 Ajax 數(shù)據(jù))

這是關(guān)鍵的一步,也是最難的一步。

假如第二步的組件各自都需要請求 Ajax 數(shù)據(jù)的話,該怎么處理呢?官方文檔給我們指出了思路,我簡要概括如下:

  1. 在開始渲染之前,預(yù)先獲取所有需要的 Ajax 數(shù)據(jù)(然后存在 Vuex 的 Store 中);

  2. 后端渲染的時候,通過 Vuex 將獲取到的 Ajax 數(shù)據(jù)分別注入到各個組件中;

  3. 把全部 Ajax 數(shù)據(jù)埋在 window.INITIAL_STATE 中,通過 HTML 傳遞到瀏覽器端;

  4. 瀏覽器端通過 Vuex 將 window.INITIAL_STATE 里面的 Ajax 數(shù)據(jù)分別注入到各個組件中。

下面談幾個重點。

我們知道,在常規(guī)的 Vue 前端渲染中,組件請求 Ajax 一般是這么寫的:“在 mounted 中調(diào)用 this.fetchData,然后在回調(diào)里面把返回數(shù)據(jù)寫到實例的 data 中,這就 ok 了?!?/p>

在 SSR 中,這是不行的,因為服務(wù)器并不會執(zhí)行 mounted 周期。那么我們是否可以把 this.fetchData

提前到 created 或者 beforeCreate 這兩個生命周期中執(zhí)行?同樣不行。原因是:this.fetchData 是異步請求,請求發(fā)出去之后,沒等數(shù)據(jù)返回呢,后端就已經(jīng)渲染完了,無法把 Ajax 返回的數(shù)據(jù)也一并渲染出來。

所以,我們得提前知道都有哪些組件有 Ajax 請求,等把這些 Ajax 請求都返回了數(shù)據(jù)之后,才開始組件的渲染。

// store.js
function fetchBar() {
 return new Promise(function (resolve, reject) {
 resolve('bar ajax 返回數(shù)據(jù)');
 });
}

export default function createStore() {
 return new Vuex.Store({
 state: {
  bar: '',
 },
 actions: {
  fetchBar({commit}) {
  return fetchBar().then(msg => {
   commit('setBar', {msg})
  })
  }
 },
 mutations:{
  setBar(state, {msg}) {
  Vue.set(state, 'bar', msg);
  }
 }
 })
}
// Bar.uve
asyncData({store}) {
 return store.dispatch('fetchBar');
},
computed: {
 bar() {
 return this.$store.state.bar;
 }
}

組件的 asyncData 方法已經(jīng)定義好了,但是怎么索引到這個 asyncData 方法呢?先看我的根組件 App.vue 是怎么寫的。

// App.vue

從根組件 App.vue 我們可以看到,只需要解析其 components 字段,便能依次找到各個組件的 asyncData 方法了。

// entry-server.js 
export default function (context) {
 // context 是 vue-server-render 注入的參數(shù)
 const store = createStore();
 let app = new Vue({
 store,
 render: h => h(App)
 });

 // 找到所有 asyncData 方法
 let components = App.components;
 let prefetchFns = [];
 for (let key in components) {
 if (!components.hasOwnProperty(key)) continue;
 let component = components[key];
 if(component.asyncData) {
  prefetchFns.push(component.asyncData({
  store
  }))
 }
 }

 return Promise.all(prefetchFns).then((res) => {
 // 在所有組件的 Ajax 都返回之后,才最終返回 app 進行渲染
 context.state = store.state;
 // context.state 賦值成什么,window.__INITIAL_STATE__ 就是什么
 return app;
 });
};

還有幾個問題比較有意思:

1、是否必須使用 vue-router?→ 不是。雖然官方給出的 Demo 里面用到了 vue-router,那只不過是因為官方 Demo 是包含多個頁面的 SPA 罷了。一般情況下,是需要用 vue-router 的,因為不同路由對應(yīng)不同的組件,并非每次都把所有組件的 asyncData 都執(zhí)行的。但是有例外,比如我的這個老項目,就只有一個頁面(一個頁面中包含很多的組件),所以根本不需要用到 vue-router,也照樣能做 SSR。主要的區(qū)別就是如何找到那些該被執(zhí)行的 asyncData 方法:官方 Demo 通過 vue-router,而我通過直接解析 components 字段,僅此而已。

2、是否必須使用 Vuex? → 是,但也不是,請看尤大的回答。為什么必須要有類似 Vuex 的存在?我們來分析一下。

2.1. 當(dāng)預(yù)先獲取到的 Ajax 數(shù)據(jù)返回之后,Vue 組件還沒開始渲染。所以,我們得把 Ajax 先存在某個地方。

2.2. 當(dāng) Vue 組件開始渲染的時候,還得把 Ajax 數(shù)據(jù)拿出來,正確地傳遞到各個組件中。

2.3. 在瀏覽器渲染的時候,需要正確解析 window.INITIAL_STATE ,并傳遞給各個組件。

因此,我們得有這么一個獨立于視圖以外的地方,用來存儲、管理和傳遞數(shù)據(jù),這就是 Vuex 存在的理由。

3、后端已經(jīng)把 Ajax 數(shù)據(jù)轉(zhuǎn)化為 HTML 了,為什么還需要把 Ajax 數(shù)據(jù)通過 window.INITIAL_STATE 傳遞到前端? → 因為前端渲染的時候仍然需要知道這些數(shù)據(jù)。舉個例子,你寫了一個組件,給它綁定了一個點擊事件,點擊的時候打印出 this.msg 字段值?,F(xiàn)在后端是把組件 HTML 渲染出來了,但是事件的綁定肯定得由瀏覽器來完成啊,如果瀏覽器拿不到跟服務(wù)器端同樣的數(shù)據(jù)的話,在觸發(fā)組件的點擊事件的時候,又上哪兒去找 msg 字段呢?

至此,我們已經(jīng)完成了帶 Ajax 數(shù)據(jù)的后端渲染了。這一步最為復(fù)雜,也最為關(guān)鍵,需要反復(fù)思考和嘗試。具體渲染效果圖如下所示,源碼請參考這里。

Vue中SSR如何實現(xiàn)

效果

大功告成了嗎?還沒。人們都說 SSR 能提升首屏渲染速度,下面我們對比一下看看到底是不是真的。(同樣在 Fast 3G 網(wǎng)絡(luò)條件下)。

Vue中SSR如何實現(xiàn)

Vue中SSR如何實現(xiàn)

官方思路的變形

行文至此,關(guān)于 Vue SSR Demo便已經(jīng)結(jié)束了。后面是我結(jié)合自身項目特點的一些變形,不感興趣的讀者可以不看。

第三步官方思路有什么缺點嗎?我認為是有的:對老項目來說,改造成本比較大。需要顯式的引入 vuex,就得走 action、mutations 那一套,無論是代碼改動量還是新人學(xué)習(xí)成本,都不低。

有什么辦法能減少對舊有前端渲染項目的改動量的嗎?我是這么做的。

// store.js
// action,mutations 那些都不需要了,只定義一個空 state
export default function createStore() {
 return new Vuex.Store({
 state: {}
 })
}
// Bar.vue
// tagName 是組件實例的名字,比如 bar1、bar2、foo1 等,由 entry-server.js 注入
export default {
 prefetchData: function (tagName) {
 return new Promise((resolve, reject) => {
  resolve({
  tagName,
  data: 'Bar ajax 數(shù)據(jù)'
  });
 })
 }
}
// entry-server.js
return Promise.all(prefetchFns).then((res) => {
 // 拿到 Ajax 數(shù)據(jù)之后,手動將數(shù)據(jù)寫入 state,不通過 action,mutation 那一套
 // state 內(nèi)部區(qū)分的 key 值就是 tagName,比如 bar1、bar2、foo1 等
 res.forEach((item, key) => {
 Vue.set(store.state, `${item.tagName}`, item.data);
 });
 context.state = store.state;
 return app;
});
// ssrmixin.js
// 將每個組件都需要的 computed 抽象成一個 mixin,然后注入
export default {
 computed: {
 prefetchData () {
  let componentTag = this.$options._componentTag; // bar1、bar2、foo1
  return this.$store.state[componentTag];
 }
 }
}

以上是“Vue中SSR如何實現(xiàn)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計公司行業(yè)資訊頻道!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。


本文標題:Vue中SSR如何實現(xiàn)-創(chuàng)新互聯(lián)
新聞來源:http://weahome.cn/article/decdpc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部