Skip to content

SSR-pinia

vite-ssr

参考文档在这里

ts
import devalue from '@nuxt/devalue';

export default viteSSR(
  App,
  {
    routes,
    transformState(state) {
      return import.meta.env.SSR ? devalue(state) : state;
    },
  },
  ({ initialState }) => {
    // ...
    if (import.meta.env.SSR) {
      // 序列化并设置为 window.__INITIAL_STATE__
      initialState.pinia = pinia.state.value;
    } else {
      // 在客户端,我们恢复 state
      pinia.state.value = initialState.pinia;
    }
  }
);

@pinia/nuxt

@pinia/nuxt 源代码

ts
import { createPinia, setActivePinia } from 'pinia';
import type { Pinia } from 'pinia';
import { defineNuxtPlugin, type Plugin } from '#app';
import { toRaw } from 'vue';

const plugin: Plugin<{ pinia: Pinia }> = defineNuxtPlugin({
  name: 'pinia',
  setup(nuxtApp) {
    const pinia = createPinia();
    nuxtApp.vueApp.use(pinia);
    setActivePinia(pinia);

    if (import.meta.server) {
      nuxtApp.payload.pinia = toRaw(pinia.state.value);
    } else if (nuxtApp.payload && nuxtApp.payload.pinia) {
      pinia.state.value = nuxtApp.payload.pinia as any;
    }

    // Inject $pinia
    return {
      provide: {
        pinia,
      },
    };
  },
});

export default plugin;

总结

观察 pinia 在两种不同的 SSR 方案下的实现逻辑。 可以发现最关键的点在于 pinia 把所有的数据都保存在pinia.state.value变量中。 而关于序列化和反序列化的逻辑都是有外部方案提供。

比如在服务器端需要把数据进行序列化,然后需要把序列化后的数据放到 html 中特定位置,并且在客户端可以自动读取刚才序列化之后的数据。 这是一个复杂的流程。但是在vite-ssr项目中只需要initialState.pinia = pinia.state.value;,而在nuxt项目中只需要nuxtApp.payload.pinia = toRaw(pinia.state.value);。 剩下的工作,vite-ssr或者nuxt自动会帮我们完成。

反序列化也是类似的,本来我们是需要按照服务器端序列化的逻辑,需要手动读取 html 中特定的序列化字符串,然后将这个序列化字符串转换成对象。 但是在vite-ssr或者nuxt项目中分别只需要pinia.state.value = initialState.pinia;pinia.state.value = nuxtApp.payload.pinia as any;即可。

核心总结就是序列化和反序列化的具体工作自然是有其他技术框架来完成,作为状态管理工作的 pinia,只需要提供一个持有所有数据的变量pinia.state.value即可。

分析源码,pinia 在实现这一点上主要依赖 2 个特性。
第 1 是创建的每一个 store 都有一个唯一的 ID,这个 ID 可以作为这个 store 的 key。
第 2 是创建的每一个 store 都有一个 state 属性,所有需要序列化的数据都存储在 state 对象中。这种方案的好处就是只需要关注 state 属性中的所有数据即可,而不需要通过遍历 store 来获取所有属性。