成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

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

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

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

思路

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

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

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

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

第一步:前端渲染 Demo

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

<!-- index.html -->
<body>
<div id="app">
 <app></app>
</div>
<script src="./dist/web.js"></script> <!--這是 app.js 打包出來的 JS 文件 -->
</body>
// app.js,也是 webpack 打包入口
import Vue from 'vue';
import App from './App.vue';
var app = new Vue({
 el: '#app',
 components: {
 App
 }
});
// App.vue
<template>
 <div>
 <foo></foo>
 <bar></bar>
 </div>
</template>
<script>
 import Foo from './components/Foo.vue';
 import Bar from './components/Bar.vue';
 export default {
 components:{
  Foo,
  Bar
 }
 }
</script>
// Foo.vue
<template>
 <div class='foo'>
 <h2>Foo</h2>
 <p>Component </p>
 </div>
</template>
<style>
 .foo{
 background: yellow;
 }
</style>
// Bar.vue
<template>
 <div class='bar'>
 <h2>Bar</h2>
 <p>Component </p>
 </div>
</template>
<style>
 .bar{
 background: blue;
 }
</style>

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

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

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

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

  1. 拆分 JS 入口;

  2. 拆分 Webpack 打包配置;

  3. 編寫服務端渲染主體邏輯。

1. 拆分 JS 入口

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

先看 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 在服務端被調(diào)用,因此需要導出為一個函數(shù)。

2. 拆分 Webpack 打包配置

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

關于 webpack.server.conf.js,有兩個注意點:

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

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

3. 編寫服務端渲染主體邏輯

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

// server.js 服務端渲染主體邏輯
// 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('服務器內(nèi)部錯誤');
  return;
 }
 res.end(html);
 })
});

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

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

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

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

這是關鍵的一步,也是最難的一步。

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

  1. 在開始渲染之前,預先獲取所有需要的 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 中,這是不行的,因為服務器并不會執(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
<template>
 <div>
 <h2>App.vue</h2>
 <p>vue with vue </p>
 <hr>
 <foo1 ref="foo_ref"></foo1>
 <bar1 ref="bar_ref"></bar1>
 <bar2 ref="bar_ref2"></bar2>
 </div>
</template>
<script>
 import Foo from './components/Foo.vue';
 import Bar from './components/Bar.vue';

 export default {
 components: {
  foo1: Foo,
  bar1: Bar,
  bar2: Bar
 }
 }
</script>

從根組件 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 的,因為不同路由對應不同的組件,并非每次都把所有組件的 asyncData 都執(zhí)行的。但是有例外,比如我的這個老項目,就只有一個頁面(一個頁面中包含很多的組件),所以根本不需要用到 vue-router,也照樣能做 SSR。主要的區(qū)別就是如何找到那些該被執(zhí)行的 asyncData 方法:官方 Demo 通過 vue-router,而我通過直接解析 components 字段,僅此而已。

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

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

2.2. 當 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 渲染出來了,但是事件的綁定肯定得由瀏覽器來完成啊,如果瀏覽器拿不到跟服務器端同樣的數(shù)據(jù)的話,在觸發(fā)組件的點擊事件的時候,又上哪兒去找 msg 字段呢?

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

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

效果

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

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

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

官方思路的變形

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

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

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

// 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)容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)成都網(wǎng)站設計公司行業(yè)資訊頻道!

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

網(wǎng)頁標題:Vue中SSR如何實現(xiàn)-創(chuàng)新互聯(lián)
網(wǎng)站鏈接:http://jinyejixie.com/article32/decdpc.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供微信小程序、Google、網(wǎng)站排名、品牌網(wǎng)站制作、網(wǎng)站內(nèi)鏈服務器托管

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站
肃北| 巴林左旗| 乌鲁木齐县| 溧水县| 灌阳县| 乃东县| 繁峙县| 东至县| 德化县| 宜丰县| 随州市| 阿拉善右旗| 江津市| 钦州市| 新泰市| 松江区| 彰化市| 云南省| 宁德市| 丹凤县| 海阳市| 旅游| 乐安县| 墨竹工卡县| 苍梧县| 白城市| 平凉市| 新巴尔虎左旗| 东乌| 霍邱县| 炉霍县| 大港区| 通州市| 镇赉县| 瑞金市| 樟树市| 甘南县| 沈阳市| 通州区| 维西| 绿春县|