温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Egg Vue SSR服务端渲染数据请求与asyncData的示例分析

发布时间:2021-09-06 13:57:55 来源:亿速云 阅读:210 作者:小新 栏目:web开发

小编给大家分享一下Egg Vue SSR服务端渲染数据请求与asyncData的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

服务端渲染 Node 层直接获取数据

在 Egg 项目如果使用模板引擎规范时通是过 render 方法进行模板渲染,render 的第一个参数模板路径,第二个参数时模板渲染数据. 如如下调用方式:

async index(ctx) {   // 获取数据,可以是从数据库,后端 Http 接口 等形式   const list = ctx.service.article.getArtilceList();   // 对模板进行渲染,这里的 index.js 是 vue 文件通过 Webpack 构建的 JSBundle 文件   await ctx.render('index.js', { list }); }

从上面的例子可以看出,这种使用方式是非常典型的也容易理解的模板渲染方式。在实际业务开发时,对于常规的页面渲染也建议使用这种方式获取数据没,然后进行页面渲染。Node 获取数据后,在 Vue 的根 Vue 文件里面就可以通过 this.list 的方式拿到 Node 获取的数据,然后就可以进行 vue 模板文件数据绑定了。

在这里有个高阶用法,可以直接把 ctx 等 Node 对象传递到 第二个参数里面,  这个时候你在模板里面就直接拿到 ctx 这些对象。 但这个时候就需要自己处理好 SSR 渲染时导致的 hydrate 问题,因为前端hydrate时并没有 ctx 对象。 

async index(ctx) {   // 获取数据,可以是从数据库,后端 Http 接口 等形式   const list = ctx.service.article.getArtilceList();   // 对模板进行渲染,这里的 index.js 是 vue 文件通过 Webpack 构建的 JSBundle 文件   await ctx.render('index.js', { ctx, list }); }

服务端渲染 asyncData 方式获取数据

在 Vue 单页面 SSR 时涉及数据的请求方式,Node 层获取数据方式可以继续使用,但当路由切换时(页面直接刷新),Node 层就需要根据路由获取不同页面的数据,同时还要考虑前端路由切换的情况,这个时候路由是不会走 Node 层路由,而是直接进行的前端路由,这个时候也要考虑数据的请求方式。

基于以上使用的优雅问题,这里提供一种 asyncData 获取数据的方式解决单页面 SSR 刷新不走 SSR 问题。 Node 不直接获取数据,获取数据的代码直接写到前端代码里面。这里需要解决如下两个问题:

前端路由匹配 asyncData 调用

这里根据路由切换 url 获取指定的路由 componet 组件,然后检查是否有 aysncData,如果有就进行调用。调用之后,数据会放到 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 => {        // 关键代码        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 进行数据管理,把数据统一放到 store 里面,前端通过 this.$store.state 方式可以获取数据,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 数据统一调用

在服务端 asyncData 调用时,可以解决单页面 SSR 刷新问题,那直接在前端切换路由时因不走服务端路由,那数据如何处理?

在 Vue 单页面实现时,通常都会使用 Vue-Router,这个时候可以借助 Vue-Router 提供 afterEach 钩子进行统一数据请求,可以直接调用 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);   }  } });

最后贴上可以用的完整代码,请根据实际需要进行修改, 实际可运行例子见 https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/feature/green/spa

Vue 页面初始化统一封装

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')    }   ]  }); }

看完了这篇文章,相信你对“Egg Vue SSR服务端渲染数据请求与asyncData的示例分析”有了一定的了解,如果想了解更多相关知识,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI