vue ssr实战2(增加vuex)

Posted by Start Bootstrap on March 08, 2018
  1. 准备工作
npm i vuex -S
  1. 创建store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  topics: [],
  count: 0
}

const getters = {
  getCount(state) {
    return state.count
  }
}

const actions = {
  increment({
    commit,
    state
  }) {
    setTimeout(() => {
      commit('INCREMENT', 1)
    }, 1000)
  },
  decrement({
    commit,
    state
  }) {
    setTimeout(() => {
      commit('DECREMENT', 1)
    }, 1000)
  }
}

const mutations = {
  TOPICS_LIST: (state, topics) => {
    state.topics = topics
  },

  INCREMENT: (state) => {
    state.count++
  },

  DECREMENT: (state) => {
    state.count--
  }
}

export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})
  1. 修改main.js,增加vuex功能
import Vue from 'vue'
import App from './App'
import store from './store'
import {
  createRouter
} from './router'
import {
  sync
} from 'vuex-router-sync' // 取得route状态

export function createApp() {
  // 创建 router 实例
  const router = createRouter()
  sync(store, router)
  const app = new Vue({
    // 注入 router 到根 Vue 实例
    router,
    store,
    render: h => h(App)
  })
  // 返回 app 和 router
  return {
    app,
    router,
    store
  }
}
  1. 新建一个count路由,使用getter中的方法
<template>
  <div>
    <button @click='increment'>Increment +1</button>
    <button @click='decrement'>Decrement -1</button>
    <h3>Count is </h3>

    <router-link to="/">Home</router-link>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'
export default {
  data() {
    return {
      res: [1, 2, 3, 4]
    }
  },
  methods: {
    ...mapActions(['increment', 'decrement']),
    asy: function() {
      return new Promise((resolve, reject) => {
        setTimeout(n => {
          console.log(new Date())
          resolve()
        }, 5000)
      })
    },
    fasdf: async function() {
      await this.asy()
    }
  },
  computed: {
    ...mapGetters(['getCount'])
  },
  created() {
    this.fasdf()
    if (this.res.includes(1)) {
      console.log(123)
    }
  }
}
</script>
<style>
body {
}
</style>
  1. 修改entry-client.js,增加vuex功能以及asyncData asyncData的作用就是统一客户端与服务器端的数据获取过程
import {
  createApp
} from './main'
const {
  app,
  router,
  store
} = createApp()
// 统一客户端与服务器端的store
if (window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
}
// 因为可能存在异步组件,所以等待router将所有异步组件加载完毕,服务器端配置也需要此操作
router.onReady(() => {
  router.beforeResolve((to, from, next) => {
    const matched = router.getMatchedComponents(to)
    const prevMatched = router.getMatchedComponents(from)
    let diffed = false
    const activated = matched.filter((c, i) => {
      return diffed || (diffed = (prevMatched[i] !== c))
    })
    if (!activated.length) {
      return next()
    }
    // start loading indicator
    Promise.all(activated.map(c => {
      if (c.asyncData) {
        return c.asyncData({
          store,
          route: to
        })
      }
    })).then(() => {
      // stop loading indicator
      next()
    }).catch(next)
  })
  app.$mount('#app')
})

  1. 同样修改entry-server.js

// entry-server.js
import { createApp } from './main'
export default context => {
  // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
  // 以便服务器能够等待所有的内容在渲染前,
  // 就已经准备就绪。
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp()
    // 设置服务器端 router 的位置
    router.push(context.url)
    // 等到 router 将可能的异步组件和钩子函数解析完
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      // 匹配不到的路由,执行 reject 函数,并返回 404
      if (!matchedComponents.length) {
        // eslint-disable-next-line
        return reject({ code: 404 })
      }
      // Promise 应该 resolve 应用程序实例,以便它可以渲染
      Promise.all(matchedComponents.map(Component => {
        if (Component.asyncData) {
          return Component.asyncData({
            store,
            route: router.currentRoute
          })
        }
      })).then(() => {
        context.state = store.state
        resolve(app)
      }).catch(reject)
    }, reject)
  })
}

  1. 运行命令,大功告成 image.png