【源码阅读】vue官方状态管理库Pinia,项目中到底还需不需要?

vue3引入Composition API之后,写法就变得很灵活了。

比如我们可以把一些功能封装成hook,直接使用hook返回的变量。

又或者我们可以将变量,方法定义在文件外部,作为全局变量引入。

比如下面这样。

user.js 复制代码
import {reactive, computed } from "vue"

export const userIfno = reactive({
  age: 0,
  name: "张三"
})

export const getUserInfo = computed(() => {
  return userIfno.age.toString() + userIfno.name
})

export const setUserInfo = ({age, name}) => {
  userIfno.age = age
  userIfno.name = name
}
.vue 复制代码
<script setup lang="ts">
import { userIfno, setUserInfo, getUserInfo } from "./user"
</script>

我们完全可以写一个全局的js文件来代替状态管理库。

那我们还需不需要专门再引入一个状态管理库呢?

这是我在使用Pinia之前的一个最大的疑问。

关于这一点,Pinia在官方也做了说明。

从官方的介绍来看,用js文件来管理全局状态也是可行的,只不过在服务端渲染会有安全漏洞。

(嗯,好像也不是很吸引人)也有可能是我很少做服务端渲染的原因。

但是Devtools是比较方便,也比较好用的。

插件功能好像也挺好的。

但是不管怎么样,我们还是从源码中找一找答案,再来决定是否需要pinia。


1. 源码调试。

arduino 复制代码
git  clone https://github.com/vuejs/pinia.git
pnpm install
pnpm run play

只要简单3步,代码就可以启动了。

这里说一下怎么找到对应的源码文件。(不仅适用于pinia,其他库也是适用)

1.找入口脚本

我们是通过play命令启动的,所以先找到play命令是在哪里定义的。

pnpm执行命令先执行当前package.json里的脚本,如果没有,则去找子包里面的脚本。

发现对应的入口文件,就是packages/playground

2.找入口文件

从上面截图可以看出,是通过vite启动的。

那我们看一下vite.config的配置。

如果vite.config没有配置的话,默认就是src/main文件。(这其实就是一个普通的vue项目,跟我们平常开发的没有什么两样,直接上各种测试代码就行。)

配置文件中并没有配置入口,但是配了一个alias。(也就是说,import pinia 会从本地的pinia库引入。)

3.找源码位置

找到pinia/src/index.ts文件。

如果第二步没有配置,或者没有找到,也可以找一下pinia下面的打包入口文件。

2. 源码阅读

阅读就按照我们使用的逻辑来看,理一遍主线的逻辑。

1.首先是插件注册。

scss 复制代码
import { createPinia } from 'pinia'
const pinia = createPinia()
pinia.use(() => ({
  route: computed(() => markRaw(router.currentRoute.value)),
}))
app.use(pinia)

返回一个对象,对象中提供install方法。(当app.use的时候,执行install)

我们接着看install方法。

scss 复制代码
      setActivePinia(pinia)
      // 不支持vue2
      if (!isVue2) {
        // 存app对象
        pinia._a = app
        // 将对象设置为上下文,传入后续组件
        app.provide(piniaSymbol, pinia)
        // 设置全局变量
        app.config.globalProperties.$pinia = pinia
        /* istanbul ignore else */
        // dev_tools 
        if (__USE_DEVTOOLS__ && IS_CLIENT) {
          registerPiniaDevtools(app, pinia)
        }
        // 处理在app.use之前使用pinia.use
        toBeInstalled.forEach((plugin) => _p.push(plugin))
        toBeInstalled = []
      }

代码中基本都加了备注。

除此之外的,还需要额外关注一下,_e,_s两个属性。

_s就是一个map用来存store对象的。 _e是effectScope(),用来捕获响应式副作用的。(参考一下官方介绍)

2.创建仓库

defineStore 复制代码
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useTest = defineStore('test', () => {
  const age = ref(0)

  const addAge = () => {
    age.value++
  }

  return {
    age,
    addAge,
  }
})
.vue 复制代码
const test = useTest()

最主要的就是这个defineStore了。

返回了一个useStore。(use开头的,这不就是一个hook吗?)

红框部分很好理解,就是先获取pinia对象,然后判断这个对象的_s上有没有这个仓库,如果有则获取,如果没有,就是创建完之后再获取。

我们第一次用,肯定是没有。(那就继续看如何创建)

这里判断了一下,options的传参数类型。可以是对象(vuex写法),也可以是setup函数。

我们直接看createSetupStore。(因为对象最后也会被转成函数)

这个函数就有点长了,大概有个500来行代码。(很多的钩子函数都是在这里定义的)

虽然代码有点多,但是核心的其实也就那么几段代码。

1.定义了store的格式

从这里可以看出,store上是有很多钩子函数的。

2. 获取定义的store数据

首先runWithContext这个函数是注入上下文的。比如我provide一个数据,但是我现在又没有.vue组件,那怎么拿到这个值呢?就可以用runWithContext这个函数。

scope.run(setup) 直接把数据返回回来了。

3.返回store

至此,一个store从注册,创建到使用的流程就走通了。

篇幅有限,中间其实省了很多内容,比如钩子怎么用?插件怎么用?参数对象形式怎么转换?

这个就留给大家自己研究了。

3.总结一波

vue3的强悍,让pinia这个状态管理库的存在感略低。

尽管使用全局变量的方式,也可以实现类似pinia的状态管理。

但是不管是可视化的调试,还是数据的管理,以及插件钩子函数的使用,pinia都是非常值得使用的。

如果你还未开始pinia的使用,我十分建议你在项目中引入pinia,体验一下丝滑,流畅的状态管理。

如果这篇文章对你有所收获,欢迎点赞评论。

相关推荐
乘风gg1 小时前
还在养虾吗?虾王已诞生:微信龙虾 ClawBot
前端·ai编程·claude
小小小小宇1 小时前
LLM 长期记忆构建
前端
lichenyang4531 小时前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__2 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
程序员小富3 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇3 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇3 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆3 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马3 小时前
Verilog开发常见问题汇总解析
前端
子兮曰3 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端