简单了解一下pinia的结构

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

随着 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>
复制代码


  • 然后看看效果


image.png


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


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



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


有图为证:


image.png

getter 变成了 computed



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


image.png


action 和数据是一级的。



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


image.png


状态是否需要遍历?



为啥这么在意 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


{{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的时候,只会出现数据部分,没有各种函数了。


整体结构也很简洁。


  • 看看打印效果


image.png


遍历的情况也是挺好的。


相关文章
|
6月前
|
存储 JavaScript 开发者
Pinia和Vuex的区别
Pinia和Vuex的区别
1596 0
|
6月前
|
存储 JavaScript 前端开发
vue3使用pinia 实现权限code存取
在本文我们一开始了解了pinia是什么以及如果安装使用。接着结合使用pinia实现菜单权限控制的综合实例,来进一步了解pinia的使用。
124 0
|
存储 资源调度 JavaScript
在Vue 3项目中引入Pinia进行状态管理并持久化存储
在Vue 3项目中引入Pinia进行状态管理并持久化存储
461 0
|
24天前
|
存储 JavaScript API
Vuex 和 Pinia 的区别
【10月更文挑战第18天】Vuex 和 Pinia 都有各自的优势和适用场景。Vuex 适合较为大型和复杂的项目,强调严格的架构和流程;而 Pinia 则更适合中小型项目以及对灵活性和简洁性有更高要求的开发者。你可以根据项目的具体需求和个人喜好来选择使用哪一个状态管理库。
460 59
|
24天前
|
存储 JavaScript
vue——store全局存储
【10月更文挑战第18天】Vuex 是 Vue.js 应用中非常重要的一个工具,它为我们提供了一种有效的状态管理方式。通过合理地使用 Vuex,我们可以更好地组织和管理应用的状态,提高应用的开发效率和质量。
47 1
|
30天前
|
存储 JavaScript 前端开发
《进阶篇第8章:vuex》包括理解vuex、安装vuex、搭建vuex环境、四个map方法的使用、模块化+名命空间
《进阶篇第8章:vuex》包括理解vuex、安装vuex、搭建vuex环境、四个map方法的使用、模块化+名命空间
17 3
|
5月前
|
存储 前端开发 数据库
|
5月前
|
前端开发
|
3月前
|
存储 JavaScript API
从 Vuex 到 Pinia:解决 Pinia 缺失的三个基本功能
从 Vuex 到 Pinia:解决 Pinia 缺失的三个基本功能
|
3月前
|
Web App开发 JavaScript 安全
Vue状态管理库Pinia详解
Pinia 是一款专为 Vue 设计的状态管理库,它提供了一套简洁且直观的 API,旨在简化状态管理流程。Pinia 的设计理念强调简单性和易用性,相较于 Vuex,它摒弃了许多复杂的概念如 mutations 和模块的深层嵌套结构,转而提供一种更现代化且与 Vue 3 Composition API 高度兼容的状态管理模式。