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,体验一下丝滑,流畅的状态管理。
如果这篇文章对你有所收获,欢迎点赞评论。