Egg Vue SSR 服務端渲染數(shù)據(jù)請求與asyncData
服務端渲染 Node 層直接獲取數(shù)據(jù)
在 Egg 項目如果使用模板引擎規(guī)范時通是過 render 方法進行模板渲染,render 的第一個參數(shù)模板路徑,第二個參數(shù)時模板渲染數(shù)據(jù). 如如下調(diào)用方式:
async index(ctx) {
// 獲取數(shù)據(jù),可以是從數(shù)據(jù)庫,后端 Http 接口 等形式
const list = ctx.service.article.getArtilceList();
// 對模板進行渲染,這里的 index.js 是 vue 文件通過 Webpack 構(gòu)建的 JSBundle 文件
await ctx.render('index.js', { list });
}
從上面的例子可以看出,這種使用方式是非常典型的也容易理解的模板渲染方式。在實際業(yè)務開發(fā)時,對于常規(guī)的頁面渲染也建議使用這種方式獲取數(shù)據(jù)沒,然后進行頁面渲染。Node 獲取數(shù)據(jù)后,在 Vue 的根 Vue 文件里面就可以通過 this.list 的方式拿到 Node 獲取的數(shù)據(jù),然后就可以進行 vue 模板文件數(shù)據(jù)綁定了。
在這里有個高階用法,可以直接把 ctx 等 Node 對象傳遞到 第二個參數(shù)里面, 這個時候你在模板里面就直接拿到 ctx 這些對象。 但這個時候就需要自己處理好 SSR 渲染時導致的 hydrate 問題,因為前端hydrate時并沒有 ctx 對象。
async index(ctx) {
// 獲取數(shù)據(jù),可以是從數(shù)據(jù)庫,后端 Http 接口 等形式
const list = ctx.service.article.getArtilceList();
// 對模板進行渲染,這里的 index.js 是 vue 文件通過 Webpack 構(gòu)建的 JSBundle 文件
await ctx.render('index.js', { ctx, list });
}
服務端渲染 asyncData 方式獲取數(shù)據(jù)
在 Vue 單頁面 SSR 時涉及數(shù)據(jù)的請求方式,Node 層獲取數(shù)據(jù)方式可以繼續(xù)使用,但當路由切換時(頁面直接刷新),Node 層就需要根據(jù)路由獲取不同頁面的數(shù)據(jù),同時還要考慮前端路由切換的情況,這個時候路由是不會走 Node 層路由,而是直接進行的前端路由,這個時候也要考慮數(shù)據(jù)的請求方式。
基于以上使用的優(yōu)雅問題,這里提供一種 asyncData 獲取數(shù)據(jù)的方式解決單頁面 SSR 刷新不走 SSR 問題。 Node 不直接獲取數(shù)據(jù),獲取數(shù)據(jù)的代碼直接寫到前端代碼里面。這里需要解決如下兩個問題:
前端路由匹配 asyncData 調(diào)用
這里根據(jù)路由切換 url 獲取指定的路由 componet 組件,然后檢查是否有 aysncData,如果有就進行調(diào)用。調(diào)用之后,數(shù)據(jù)會放到 Vuex 的 store 里面。
return new Promise((resolve, reject) => {
router.onReady(() => {
// url 為當前請求路由,可以通過服務端傳遞到前端頁面
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 方法
前端通過 Vuex 進行數(shù)據(jù)管理,把數(shù)據(jù)統(tǒng)一放到 store 里面,前端通過 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)用
在服務端 asyncData 調(diào)用時,可以解決單頁面 SSR 刷新問題,那直接在前端切換路由時因不走服務端路由,那數(shù)據(jù)如何處理?
在 Vue 單頁面實現(xiàn)時,通常都會使用 Vue-Router,這個時候可以借助 Vue-Router 提供 afterEach 鉤子進行統(tǒng)一數(shù)據(jù)請求,可以直接調(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);
}
}
});
最后貼上可以用的完整代碼,請根據(jù)實際需要進行修改, 實際可運行例子見 https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/feature/green/spa
Vue 頁面初始化統(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));
});
});
});
};
}
}
頁面入口代碼
// 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')
}
]
});
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
微信公眾號平臺接口開發(fā) 獲取微信服務器IP地址方法解析
這篇文章主要介紹了微信公眾號平臺接口開發(fā) 獲取微信服務器IP地址方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-08-08
js文件中調(diào)用js的實現(xiàn)方法小結(jié)
JavaScript文件引用JavaScript文件的方法,需要的朋友可以參考下。2009-10-10
利用google提供的API(JavaScript接口)獲取網(wǎng)站訪問者IP地理位置的代碼詳解
利用google提供的API(JavaScript接口)獲取網(wǎng)站訪問者IP地理位置2010-07-07
JS拖動鼠標畫出方框?qū)崿F(xiàn)鼠標選區(qū)的方法
這篇文章主要介紹了JS拖動鼠標畫出方框?qū)崿F(xiàn)鼠標選區(qū)的方法,涉及javascript鼠標事件及頁面元素樣式的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08

