Vue3的SSR重构实战之状态管理方案

197 阅读1分钟

前言

历经一年的时间,我负责的业务快手游戏直播完成了重构,项目是基于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下状态管理的方案,在思考要不要把完整的代码放出来,等待后续大家的反应吧。