【源码阅读】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,体验一下丝滑,流畅的状态管理。

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

相关推荐
寒雒1 小时前
【Python】实战:实现GUI登录界面
开发语言·前端·python
独上归州1 小时前
Vue与React的Suspense组件对比
前端·vue.js·react.js·suspense
Komorebi⁼1 小时前
Vue核心特性解析(内含实践项目:设置购物车)
前端·javascript·vue.js·html·html5
明月清风徐徐1 小时前
Vue实训---0-完成Vue开发环境的搭建
前端·javascript·vue.js
SameX1 小时前
HarmonyOS Next 企业数据备份与恢复策略
前端·harmonyos
SameX1 小时前
HarmonyOS Next 企业数据传输安全策略
前端·harmonyos
daopuyun1 小时前
LoadRunner小贴士|开发Web-HTTP/HTML协议HTML5相关视频应用测试脚本的方法
前端·http·html
李先静1 小时前
AWTK-WEB 快速入门(1) - C 语言应用程序
c语言·开发语言·前端
MR·Feng1 小时前
使用Electron将vue2项目打包为桌面exe安装包
前端·javascript·electron
萧大侠jdeps1 小时前
图片生成视频-右进
前端·javascript·音视频