vuessr 中 serverPrefetch 的数据预处理(即服务端请求数据)


本文介绍如何在 serverPrefetch 做数据预处理(服务端请求数据)

vue-ssr 中 serverPrefetch 的数据预处理(即服务端请求数据)

原理都是通过 vuex 保存好数据然后传到客户端使用

普通 vuex 方式

请参照官方文档

用到 vuex-module-decorators 的方式

以下为简易代码:
/store/modules/serverState.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { VuexModule, Module, Mutation } from "vuex-module-decorators";
import store from "@/store";

export interface IServerState {
data: {};
}
@Module({ store, name: "serverState" })
export default class extends VuexModule implements IServerState {
data: Record<string, any> = {};//用于存储所有服务端数据
@Mutation
handleSetValue({ key, value }: { key: string; value: any }) {
this.data[key] = value;
}
}

store/index.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue'
import Vuex from 'vuex'
import serverStateModule from './modules/serverState'
Vue.use(Vuex)

export interface IRootState {
}

const store = new Vuex.Store<IRootState>({
modules:{
serverStateModule//注册模块
}
})

export default store

main.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'
import store from './store'

export function createApp(ctx: any) {
const router: any = createRouter(ctx ? ctx.cookies : '')
// sync(store, router)
const app = new Vue({
router,
store,
render: h => h(App)
})
return { app, router, store }
}

// promise.finally Polyfill
if (!Promise.prototype.finally) {
const P: any = Promise
P.prototype.finally = function (callback: Function) {
const P: any = this.constructor
return this.then(
(value: any) => P.resolve(callback()).then(() => value),
(reason: any) =>
P.resolve(callback()).then(() => {
throw reason
})
)
}
}

entry-server.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {
createApp
} from './main'
export default context => {
return new Promise((resolve, reject) => {
const beginTime = Date.now()
const {
app,
router,
store
} = createApp(context)
// set router's location
router.push(context.url)
router.onReady(() => {
// This `rendered` hook is called when the app has finished rendering
context.rendered = () => {
// After the app is rendered, our store is now
// filled with the state from our components.
// When we attach the state to the context, and the `template` option
// is used for the renderer, the state will automatically be
// serialized and injected into the HTML as `window.__INITIAL_STATE__`.
context.state = store.state //必须
}
resolve(app)
}, reject)
})
}

entry-client.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {
createApp
} from './main'
const {
app,
router,
store
} = createApp()
//必须
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__)
}
router.onReady(() => {
app.$mount('#app')
})

使用:
views/home/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template><div>{{bannerList}}</div></template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { getModule } from 'vuex-module-decorators'
import serverStateModule from '@/store/modules/serverState'
@Component({
name: 'Home',
mixins: [],
filters: {},
data() {
return {}
},
components: {},
})
export default class Home extends Vue {
created() {
}
get bannerList() {
return this.$store.state.serverStateModule &&
this.$store.state.serverStateModule.data.bannerList
? this.$store.state.serverStateModule.data.bannerList
: []
}
async mounted() {
//预防万一服务端没有数据可以在客户端再次请求
if (this.bannerList.length <= 0) {
const bannerList = await this.getBannerList()
this.$store.commit('handleSetValue', {
value: bannerList,
key: 'bannerList',
})
}
}
async getBannerList(): Promise<any> {
return this.getAjaxData()//这里逻辑自行实现
}
async serverPrefetch() {
const bannerList = await this.getBannerList()
const serverStateModuleInstance = getModule(serverStateModule, this.$store)
// 使用模块进行赋值操作
serverStateModuleInstance.handleSetValue({
value: bannerList,
key: 'bannerList',
})
}
}
</script>

参考资料

1.官方文档
2.vuex-module-decorators 文档