vue-ssr的实现原理连载(五):vue-ssr中的vuex
上一节 vue-ssr的实现原理连载(五):vue-ssr中的路由 中实现了vue-ssr的路由,这一节来动手实现一下vue-ssr中的store
本节的源代码可以在我的github上查看:
首先思路是和路由的一样: 创建一个store,并导出一个方法,每次调用这个方法会返回一个store实例,在main.js中创建这个实例并在Vue实例生成的时候进行注册,然后同router一同导出。
熟悉nuxt的同学知道nuxt比vue多了一部分服务端的生命周期,比如请求数据我们可以使用 fetch 或者 asyncData ,这里我们实现一下 asyncData;
首先在src下新建store.js,导出一个函数,这个函数每次调用的时候都创建一个store实例:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default () => {
const store = new Vuex.Store({
state: {
username: ""
},
getters: {
username: state => state.username
},
mutations: {
SET_USER_NAME(state, name) {
state.username = name;
}
},
actions: {
setUserName({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit("SET_USER_NAME", "Jason");
resolve();
}, 1000);
});
}
}
});
return store;
};
和路由相似,在src/main.js中引入这个store创建函数,并在Vue实例创建的时候注入
import Vue from "vue";
import App from "./App";
import createRouter from "./router";
import createStore from "./store";
export default () => {
const router = createRouter();
const store = createStore();
const app = new Vue({
router,
store,
render: h => h(App)
});
return { app, router, store };
};
在Foo页面添加上dispatch函数
<template>
<div>
<p>Foo页面: {{username}}</p>
<button @click="handleClick">点击事件</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
return {};
},
computed: {
...mapGetters([
'username'
])
},
mounted() {
this.$store.dispatch('setUserName', 'Jason')
},
methods: {
handleClick() {
alert('点击成功')
}
}
};
</script>
然后打包客户端和服务端,然后启动node服务,打开浏览器,一切正常,但是右键查看源代码,你发现页面上并没有vuex的数据。熟悉nuxt的同学知道nuxt在页面window上挂载了一个对象,这个对象包含着首页渲染需要的数据,服务端正是从这个对象上拿到数据并渲染在页面上的。下面我来看下实现的方法;
首先,nuxt提供了一个asyncData的方法,我们先在Foo页面上添加这样一个方法:
asyncData(store) {
return store.dispatch('setUserName')
},
然后在server.entry.js中,当我们使用 router.getMatchedComponents() 拿到所有匹配的组件的时候,可以判断下每个组件中是否包含 asyncDate 这个方法,如果包含了就依次去执行。同时由于getMatchedComponents返回的是一组组件,而每个 asyncData 又是一个异步的方法,所以我们需要使用 Promise.all() 来等待所有的组件中的所有的异步asyncData都执行完毕然后再返回给客户端。
src/server.entry.js:
Promise.all(
matchs.map(component => {
if (component.asyncData) {
return component.asyncData(store);
}
})
).then(res => {
resolve(app);
});
刷新页面查看源码,可以看到页面确实渲染出了store中的数据, nuxt 中 window.__INITIAL_STATE__对象是如何生成的呢,其实很简单,我们在返回客户端之前,在context上挂载一个state属性即 store.state:
... context.state = store.state; resolve(app); ....
然后查看网页源代码:

页面上已经出现了 window.__INITIAL_STATE__对象,但是页面为什么没有显示出来store.state呢,因为store在现在服务端执行,然后渲染了页面,客户端加载页面以后,store重新执行,然后被置空了,所以我们需要在store被返回前判断环境,如果是客户端环境并且store.state被填充了之后,就使用vuex的api store.replaceState() 去替换store中的state:
if (typeof window !== "undefined" && window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__);
}
保存文件,刷新浏览器,页面正常显示了。到此,vue-ssr的store也就已经完成了。
本节源码可以查看我的github: https://github.com/Jasonwang911/vue-ssr/tree/master/step5
vue-ssr的实现原理连载(一): https://www.cnblogs.com/jasonwang2y60/p/11299503.html 源码: https://github.com/Jasonwang911/vue-ssr/tree/master/step1
vue-ssr的实现原理连载(二):https://www.cnblogs.com/jasonwang2y60/p/11300255.html 源码: https://github.com/Jasonwang911/vue-ssr/tree/master/step2
vue-ssr的实现原理连载(三):https://www.cnblogs.com/jasonwang2y60/p/11300255.html 源码: https://github.com/Jasonwang911/vue-ssr/tree/master/step3
vue-ssr的实现原理连载(四):https://www.cnblogs.com/jasonwang2y60/p/11386463.html 源码: https://github.com/Jasonwang911/vue-ssr/tree/master/step4

浙公网安备 33010602011771号