前言

在我写nuxt3项目时,遇到了使用pinia在页面刷新时数据丢失的老生常谈的问题,在这里简单记录下我的解决方案

工程搭建

这里我就不做过多的工程搭建介绍,我是跟着nuxt官网步骤完成的工程初始化,完整的工程代码可点击链接查阅

初始化后的工程结构如下

配置并使用pinia

根据pinia官网的步骤配置即可

首先安装相关依赖

pnpm add pinia @pinia/nuxt
复制代码

然后进行配置,修改nuxt.config.ts

export default defineNuxtConfig({
+  modules: [
+    '@pinia/nuxt'
+  ]
})

复制代码

个人习惯,我会配置一个autoImport,运行时自动引入defineStore

export default defineNuxtConfig({
  modules: [
    '@pinia/nuxt'
  ],
+  pinia: {
+    autoImports: [
+      'defineStore', // import { defineStore } from 'pinia'
+    ],
+  }
})

复制代码

新建目录composables,新建文件store.ts

export const useNuxtStore = defineStore('nuxtStore', () => {
  const state = ref(0)

  const setState = (num: number) => {
    state.value = num
  }

  return {
    state,
    setState,
  }
})
复制代码

我这里的写法是Setup Stores,就像是一个普通的composable,只不过使用defineStore定义生成

定义好store之后,我们在app.vue中使用store

<script setup lang="ts">
const store = useStateStore()
</script>

<template>
  <div style="font-size: 40px; line-height: 60px;">
    <div>Hi! I'm nuxt project!</div>
    <div>
      Here state: {{ store.state }}
      <button @click="store.setState">setState</button>
    </div>
  </div>
</template>

复制代码

这时候保存即可看到效果

提个醒,我这里直接使用的const store = useStateStore()而没有解构,是因为解构后会丢失响应式,如果你需要解构直接使用state并且保持响应式,可以使用storeToRefs

多次点击setState按钮会增加state的数值,当页面刷新之后,state会变回0

到这里工程就算是搭建好了,下面来讲解持久化保持数据的处理方法

使用vueuse

vueuse中有个storage相关的hook,我们可以使用hook来手动保存store,在刷新的时候会从storage中读取,就能达到数据保存的效果

我们需要配置nuxt+vueuse的环境,这里根据vueuse官网进行配置即可

首先安装依赖

pnpm add @vueuse/nuxt @vueuse/core
复制代码

然后配置nuxt.config.ts

export default defineNuxtConfig({
  modules: [
    '@pinia/nuxt',
+    '@vueuse/nuxt',
  ],
  pinia: {
    autoImports: [
      'defineStore', // import { defineStore } from 'pinia'
    ],
  }
})

复制代码

这样就能直接在工程中使用@vueuse/core的所有hook了

我们使用useSessionStoragehook来进行缓存,修改composables/store.ts

export const useStateStore = defineStore('nuxtStore', () => {
-  const state = ref(0)
+  const state = useSessionStorage('nuxt-store-test', 0)

  const setState = () => {
    state.value = state.value + 1
  }

  return {
    state,
    setState,
  }
})
复制代码

这里我们使用useSessionStorage来初始化state,在应用首次运行的时候,session-storage中不存在nuxt-store-test这个键值对,所以就取第二个默认值参数作为初始值

保存运行后可以看到,控制台中多了个nuxt-store-test键值对

当我点击按钮改变state的时候,控制台中nuxt-store-test的值也跟着改变

当页面刷新的时候,期望场景是state的值会保持不变,但其实发现,state会变回0

具体原因在pinia文档里也有讲解,在SSR环境下使用部分composables需要额外注意

当我们在SSR下使用useSessionStorage时,需要考虑hydrate阶段的处理,state的值应该从浏览器读取,而不需要在hydrate阶段进行激活,所以我们需要对这些字段跳过处理

我们修改composables/store.ts

+ import { skipHydrate } from 'pinia'

export const useStateStore = defineStore('nuxtStore', () => {
  const state = useSessionStorage('nuxt-store-test', 0)

  const setState = () => {
    state.value = state.value + 1
  }

  return {
+    state: skipHydrate(state),
    setState,
  }
})
复制代码

使用skipHydrate方法,标记state属性不能被激活,需要到浏览器中读取数值,这样处理后在页面刷新时也能保持数据的内容

使用pinia插件实现持久化

这应该是比较普遍的做法,像vuex时期也是用持久化插件来实现

pinia中有个下载量较多的插件pinia-plugin-persistedstate,这是插件文档

首先安装插件

pnpm add @pinia-plugin-persistedstate/nuxt
复制代码

然后配置nuxt.config.ts

export default defineNuxtConfig({
  modules: [
    '@pinia/nuxt',
    '@vueuse/nuxt',
+    '@pinia-plugin-persistedstate/nuxt',
  ],
  pinia: {
    autoImports: [
      'defineStore', // import { defineStore } from 'pinia'
    ],
  },
+  piniaPersistedstate: {
+    storage: 'sessionStorage',
+  }
})

复制代码

新建文件composables/plugin.ts,在这里写使用插件的store

export const usePluginStateStore = defineStore('pluginStore', () => {
  const pState = ref(0)

  const setPState = () => {
    pState.value += 1
  }

  return {
    pState,
    setPState,
  }
}, {
  persist: true,
})

复制代码

重点是,在defineStore方法的第三个参数,要传入persist: true,表示这个store要进行持久化处理

app.vue中使用该store

<script setup lang="ts">
const store = useStateStore()
+ const pStore = usePluginStateStore()
</script>

<template>
  <div style="font-size: 40px; line-height: 60px;">
    <div>Hi! I'm nuxt project!</div>
    <div>
      Here state: {{ store.state }}
      <button @click="store.setState">setState</button>
    </div>
+    <div>
+      Here plugin state: {{ pStore.pState }}
+      <button @click="pStore.setPState">setPState</button>
+    </div>
  </div>
</template>

复制代码

这时候点击setPState按钮,控制台就会保存pState的值,并且刷新后也能展示

使用其他pinia插件

以上两种方案已经能很好的解决现有问题,但如果你还希望使用其他pinia插件,而该插件没有做nuxt版本呢

pinia文档中有介绍在nuxt环境下如何使用pinia插件,按照如下方式使用即可,这里没有进行验证,想用这种方式的需要自行验证使用

// plugins/myPiniaPlugin.ts
import { PiniaPluginContext } from 'pinia'

function MyPiniaPlugin({ store }: PiniaPluginContext) {
  store.$subscribe((mutation) => {
    // react to store changes
    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)
  })

  // Note this has to be typed if you are using TS
  return { creationTime: new Date() }
}

export default defineNuxtPlugin(({ $pinia }) => {
  $pinia.use(MyPiniaPlugin)
})

复制代码

总结

文章主要介绍了两种nuxt3+pinia的数据持久化处理:

  • 使用vueuseuseStorage/useLocalStorage/useSessionStorage
  • 使用pinia的插件pinia-plugin-persistedstate
Logo

前往低代码交流专区

更多推荐