前言
历经一年的时间,我负责的业务快手游戏直播完成了重构,项目是基于Vue3的SSR,至少在项目启动时,Nuxt还只是beta,成熟的Vue3的SSR项目还是比较少见,目前项目已经完整上线,最近也是在复盘总结,在团队内也做了一些分享 ,也想在外界分享一些解决方案。
正文
综合考虑使用Pinia作为项目的状态管理方案,虽然Pinia支持SSR的使用,但是在项目实践中,感觉还是不太好用,基于Hook的思想,我思考是否在上面封装一层useStore更方便使用
仔细阅读Pinia的官方文档和源码,useStore需要做三件事情
- 需要保证正确透传ts类型
// 补充一些自定义的字段
interface InnerStore {
$count: number
$isServerPretch: boolean
prefetch: () => void
preload: () => void
$id: string
$dispose: () => void
$state: any
}
function useStore<RETURNT>(storeFactory: () => RETURNT): RETURNT {
// 正确设置storeFactory的类型
const store = storeFactory() as RETRUNT & InnterStore
return store;
}
- pinia会将已创建过的pinia存储在根的store,避免内存泄漏和旧数据的干扰。利用 beforeMount,beforeUnmount的生命周期hook 自动引用计数的方式,将已废弃的数据自动删除
- 利用ServerPrefetch的生命周期Hook实现颗粒化控制在SSR阶段执行的请求与进入页面自动执行,并标记是否在SSR执行过
// beforeMount生命周期
onBeforeMount(() => {
if (!store.$count) {
store.$count = 0;
}
// 引用计数+1
store.$count++;
// 在csr阶段,store第一次被引用时
if (!isServer && store.$count === 1) {
// 并未在ssr阶段执行过prefetch方法时
if (!store.$isServerPretch && store.prefetch) {
store.prefetch({ ssrHeaders: null });
}
// 执行preload方法
if (store.preload) {
store.preload();
}
}
})
// serverPrefetch 只会在ssr阶段执行
onServerPrefetch(async () => {
(!store.$state.$isServerPretch) && store.prefetch && await store.prefetch();
// 用于标识serverpetch方法是否被执行过
store.$state.$isServerPretch = true;
})
import { getActivePinia } from 'pinia';
const rootStore = getActivePinia();
// beforeUnmount
onBeforeUnmount(() => {
store.$count--;
if (store.$count === 0) {
// 清理根store的缓存
store.$dispose();
delete rootStore.state.value[store.$id];
}
});
实际使用
// user.store
export const userStore = defineStore('user', {
state: () => ({
info: {}
}),
actions: {
// 进入页面时自动执行,并不会在ssr阶段执行
preload() {
},
// 在ssr阶段执行或者csr阶段自动执行
prefetch() {
},
}
})
结尾
这个文章只是简单描述了ssr下状态管理的方案,在思考要不要把完整的代码放出来,等待后续大家的反应吧。