vuex-ssr

server-side-render

项目当中页面开发使用的是服务端直出的页面,为了可读性以及后期维护使用了 vue 框架

直出页面相比于异步页面的优点

  • 首屏渲染的体验较好
  • 服务端统一进行页面的拼接和渲染,统一做测速和数据上报
  • 多平台和设备上也能有较好的体验,降低浏览器的压力

核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 第 1 步:创建一个 Vue 实例
const Vue = require('vue')
const app = new Vue({
template: `<div>Hello World</div>`
})

// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()

// 第 3 步:将 Vue 实例渲染为 HTML
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(html)
// => <div data-server-rendered="true">Hello World</div>
})

// 在 2.5.0+,如果没有传入回调函数,则会返回 Promise:
renderer.renderToString(app).then(html => {
console.log(html)
}).catch(err => {
console.error(err)
})

注意这里重点是通过一个异步操作renderToString得到一个异步页面,这样子服务端就可以直接输出这个异步页面

vuex

store.js文件夹当中设定该页面相关的state, commit, action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export function createStore () {
return new Vuex.Store({
state: {
},

getters: {

},

mutations: {
},

actions: {
}
})
}

state

统一进行状态管理,包含初始数据和各类判断页面是否渲染的 flag 值

在.vue 文件当中通过以下方式进行绑定,可以实现双向绑定,关联modelview

1
2
3
computed: Vuex.mapState([

]),

mutation

state的所有修改都放在一个mutation当中,使用store.commit('mutation', payload)进行状态的更新

1
2
3
methods: {
...Vuex.mapMutations(['name'])
}

注意所有的mutation都是同步操作

action

action当中封装了一组mutation,并且外层返回一个promise,可以进行异步操作

1
2
3
methods: {
...Vuex.mapActions(['name'])
}

可以在action当中手动返回一个promise进行错误的传递处理,通过 store.dispatch('name', payload).then().catch() 进行异步操作和错误处理

代码结构

  • app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Vue from 'vue';
import Vuex from 'vuex';

import {
createStore
} from './store';
import App from './App.vue';

Vue.use(Vuex);
export const store = createStore();
export function createApp() {

const app = new Vue({
store,
render: h => h(App)
});

return {
app,
store
};
}

这个地方是进行 vuex 的注册,可以将创建的 store 导出,这样子在其他 js 文件当中就能够引用这个单例的 store

  • App.vue

在该文件当中导出一个函数

1
2
3
4
asyncData(store) {
return new Promise((resolve) => {
})
}
1
2


在该函数当中请求后台接口预先加载数据并储存到vuex当中

  • entry-client.js

客户端文件的入口,替换服务端 store__INITIAL_STATE__,并生成页面并挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import {
createApp
} from './app';

const {
app,
store
} = createApp();

if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__);
}

app.$mount('#app');
  • entry-server.js

服务端的入口文件,await App.asyncData(), 对 store储存了之后再载入到客户端当中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import {
createApp
} from './app';
import App from './App.vue';

export default context => {
return new Promise(async (resolve, reject) => {
const {
app,
store
} = createApp();
await App.asyncData(store);
context.state = store.state;
resolve(app);
});
};
  • store.js