亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Egg Vue SSR 服務(wù)端渲染數(shù)據(jù)請(qǐng)求與asyncData

 更新時(shí)間:2019年11月24日 09:36:02   作者:hubcarl  
這篇文章主要介紹了Egg Vue SSR 服務(wù)端渲染數(shù)據(jù)請(qǐng)求與asyncData,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

服務(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í)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論