Egg Vue SSR 服務(wù)端渲染數(shù)據(jù)請(qǐng)求與asyncData
服務(wù)端渲染 Node 層直接獲取數(shù)據(jù)
在 Egg 項(xiàng)目如果使用模板引擎規(guī)范時(shí)通是過(guò) render 方法進(jìn)行模板渲染,render 的第一個(gè)參數(shù)模板路徑,第二個(gè)參數(shù)時(shí)模板渲染數(shù)據(jù). 如如下調(diào)用方式:
async index(ctx) { // 獲取數(shù)據(jù),可以是從數(shù)據(jù)庫(kù),后端 Http 接口 等形式 const list = ctx.service.article.getArtilceList(); // 對(duì)模板進(jìn)行渲染,這里的 index.js 是 vue 文件通過(guò) Webpack 構(gòu)建的 JSBundle 文件 await ctx.render('index.js', { list }); }
從上面的例子可以看出,這種使用方式是非常典型的也容易理解的模板渲染方式。在實(shí)際業(yè)務(wù)開(kāi)發(fā)時(shí),對(duì)于常規(guī)的頁(yè)面渲染也建議使用這種方式獲取數(shù)據(jù)沒(méi),然后進(jìn)行頁(yè)面渲染。Node 獲取數(shù)據(jù)后,在 Vue 的根 Vue 文件里面就可以通過(guò) this.list 的方式拿到 Node 獲取的數(shù)據(jù),然后就可以進(jìn)行 vue 模板文件數(shù)據(jù)綁定了。
在這里有個(gè)高階用法,可以直接把 ctx 等 Node 對(duì)象傳遞到 第二個(gè)參數(shù)里面, 這個(gè)時(shí)候你在模板里面就直接拿到 ctx 這些對(duì)象。 但這個(gè)時(shí)候就需要自己處理好 SSR 渲染時(shí)導(dǎo)致的 hydrate 問(wèn)題,因?yàn)榍岸薶ydrate時(shí)并沒(méi)有 ctx 對(duì)象。
async index(ctx) { // 獲取數(shù)據(jù),可以是從數(shù)據(jù)庫(kù),后端 Http 接口 等形式 const list = ctx.service.article.getArtilceList(); // 對(duì)模板進(jìn)行渲染,這里的 index.js 是 vue 文件通過(guò) Webpack 構(gòu)建的 JSBundle 文件 await ctx.render('index.js', { ctx, list }); }
服務(wù)端渲染 asyncData 方式獲取數(shù)據(jù)
在 Vue 單頁(yè)面 SSR 時(shí)涉及數(shù)據(jù)的請(qǐng)求方式,Node 層獲取數(shù)據(jù)方式可以繼續(xù)使用,但當(dāng)路由切換時(shí)(頁(yè)面直接刷新),Node 層就需要根據(jù)路由獲取不同頁(yè)面的數(shù)據(jù),同時(shí)還要考慮前端路由切換的情況,這個(gè)時(shí)候路由是不會(huì)走 Node 層路由,而是直接進(jìn)行的前端路由,這個(gè)時(shí)候也要考慮數(shù)據(jù)的請(qǐng)求方式。
基于以上使用的優(yōu)雅問(wèn)題,這里提供一種 asyncData 獲取數(shù)據(jù)的方式解決單頁(yè)面 SSR 刷新不走 SSR 問(wèn)題。 Node 不直接獲取數(shù)據(jù),獲取數(shù)據(jù)的代碼直接寫(xiě)到前端代碼里面。這里需要解決如下兩個(gè)問(wèn)題:
前端路由匹配 asyncData 調(diào)用
這里根據(jù)路由切換 url 獲取指定的路由 componet 組件,然后檢查是否有 aysncData,如果有就進(jìn)行調(diào)用。調(diào)用之后,數(shù)據(jù)會(huì)放到 Vuex 的 store 里面。
return new Promise((resolve, reject) => { router.onReady(() => { // url 為當(dāng)前請(qǐng)求路由,可以通過(guò)服務(wù)端傳遞到前端頁(yè)面 const matchedComponents = router.getMatchedComponents(url); if (!matchedComponents) { return reject({ code: '404' }); } return Promise.all( matchedComponents.map(component => { // 關(guān)鍵代碼 if (component.methods && component.methods.asyncData) { return component.methods.asyncData(store); } return null; }) ).then(() => { context.state = { ...store.state, ...context.state }; return resolve(new Vue(options)); }); }); });
Vue 模板定義 asyncData 方法
前端通過(guò) Vuex 進(jìn)行數(shù)據(jù)管理,把數(shù)據(jù)統(tǒng)一放到 store 里面,前端通過(guò) this.$store.state 方式可以獲取數(shù)據(jù),Node 和 前端都可以獲取到。
<script type="text/babel"> export default{ computed: { isLoading(){ return false; }, articleList() { return this.$store.state.articleList; } }, methods: { asyncData ({ state, dispatch, commit }) { return dispatch('FETCH_ARTICLE_LIST') } } } </script>
前端 asyncData 數(shù)據(jù)統(tǒng)一調(diào)用
在服務(wù)端 asyncData 調(diào)用時(shí),可以解決單頁(yè)面 SSR 刷新問(wèn)題,那直接在前端切換路由時(shí)因不走服務(wù)端路由,那數(shù)據(jù)如何處理?
在 Vue 單頁(yè)面實(shí)現(xiàn)時(shí),通常都會(huì)使用 Vue-Router,這個(gè)時(shí)候可以借助 Vue-Router 提供 afterEach 鉤子進(jìn)行統(tǒng)一數(shù)據(jù)請(qǐng)求,可以直接調(diào)用 Vue 模板定義的 asyncData 方法。代碼如下:
const options = this.create(window.__INITIAL_STATE__); const { router, store } = options; router.beforeEach((route, redirec, next) => { next(); }); router.afterEach((route, redirec) => { if (route.matched && route.matched.length) { const asyncData = route.matched[0].components.default.asyncData; if (asyncData) { asyncData(store); } } });
最后貼上可以用的完整代碼,請(qǐng)根據(jù)實(shí)際需要進(jìn)行修改, 實(shí)際可運(yùn)行例子見(jiàn) https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/feature/green/spa
Vue 頁(yè)面初始化統(tǒng)一封裝
import Vue from 'vue'; import { sync } from 'vuex-router-sync'; import './vue/filter'; import './vue/directive'; export default class App { constructor(config) { this.config = config; } bootstrap() { if (EASY_ENV_IS_NODE) { return this.server(); } return this.client(); } create(initState) { const { index, options, createStore, createRouter } = this.config; const store = createStore(initState); const router = createRouter(); sync(store, router); return { ...index, ...options, router, store }; } client() { Vue.prototype.$http = require('axios'); const options = this.create(window.__INITIAL_STATE__); const { router, store } = options; router.beforeEach((route, redirec, next) => { next(); }); router.afterEach((route, redirec) => { console.log('>>afterEach', route); if (route.matched && route.matched.length) { const asyncData = route.matched[0].components.default.asyncData; if (asyncData) { asyncData(store); } } }); const app = new Vue(options); const root = document.getElementById('app'); const hydrate = root.childNodes.length > 0; app.$mount('#app', hydrate); return app; } server() { return context => { const options = this.create(context.state); const { store, router } = options; router.push(context.state.url); return new Promise((resolve, reject) => { router.onReady(() => { const matchedComponents = router.getMatchedComponents(); if (!matchedComponents) { return reject({ code: '404' }); } return Promise.all( matchedComponents.map(component => { if (component.asyncData) { return component.asyncData(store); } return null; }) ).then(() => { context.state = { ...store.state, ...context.state }; return resolve(new Vue(options)); }); }); }); }; } }
頁(yè)面入口代碼
// index.js 'use strict'; import App from 'framework/app.js'; import index from './index.vue'; import createStore from './store'; import createRouter from './router'; const options = { base: '/' }; export default new App({ index, options, createStore, createRouter, }).bootstrap();
前端 router / store 定義
// store/index.js 'use strict'; import Vue from 'vue'; import Vuex from 'vuex'; import actions from './actions'; import getters from './getters'; import mutations from './mutations'; Vue.use(Vuex); export default function createStore(initState = {}) { const state = { articleList: [], article: {}, ...initState }; return new Vuex.Store({ state, actions, getters, mutations }); } // router/index.js import Vue from 'vue'; import VueRouter from 'vue-router'; import ListView from './list'; Vue.use(VueRouter); export default function createRouter() { return new VueRouter({ mode: 'history', base: '/', routes: [ { path: '/', component: ListView }, { path: '/list', component: ListView }, { path: '/detail/:id', component: () => import('./detail') } ] }); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 15分鐘學(xué)會(huì)vue項(xiàng)目改造成SSR(小白教程)
- vue ssr+koa2構(gòu)建服務(wù)端渲染的示例代碼
- Vue SSR 即時(shí)編譯技術(shù)的實(shí)現(xiàn)
- Vue使用預(yù)渲染代替SSR的方法
- 如何構(gòu)建 vue-ssr 項(xiàng)目的方法步驟
- vuecli項(xiàng)目構(gòu)建SSR服務(wù)端渲染的實(shí)現(xiàn)
- vue的ssr服務(wù)端渲染示例詳解
- vue中vue-router的使用說(shuō)明(包括在ssr中的使用)
- 關(guān)于VueSSR的一些理解和詳細(xì)配置
- Vue.js?狀態(tài)管理及?SSR解析
相關(guān)文章
原生js實(shí)現(xiàn)的觀(guān)察者和訂閱者模式簡(jiǎn)單示例
這篇文章主要介紹了原生js實(shí)現(xiàn)的觀(guān)察者和訂閱者模式,結(jié)合簡(jiǎn)單實(shí)例形式分析了js觀(guān)察者和訂閱者模式的相關(guān)原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下2020-04-04微信公眾號(hào)平臺(tái)接口開(kāi)發(fā) 獲取微信服務(wù)器IP地址方法解析
這篇文章主要介紹了微信公眾號(hào)平臺(tái)接口開(kāi)發(fā) 獲取微信服務(wù)器IP地址方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08用Node.js通過(guò)sitemap.xml批量抓取美女圖片
這篇文章主要介紹了用Node.js通過(guò)sitemap.xml批量抓取美女圖片的方法和相關(guān)代碼,有需要的小伙伴可以參考下。2015-05-05js文件中調(diào)用js的實(shí)現(xiàn)方法小結(jié)
JavaScript文件引用JavaScript文件的方法,需要的朋友可以參考下。2009-10-10JS中隊(duì)列和雙端隊(duì)列實(shí)現(xiàn)及應(yīng)用詳解
這篇文章主要介紹了JS中隊(duì)列和雙端隊(duì)列實(shí)現(xiàn)及應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09利用google提供的API(JavaScript接口)獲取網(wǎng)站訪(fǎng)問(wèn)者IP地理位置的代碼詳解
利用google提供的API(JavaScript接口)獲取網(wǎng)站訪(fǎng)問(wèn)者IP地理位置2010-07-07JS拖動(dòng)鼠標(biāo)畫(huà)出方框?qū)崿F(xiàn)鼠標(biāo)選區(qū)的方法
這篇文章主要介紹了JS拖動(dòng)鼠標(biāo)畫(huà)出方框?qū)崿F(xiàn)鼠標(biāo)選區(qū)的方法,涉及javascript鼠標(biāo)事件及頁(yè)面元素樣式的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08JS短路原理的應(yīng)用示例 精簡(jiǎn)代碼的途徑
正如標(biāo)題所言,js中||和&&的特性幫我們精簡(jiǎn)了代碼的同時(shí),也帶來(lái)了代碼可讀性的降低。這就需要我們自己來(lái)權(quán)衡了,下面有個(gè)不錯(cuò)的示例2013-12-12js中 計(jì)算兩個(gè)日期間的工作日的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇js中 計(jì)算兩個(gè)日期間的工作日的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08