• 简单了解一下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>
    
    
    • 然后看看效果

    pinia的状态结构.png

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

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

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

    有图为证:

    数据变成了toRef.png

    getter 变成了 computed

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

    getter变成了computed.png

    action 和数据是一级的。

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

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

    整体结构也很简洁。

    • 看看打印效果

    class+reactive的方式.png

    遍历的情况也是挺好的。

  • 相关阅读:
    C++学习笔记13 - 浅拷贝和深拷贝
    java的for循环中遇到异常抛出后继续循环执行
    基于K7的PXI&PXIe数据处理板(Kintex-7 FMC载板)
    Go基础——基础语法
    超简单版Python打包exe文件,并修改图标,这将是你见过最容易上手的教程~
    【计算机网络笔记】路由算法之链路状态路由算法
    2022飞天技术峰会:硬之城如何基于 SAE 打造数智化电子工业互联网平台
    libigl 网格等值线
    云安全系列3:如何构建云安全策略
    序列合并
  • 原文地址:https://www.cnblogs.com/jyk/p/16054309.html