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

 

posted @ 2019-11-25 13:39  Jason齐齐  阅读(417)  评论(0编辑  收藏  举报