Fork me on GitHub

vue服务端渲染axios预取数据

首先是要参考vue服务端渲染教程:https://ssr.vuejs.org/zh/data.html。

本文主要代码均参考教程得来。基本原理如下,拷贝的原文教程。

为了解决这个问题,获取的数据需要位于视图组件之外,即放置在专门的数据预取存储容器(data store)或"状态容器(state container))"中。首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。

依据这段话,需要使用vuex;使用vuex的代码如下:

 1 import Vue from 'vue'
 2 import Vuex from 'vuex'
 3 Vue.use(Vuex)
 4 // 假定我们有一个可以返回 Promise 的
 5 // 通用 API(请忽略此 API 具体实现细节)
 6 import { fetchItem } from '../api'
 7 export function createStore () {
 8   return new Vuex.Store({
 9     state: {
10       items: {}
11     },
12     actions: {
13       fetchItem ({ commit }, id) {
14         // `store.dispatch()` 会返回 Promise,
15         // 以便我们能够知道数据在何时更新
16         return fetchItem(id).then(item => {
17           
18           commit('setItem', { id, item })
19         })
20       }
21     },
22     mutations: {
23       setItem (state, { id, item }) {
24         Vue.set(state.items, id, item)
25       }
26     }
27   })
28 }

上面的api中代码:

 1 import api from "create-api"
 2 export function fetchItem(id) {
 3 
 4   return new Promise(function(resolve, reject) {
 5     api.get("http://www.youxuewang.com.cn/shouji/home/LoadProducts", {
 6       pageno: 1,
 7       pagesize: 200,
 8       condstr: '社会大课堂:0'
 9     }).then(function(res) {
10       resolve({ text: JSON.stringify(res)});
11 
12     }).catch(function() {
13       console.log(222222222222222);
14     });
15   })
16 
17   //return Promise.resolve(obj)
18 }
create-api是webpack的别名也就是alias配置,实际是两个文件,服务端文件如下
 1 const isProd = process.env.NODE_ENV === 'production';
 2 
 3 const axios = require('axios');
 4 let host = isProd ? 'http://www.youxuewang.com.cn/shouji/home/LoadProducts' : 'http://www.youxuewang.com.cn/shouji/home/LoadProducts';
 5 let cook = process.__COOKIE__ || '';
 6 let api;
 7 
 8 axios.defaults.baseURL = host;
 9 axios.defaults.timeout = 10000;
10 
11 axios.interceptors.response.use((res) => {
12   if (res.status >= 200 && res.status < 300) {
13     console.log(121212,res.status );
14     return res;
15   }
16   return Promise.reject(res);
17 }, (error) => {
18   // 网络异常
19   return Promise.reject({message: '网络异常,请刷新重试', err: error, type: 1});
20 });
21 
22 if (process.__API__) {
23   api = process.__API__;
24 } else {
25   api = {
26     get: function(target, options = {}) {
27       return new Promise((resolve, reject) => {
28         axios.request({
29           url: target,
30           method: 'get',
31           headers: {
32             'Cookie': cook
33           },
34           params: options
35         }).then(res => {
36           resolve(res.data);
37         }).catch((error) => {
38           reject(error);
39         });
40       });
41     },
42     post: function(target, options = {}) {
43       return new Promise((resolve, reject) => {
44         axios.request({
45           url: target,
46           method: 'post',
47           headers: {
48             'Cookie': cook
49           },
50           params: options
51         }).then(res => {
52           resolve(res.data);
53         }).catch((error) => {
54           reject(error);
55         });
56       });
57     }
58   };
59 }
60 
61 module.exports = api;

客户端用代码如下

 1 const axios = require('axios');
 2 let api;
 3 
 4 axios.defaults.timeout = 10000;
 5 
 6 //拦截器,使用拦截器提前对axios操控,before they are handled by then or catch.
 7 axios.interceptors.response.use((res) => {
 8   if (res.status >= 200 && res.status < 300) {
 9     console.log(22,res.status );
10     return res;
11   }
12   return Promise.reject(res);
13 }, (error) => {
14   // 网络异常
15   return Promise.reject({message: '网络异常,请刷新重试', err: error});
16 });
17 
18 if (process.__API__) {
19   api = process.__API__;
20 } else {
21   api = {
22     get: function(target, params = {}) {
23       const suffix = Object.keys(params).map(name => {
24         return `${name}=${JSON.stringify(params[name])}`;
25       }).join('&');
26       const urls = `${target}?${suffix}`;
27       return new Promise((resolve, reject) => {
28         axios.get(urls, params).then(res => {
29           resolve(res.data);
30         }).catch((error) => {
31           reject(error);
32         });
33       });
34     },
35     post: function(target, options = {}) {
36       return new Promise((resolve, reject) => {
37         axios.post(target, options).then(res => {
38           resolve(res.data);
39         }).catch((error) => {
40           reject(error);
41         });
42       });
43     }
44   };
45 }
46 
47 module.exports = api;

那么,我们在哪里放置「dispatch 数据预取 action」的代码?

我们需要通过访问路由,来决定获取哪部分数据 - 这也决定了哪些组件需要渲染。事实上,给定路由所需的数据,也是在该路由上渲染组件时所需的数据。所以在路由组件中放置数据预取逻辑,是很自然的事情

我们将在路由组件上暴露出一个自定义静态函数 asyncData。注意,由于此函数会在组件实例化之前调用,所以它无法访问 this。需要将 store 和路由信息作为参数传递进去。

上面这句话,诞生了服务端渲染数据的路由组件有一个asyncData方法,代码如下

 1 <template>
 2   <div>{{ item.text }}---{{fooCount}}</div>
 3 </template>
 4 <script>
 5 // 在这里导入模块,而不是在 `store/index.js` 中
 6 import fooStoreModule from '../store/modules/foo'
 7 export default {
 8   asyncData ({ store,route}) {
 9     store.registerModule('foo', fooStoreModule)
10     //return store.dispatch('foo/inc')
11     return Promise.all([
12         store.dispatch("fetchItem",route.params.id),
13         store.dispatch('foo/inc')
14       ])
15   },
16   // 重要信息:当多次访问路由时,
17   // 避免在客户端重复注册模块。
18   destroyed () {
19     this.$store.unregisterModule('foo')
20   },
21   computed: {
22     fooCount () {
23       return this.$store.state.foo.count
24     },item () {
25       return this.$store.state.items[this.$route.params.id]
26     }
27   }
28 }
29 </script> 

主要代码介绍如上,完成代码github链接:https://github.com/zhensg123/vue-ssr-axios。

预取数据演示访问地址:http://localhost:8080/item/22;

本文结束。

posted @ 2018-05-09 09:35  我站在山顶上  阅读(4336)  评论(0编辑  收藏  举报