首先是要参考vue服务端渲染教程:https://ssr.vuejs.org/zh/data.html。
本文主要代码均参考教程得来。基本原理如下,拷贝的原文教程。
为了解决这个问题,获取的数据需要位于视图组件之外,即放置在专门的数据预取存储容器(data store)或"状态容器(state container))"中。首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。
依据这段话,需要使用vuex;使用vuex的代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 假定我们有一个可以返回 Promise 的
// 通用 API(请忽略此 API 具体实现细节)
import { fetchItem } from '../api'
export function createStore () {
return new Vuex.Store({
state: {
items: {}
},
actions: {
fetchItem ({ commit }, id) {
// `store.dispatch()` 会返回 Promise,
// 以便我们能够知道数据在何时更新
return fetchItem(id).then(item => { commit('setItem', { id, item })
})
}
},
mutations: {
setItem (state, { id, item }) {
Vue.set(state.items, id, item)
}
}
})
}
上面的api中代码:
import api from "create-api"
export function fetchItem(id) { return new Promise(function(resolve, reject) {
api.get("http://www.youxuewang.com.cn/shouji/home/LoadProducts", {
pageno: ,
pagesize: ,
condstr: '社会大课堂:0'
}).then(function(res) {
resolve({ text: JSON.stringify(res)}); }).catch(function() {
console.log();
});
}) //return Promise.resolve(obj)
}
create-api是webpack的别名也就是alias配置,实际是两个文件,服务端文件如下
const isProd = process.env.NODE_ENV === 'production'; const axios = require('axios');
let host = isProd ? 'http://www.youxuewang.com.cn/shouji/home/LoadProducts' : 'http://www.youxuewang.com.cn/shouji/home/LoadProducts';
let cook = process.__COOKIE__ || '';
let api; axios.defaults.baseURL = host;
axios.defaults.timeout = ; axios.interceptors.response.use((res) => {
if (res.status >= && res.status < ) {
console.log(,res.status );
return res;
}
return Promise.reject(res);
}, (error) => {
// 网络异常
return Promise.reject({message: '网络异常,请刷新重试', err: error, type: });
}); if (process.__API__) {
api = process.__API__;
} else {
api = {
get: function(target, options = {}) {
return new Promise((resolve, reject) => {
axios.request({
url: target,
method: 'get',
headers: {
'Cookie': cook
},
params: options
}).then(res => {
resolve(res.data);
}).catch((error) => {
reject(error);
});
});
},
post: function(target, options = {}) {
return new Promise((resolve, reject) => {
axios.request({
url: target,
method: 'post',
headers: {
'Cookie': cook
},
params: options
}).then(res => {
resolve(res.data);
}).catch((error) => {
reject(error);
});
});
}
};
} module.exports = api;
客户端用代码如下
const axios = require('axios');
let api; axios.defaults.timeout = ; //拦截器,使用拦截器提前对axios操控,before they are handled by then or catch.
axios.interceptors.response.use((res) => {
if (res.status >= && res.status < ) {
console.log(,res.status );
return res;
}
return Promise.reject(res);
}, (error) => {
// 网络异常
return Promise.reject({message: '网络异常,请刷新重试', err: error});
}); if (process.__API__) {
api = process.__API__;
} else {
api = {
get: function(target, params = {}) {
const suffix = Object.keys(params).map(name => {
return `${name}=${JSON.stringify(params[name])}`;
}).join('&');
const urls = `${target}?${suffix}`;
return new Promise((resolve, reject) => {
axios.get(urls, params).then(res => {
resolve(res.data);
}).catch((error) => {
reject(error);
});
});
},
post: function(target, options = {}) {
return new Promise((resolve, reject) => {
axios.post(target, options).then(res => {
resolve(res.data);
}).catch((error) => {
reject(error);
});
});
}
};
} module.exports = api;
那么,我们在哪里放置「dispatch 数据预取 action」的代码?
我们需要通过访问路由,来决定获取哪部分数据 - 这也决定了哪些组件需要渲染。事实上,给定路由所需的数据,也是在该路由上渲染组件时所需的数据。所以在路由组件中放置数据预取逻辑,是很自然的事情。
我们将在路由组件上暴露出一个自定义静态函数 asyncData
。注意,由于此函数会在组件实例化之前调用,所以它无法访问 this
。需要将 store 和路由信息作为参数传递进去。
上面这句话,诞生了服务端渲染数据的路由组件有一个asyncData
方法,代码如下
<template>
<div>{{ item.text }}---{{fooCount}}</div>
</template>
<script>
// 在这里导入模块,而不是在 `store/index.js` 中
import fooStoreModule from '../store/modules/foo'
export default {
asyncData ({ store,route}) {
store.registerModule('foo', fooStoreModule)
//return store.dispatch('foo/inc')
return Promise.all([
store.dispatch("fetchItem",route.params.id),
store.dispatch('foo/inc')
])
},
// 重要信息:当多次访问路由时,
// 避免在客户端重复注册模块。
destroyed () {
this.$store.unregisterModule('foo')
},
computed: {
fooCount () {
return this.$store.state.foo.count
},item () {
return this.$store.state.items[this.$route.params.id]
}
}
}
</script>
主要代码介绍如上,完成代码github链接:https://github.com/mstzhen/vue-ssr-axios。
预取数据演示访问地址:http://localhost:8080/item/22;
本文结束。