受够了手动storeToRefs?来试试这个vite插件吧

4,185 阅读4分钟

前言

尤雨溪在 3 月 24 日晚与掘金合作的直播中提到传送门,pinia 就是实际上的 vuex5,作为新一代的状态管理器,更友好的 ts 支持,更轻量的打包体积,更简化的模块管理,无疑会在将来的市场中备受欢迎。

pinia 的优点相比也不用多说了,但也正是由于其处于一个新生的阶段,周边生态还不够完善,使用过程中也会有一些不太方便的地方。

例如我们现在创建一个 userStore:

import { defineStore } from 'pinia'

export const useUserStore = defineStore({
  id: 'user',
  state: () => {
    return {
      name: 'allen',
    }
  },
  getters: {
    fullName: ({ name }) => name + '_1998',
  },
  actions: {
    updateName(name: string) {
      this.name = name
    },
  },
})

当我们想使用 store 中的数据时,可以通过 useUserStore()来获取,但是直接解构拿到的 state 中的数据会丢失响应式:

import { useUserStore } from '@/store/user'

const { name } = useUserStore() //这里的响应式会丢失

官方为了处理这种情况暴露出了 storeToRefs 这个 api,示例:

import { useUserStore } from '@/store/user'
import { storeToRefs } from 'pinia'

const userStore = useUserStore()
const { name, fullName } = storeToRefs(userStore)

这样便可以在使用时不丢失 state 中数据的响应式,但是如果我们同时需要用到 store 中的一些方法时,我们的代码可能就会变成下面这样:

import { useUserStore } from '@/store/user'
import { storeToRefs } from 'pinia'

const userStore = useUserStore()
const { name, fullName } = storeToRefs(userStore)
const { updateName } = userStore

虽然可以满足我们使用的需求,但是整体的代码难免会显得有些冗余,可读性和可维护性也会变得有些奇怪,如果我们可以像下面一样只需简单调用就可以正常使用的话就太棒了:

const { name, fullName, updateName } = useStore('user')

在 pinia 的 github 上也有人提出了相关的issues,基于评论区的一些讨论和点子,加上我自己的一些想法,pinia-auto-refs诞生了,它可以使你的 pinia 变得更加易于使用,同时保留 ts 的类型检查和类型提示,简单引入后你便可以像上面的示例一样在你的项目中使用。

使用说明

安装

npm i pinia-auto-refs

配置

推荐配合插件unplugin-auto-import一起使用,可以帮助我们自动导入 api 等。

// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import PiniaAutoRefs from 'pinia-auto-refs'

export default defineConfig({
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
    },
  },
  plugins: [
    AutoImport({
      imports: [
        'pinia',
        {
          '@/helper/pinia-auto-refs': ['useStore'],
        },
      ],
    }),
    PiniaAutoRefs(),
  ],
})

基本示例

项目结构

注:使用该插件需要将 store 的导出切换为 export default

// 目录结构
src
├── index.ts
├── user.ts
└── app.ts

// src/store/index.ts
const store = createPinia()

export default store
// src/store/user.ts
export default defineStore({
  id: 'user',
  state: () => {
    return {
      name: 'allen',
    }
  },
  getters: {
    fullName: ({ name }) => name + '_1998',
  },
  actions: {
    updateName(name: string) {
      this.name = name
    },
  },
})
// src/store/app.ts
export default defineStore({
  id: 'app',
  ......
})

项目启动后将会自动根据 store 生成src/helper/pinia-auto-refs.ts,且当 store 里的内容更新时也会自动同步更新:

import appStore from '@/store/app'
import userStore from '@/store/user'

import { ToRef, AutoToRefs } from 'vue'
declare module 'vue' {
  export type AutoToRefs<T> = {
    [K in keyof T]: T[K] extends Function ? T[K] : ToRef<T[K]>
  }
}

const storeExports = {
  app: appStore,
  user: userStore,
}

export function useStore<T extends keyof typeof storeExports>(storeName: T) {
  const store = storeExports[storeName]()
  const storeRefs = storeToRefs(store)
  return { ...store, ...storeRefs } as unknown as AutoToRefs<ReturnType<typeof storeExports[T]>>
}

实战效果

使用时:

<script lang="ts" setup>
const { name, fullName, updateName } = useStore('user')

</script>

使用效果截图:

image.png

image.png

image.png

image.png

可以看到简化了写法的同时又完美支持了ts的类型检查和类型提示,老奶奶用了都说好。

总结

新技术会带给我们更良好的开发体验,但是我们同样应该关注其社区环境,并力所能及的贡献出自己的一份力量。本插件开发的新路历程也是为了解决目前pinia在项目中使用storeToRefs相对而言比较麻烦,代码冗余的问题。

整个插件的实现过程其实并不复杂,更重要的其实是发现问题,在社区中讨论问题,并最终解决问题的整个过程,希望小伙伴们也可以更多的参与到社区的建设当中,与国际社区更多接轨,整个前端的生态环境才会变得越来越好。

目前源码已发布至 github,详情见 源码地址 说明文档 使用案例,有相关意见或建议的也欢迎到评论区或github上进行讨论,对你有帮助或者喜欢的话请点个 Star。

参考