pinia的架构是怎样的?

2026-05-27 15:572阅读0评论SEO资源
  • 内容介绍
  • 相关推荐

本文共计1491个文字,预计阅读时间需要6分钟。

pinia的架构是怎样的?

随着Vue3的正式发布,Pinia也逐渐火热起来。为了更新自己的知识树,我们需要关注新的状态管理方式。这里主要看看响应式的新状态容器reactive是如何工作的。根据官方文档,我们可以做一个简单的例子。

随着 Vue3 的正式转正,Pinia 也渐渐火了起来。所以要更新一下自己的知识树了。这里主要是看看新的状态是什么“形态”。

状态的容器还是“reactive”
  • 按照官网教程,做一个简单的例子:

import { defineStore } from 'pinia' export const usePersonStore = defineStore('objectTest', { state: () => { return { name: 'jyk', age: 18, info: { a1: '11', a2: '22' } } }, // 也可以这样定义状态 // state: () => ({ count: 0 }) actions: { nameAction() { this.name += '11' } }, getters: { ageTest(state) { // 会有代码自动补全! return state.age += 100 } } })

  • 组件里引用:

import { usePersonStore } from './object.js' const xiaoming = usePersonStore() console.log('\n xiaoming:') console.dir(xiaoming) console.log('counter - keys :') console.log(Object.keys(xiaoming)) console.log('counter - for in :') for (const key in xiaoming){ console.log(key) }

{{xiaoming}}<br> {{xiaoming.age}}<br> <div v-for="(item, key, index) in xiaoming"> {{index}} -- {{key}}: {{item}} </div>

  • 然后看看效果

状态果然采用了 reactive,只是内部结构有点茫然,为啥是这样?

数据部分变成了 ref,错,是 toRef

一开始看,是把数据部分变成了 ref,但是仔细一看,原理是toRef。好吧,大概是为了保证响应性,自动结构了。只是还是挺无语的。

有图为证:

getter 变成了 computed

这个在意料之中,只是为啥和数据在一个“层级”上?

action 和数据是一级的。

action 还是函数的形式,只是,应该挂在“原型”上面吧,为啥又和数据挤在一起了?

状态是否需要遍历?

为啥这么在意 getter、action是不是和数据在一个“层级”上呢?因为我经常使用遍历的方式。
试了一下,果然都出来了。

如果在使用状态的时候,不需要用到遍历的话,可以跳过。

template 模板 整体使用

{{xiaoming}}

  • 结果:

{ "$id": "objectTest", "name": "jyk", "age": 118, "info": { "a1": "11", "a2": "22" }, "ageTest": 118 }

  • 讨论
    出现了$id和数据成员,虽然没有出现 getter,但是被执行了一次。
    所以age变成了“118”。
    好吧,可能是我的用法不对。
分开使用

{{xiaoming.age}}<br><br>

不会触发getter,age还是 18

pinia的架构是怎样的?

{{xiaoming.age}}<br><br> {{xiaoming.ageTest}}<br><br>

两个都显示为 “118”

v-for

<div v-for="(item, key, index) in xiaoming"> {{index}} -- {{key}}: {{item}} </div>

  • 结果

0 -- $id: objectTest 1 -- $onAction: function () { [native code] } 2 -- $patch: function $patch(partialStateOrMutator) { let subscriptionMutation; isListening = false; if (true) { debuggerEvents = []; } if (typeof partialStateOrMutator === "function") { partialStateOrMutator(pinia.state.value[$id]); subscriptionMutation = { type: MutationType.patchFunction, storeId: $id, events: debuggerEvents }; } else { innerPatch(pinia.state.value[$id], partialStateOrMutator); subscriptionMutation = { type: MutationType.patchObject, payload: partialStateOrMutator, storeId: $id, events: debuggerEvents }; } isListening = true; triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); } 3 -- $reset: function $reset() { const newState = state ? state() : {}; this.$patch(($state) => { assign($state, newState); }); } 4 -- $subscribe: $subscribe(callback, options2 = {}) { if (typeof options2 === "boolean") { console.warn(`[\u{1F34D}]: store.$subscribe() no longer accepts a boolean as the 2nd parameter: Replace "store.$subscribe(fn, ${String(options2)})" with "store.$subscribe(fn, { detached: ${String(options2)} })". This will fail in production.`); options2 = { detached: options2 }; } const _removeSubscription = addSubscription(subscriptions, callback, options2.detached); const stopWatcher = scope.run(() => watch(() => pinia.state.value[$id], (state, oldState) => { if (isListening) { callback({ storeId: $id, type: MutationType.direct, events: debuggerEvents }, state); } }, assign({}, $subscribeOptions, options2))); const removeSubscription = () => { stopWatcher(); _removeSubscription(); }; return removeSubscription; } 5 -- $dispose: function $dispose() { scope.stop(); subscriptions = []; actionSubscriptions = []; pinia._s.delete($id); } 6 -- name: jyk 7 -- age: 118 8 -- info: { "a1": "11", "a2": "22" } 9 -- nameAction: function() { const _actionId = runningActionId; const trackedStore = new Proxy(store, { get(...args) { activeAction = _actionId; return Reflect.get(...args); }, set(...args) { activeAction = _actionId; return Reflect.set(...args); } }); return actions[actionName].apply(trackedStore, arguments); } 10 -- ageTest: 118 11 -- _hotUpdate: function(newStore) { originalHotUpdate.apply(this, arguments); patchActionForGrouping(store, Object.keys(newStore._hmrPayload.actions)); }

好吧,大概是我的使用方式不对。

for in

console.log('counter - for in :') for (const key in xiaoming){ console.log(key) }

  • 结果

counter - for in : pinia.vue:42 $id pinia.vue:42 $onAction pinia.vue:42 $patch pinia.vue:42 $reset pinia.vue:42 $subscribe pinia.vue:42 $dispose pinia.vue:42 name pinia.vue:42 age pinia.vue:42 info pinia.vue:42 nameAction pinia.vue:42 ageTest pinia.vue:42 _hotUpdate

  • 讨论
    没有出现 action。出现了内部设置的成员,以及数据、getter。总之和我想的不一样。
Object.keys

console.log('counter - keys :') console.log(Object.keys(xiaoming))

  • 结果

counter - keys : pinia.vue:38 (12)["$id", "$onAction", "$patch", "$reset", "$subscribe", "$dispose", "name", "age", "info", "nameAction", "ageTest", "_hotUpdate"] 我想的到底是啥样的呢?

可以使用 class + reactive 实现一个充血实体类,比如这样:

import { computed, reactive } from 'vue' // 充血实体类 class TestClass { constructor (_info) { // 设置属性,数组或者对象 this.name = 'jyk' this.age = 18 this.info = { a1: 'a1', a2: 'a2' } } // 通用赋值 $set(model, clear = false) { if (clear) { Object.keys(this).forEach(key => { delete this[key] }) } Object.assign(this, model) } actionTest() { this.age += 1 } get getterTest() { const tmp = computed(() => { return this.age + 100}) return tmp } } export default function() { const tmp = new TestClass() return reactive(tmp) }

在组件里使用:

const test2 = testClass() console.log(test2) console.log(test2.getterTest) console.log('\n 遍历 Object.keys -----') console.log(Object.keys(test2)) console.log('\n 遍历 for in -----') for (const key in test2) { console.log(key, ':', test2[key]) } console.log('\n 遍历 for in 结束 -----')

获取实例后,套上 reactive 就可以获得响应性。

这样数据部分在第一层,其他各种方法都在“原型”里面,那么在 v-for、 Object.keys 和for...in的时候,只会出现数据部分,没有各种函数了。

整体结构也很简洁。

  • 看看打印效果

遍历的情况也是挺好的。

本文共计1491个文字,预计阅读时间需要6分钟。

pinia的架构是怎样的?

随着Vue3的正式发布,Pinia也逐渐火热起来。为了更新自己的知识树,我们需要关注新的状态管理方式。这里主要看看响应式的新状态容器reactive是如何工作的。根据官方文档,我们可以做一个简单的例子。

随着 Vue3 的正式转正,Pinia 也渐渐火了起来。所以要更新一下自己的知识树了。这里主要是看看新的状态是什么“形态”。

状态的容器还是“reactive”
  • 按照官网教程,做一个简单的例子:

import { defineStore } from 'pinia' export const usePersonStore = defineStore('objectTest', { state: () => { return { name: 'jyk', age: 18, info: { a1: '11', a2: '22' } } }, // 也可以这样定义状态 // state: () => ({ count: 0 }) actions: { nameAction() { this.name += '11' } }, getters: { ageTest(state) { // 会有代码自动补全! return state.age += 100 } } })

  • 组件里引用:

import { usePersonStore } from './object.js' const xiaoming = usePersonStore() console.log('\n xiaoming:') console.dir(xiaoming) console.log('counter - keys :') console.log(Object.keys(xiaoming)) console.log('counter - for in :') for (const key in xiaoming){ console.log(key) }

{{xiaoming}}<br> {{xiaoming.age}}<br> <div v-for="(item, key, index) in xiaoming"> {{index}} -- {{key}}: {{item}} </div>

  • 然后看看效果

状态果然采用了 reactive,只是内部结构有点茫然,为啥是这样?

数据部分变成了 ref,错,是 toRef

一开始看,是把数据部分变成了 ref,但是仔细一看,原理是toRef。好吧,大概是为了保证响应性,自动结构了。只是还是挺无语的。

有图为证:

getter 变成了 computed

这个在意料之中,只是为啥和数据在一个“层级”上?

action 和数据是一级的。

action 还是函数的形式,只是,应该挂在“原型”上面吧,为啥又和数据挤在一起了?

状态是否需要遍历?

为啥这么在意 getter、action是不是和数据在一个“层级”上呢?因为我经常使用遍历的方式。
试了一下,果然都出来了。

如果在使用状态的时候,不需要用到遍历的话,可以跳过。

template 模板 整体使用

{{xiaoming}}

  • 结果:

{ "$id": "objectTest", "name": "jyk", "age": 118, "info": { "a1": "11", "a2": "22" }, "ageTest": 118 }

  • 讨论
    出现了$id和数据成员,虽然没有出现 getter,但是被执行了一次。
    所以age变成了“118”。
    好吧,可能是我的用法不对。
分开使用

{{xiaoming.age}}<br><br>

不会触发getter,age还是 18

pinia的架构是怎样的?

{{xiaoming.age}}<br><br> {{xiaoming.ageTest}}<br><br>

两个都显示为 “118”

v-for

<div v-for="(item, key, index) in xiaoming"> {{index}} -- {{key}}: {{item}} </div>

  • 结果

0 -- $id: objectTest 1 -- $onAction: function () { [native code] } 2 -- $patch: function $patch(partialStateOrMutator) { let subscriptionMutation; isListening = false; if (true) { debuggerEvents = []; } if (typeof partialStateOrMutator === "function") { partialStateOrMutator(pinia.state.value[$id]); subscriptionMutation = { type: MutationType.patchFunction, storeId: $id, events: debuggerEvents }; } else { innerPatch(pinia.state.value[$id], partialStateOrMutator); subscriptionMutation = { type: MutationType.patchObject, payload: partialStateOrMutator, storeId: $id, events: debuggerEvents }; } isListening = true; triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); } 3 -- $reset: function $reset() { const newState = state ? state() : {}; this.$patch(($state) => { assign($state, newState); }); } 4 -- $subscribe: $subscribe(callback, options2 = {}) { if (typeof options2 === "boolean") { console.warn(`[\u{1F34D}]: store.$subscribe() no longer accepts a boolean as the 2nd parameter: Replace "store.$subscribe(fn, ${String(options2)})" with "store.$subscribe(fn, { detached: ${String(options2)} })". This will fail in production.`); options2 = { detached: options2 }; } const _removeSubscription = addSubscription(subscriptions, callback, options2.detached); const stopWatcher = scope.run(() => watch(() => pinia.state.value[$id], (state, oldState) => { if (isListening) { callback({ storeId: $id, type: MutationType.direct, events: debuggerEvents }, state); } }, assign({}, $subscribeOptions, options2))); const removeSubscription = () => { stopWatcher(); _removeSubscription(); }; return removeSubscription; } 5 -- $dispose: function $dispose() { scope.stop(); subscriptions = []; actionSubscriptions = []; pinia._s.delete($id); } 6 -- name: jyk 7 -- age: 118 8 -- info: { "a1": "11", "a2": "22" } 9 -- nameAction: function() { const _actionId = runningActionId; const trackedStore = new Proxy(store, { get(...args) { activeAction = _actionId; return Reflect.get(...args); }, set(...args) { activeAction = _actionId; return Reflect.set(...args); } }); return actions[actionName].apply(trackedStore, arguments); } 10 -- ageTest: 118 11 -- _hotUpdate: function(newStore) { originalHotUpdate.apply(this, arguments); patchActionForGrouping(store, Object.keys(newStore._hmrPayload.actions)); }

好吧,大概是我的使用方式不对。

for in

console.log('counter - for in :') for (const key in xiaoming){ console.log(key) }

  • 结果

counter - for in : pinia.vue:42 $id pinia.vue:42 $onAction pinia.vue:42 $patch pinia.vue:42 $reset pinia.vue:42 $subscribe pinia.vue:42 $dispose pinia.vue:42 name pinia.vue:42 age pinia.vue:42 info pinia.vue:42 nameAction pinia.vue:42 ageTest pinia.vue:42 _hotUpdate

  • 讨论
    没有出现 action。出现了内部设置的成员,以及数据、getter。总之和我想的不一样。
Object.keys

console.log('counter - keys :') console.log(Object.keys(xiaoming))

  • 结果

counter - keys : pinia.vue:38 (12)["$id", "$onAction", "$patch", "$reset", "$subscribe", "$dispose", "name", "age", "info", "nameAction", "ageTest", "_hotUpdate"] 我想的到底是啥样的呢?

可以使用 class + reactive 实现一个充血实体类,比如这样:

import { computed, reactive } from 'vue' // 充血实体类 class TestClass { constructor (_info) { // 设置属性,数组或者对象 this.name = 'jyk' this.age = 18 this.info = { a1: 'a1', a2: 'a2' } } // 通用赋值 $set(model, clear = false) { if (clear) { Object.keys(this).forEach(key => { delete this[key] }) } Object.assign(this, model) } actionTest() { this.age += 1 } get getterTest() { const tmp = computed(() => { return this.age + 100}) return tmp } } export default function() { const tmp = new TestClass() return reactive(tmp) }

在组件里使用:

const test2 = testClass() console.log(test2) console.log(test2.getterTest) console.log('\n 遍历 Object.keys -----') console.log(Object.keys(test2)) console.log('\n 遍历 for in -----') for (const key in test2) { console.log(key, ':', test2[key]) } console.log('\n 遍历 for in 结束 -----')

获取实例后,套上 reactive 就可以获得响应性。

这样数据部分在第一层,其他各种方法都在“原型”里面,那么在 v-for、 Object.keys 和for...in的时候,只会出现数据部分,没有各种函数了。

整体结构也很简洁。

  • 看看打印效果

遍历的情况也是挺好的。