目录
- [1,Pinia 介绍](#1,Pinia 介绍)
- [2,和 Vuex 的对比](#2,和 Vuex 的对比)
- [3,storeToRefs 源码分析](#3,storeToRefs 源码分析)
1,Pinia 介绍
Pinia 使用上的问题,官方文档很详细,这里不做赘述。
- Pinia 是 Vue 的专属状态管理库,支持vue2和vue3,不能应用于其他前端框架。
- Pinia 是 Vue 官方团队的成员实现的,原本是为了探索 vue5.x 的可能性,后来发现已经实现了 vue5.x 的提案。所以就作为最新版本的 Vuex ,也就是下一代状态管理库来使用了。
2,和 Vuex 的对比
- 删除了
mutations
,只保留了state
,getters
,actions
。异步修改 state 现在可以放到actions
中了。 - 不再有模块嵌套,只有 store(状态仓库)的概念,store 之间独立又可相互调用。
- 支持插件扩展,比如为 store 新增属性或方法,操作 action 等。
- 因为 Pinia 使用 TS 开发的,所以对 TS 有很好的支持。
- 兼容 vue2 和 vue3,同时支持选项式风格和组合式风格。
- 轻量,压缩后体积只有 1kb 左右。
在使用上,同样应避免直接操作 store,尽量都集中使用 actions 中的方法来操作 store 的状态。
3,storeToRefs 源码分析
在组件中使用 store 的属性时,会有一个问题:
html
<script setup>
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性,这就和直接解构 `props` 一样
const { name, doubleCount } = store
// ✅ 这样写是响应式的,当然也可直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
// ✅ 作为 action 的 increment 可以直接解构
const { increment } = store
</script>
这时需要使用 storeToRefs()
,它将为每一个响应式属性创建引用。
html
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const { name, doubleCount } = storeToRefs(store)
</script>
源码
js
import { toRaw, ref, isVue2, isRef, isReactive, toRef, toRefs } from 'vue-demi';
/**
* @param store - defineStore 定义的 store 对象
*/
function storeToRefs(store) {
if (isVue2) {
return toRefs(store);
}
else {
store = toRaw(store);
const refs = {};
for (const key in store) {
const value = store[key];
if (isRef(value) || isReactive(value)) {
refs[key] = toRef(store, key);
}
}
return refs;
}
}
原理其实很简单,我们在解构 props 时,直接解构也会失去响应式。需要使用 toRef
与其源属性保持同步。
所以 storeToRefs
的原理:遍历 store 中的响应式属性(跳过了 action),并使用 toRef
处理后返回新对象,这样解构新对象得到的属性会和 store 的源属性保持同步,而不失去响应式。
js
const store1 = reactive({
a: ref(1),
});
const store2 = toRaw(store1);
const _a = toRef(store2, "a");
_a.value++;
console.log(store1.a); // 2
以上。