美文网首页vuevue前端开发那些事儿
Vuex4.x(二)读取state状态的方式汇总(改)

Vuex4.x(二)读取state状态的方式汇总(改)

作者: 自然框架 | 来源:发表于2021-02-11 21:06 被阅读0次

    (昨天有点匆忙写的不够细致,今天完善一下)

    为啥还来个汇总

    写本篇的时候发现个问题,获取状态的方式怎么越写越多呢?各种方式好乱呀。所以不得不用脑图整理了一下,并且思考一个问题,为啥会这么多?每种方式的适应场景又都是啥?
    看看这个图,是不是好复杂?


    组件里 获取 state.png

    茴香豆的回字有几种写法?
    要不要知道这么多呢,有意义吗?
    还是有些意义的吧,否则有可能自以为聪明的弄了一些“巧妙”的写法,最后才发现,其实vuex早就提供了。

    不建议直接改变 state(状态)

    首先官方说要用“提交 mutation”的方式来改变状态,那么这个“mutation”是啥意思呢?我查了一下字典,是“突变”的意思。
    没有用常见的“set”,而是用了这个“突变”,大概是想强调“状态的变化”吧。

    从面向对象的角度来看,state是状态管理类的内部成员,外部不应该可以直接读写,而是要通过“属性”来操作,只不过js环境嘛,有的时候有点控制不住。

    而vuex虽然说要用 mutation 来改变state,但是并没有把其他的修改方式给堵死,留了很多漏洞,但是我们还是应该遵守官方的建议,不要直接修改状态。

    好吧,其实我也不太清楚为啥不能这样做,直接读写不是很爽的事情吗?

    只是 vuex 都第四个大版本了,这么建议应该是有一定道理的,所以我们应该还是尽量遵守一下的好。

    我们先来看看读取状态的各种方式。

    state的类型

    先定义一个简单的state

      state: {
        count: 0,
        myObject: {
          time: '现在的时间:'
        },
        myArray: [1,2,2,3,4]
      },
    

    简单类型、对象和数组,比较有代表性的类型。

    这里的类型指的不是 js 的基础类型,而是说vue3提供的ref和reactive。
    因为 vuex 把 state 变成了 reactive 的形式,所以我们要先弄清楚这一点,然后才能更好的使用其带来的优势,否则岂不是白白浪费了吗。

      // 整体获取
      const allStateManage = () => {
        const store = Vuex.useStore()
    
        // 看看state的类型
        console.log('state:', store.state)
        console.log('state.count:', store.state.count)
        console.log('state.myObject:', store.state.myObject)
     
        return {
          allState
        }
      }
    
    004state的类型.png

    整个state就是reactive的,成员又是什么样子的呢?

    • 如果成员是简单类型,那么还是简单类型,并没有变成ref的,
    • 如果成员是引用类型,获取时会变成reactive的。

    setup里面的vuex的使用方式

    import { useStore } from 'vuex'
    
    setup() {
      const store = useStore()
    }
    

    这样获取store,然后就可以按照“$store”的习惯来操作了。

    直接获取 state

      // 整体获取
      const allStateManage = () => {
        const store = Vuex.useStore()
    
        // 获得整体的state
        const allState = store.state
        console.log('allState:', allState)
        console.log('================')
    
        // 定时修改 count 看响应性
        setTimeout(() => {
          // store.commit('setCount')
          // allState.count += 101 // 会直接改vuex的state
        }, 1000)
        
        return {
          allState
        }
      }
    
    005allstate.png

    获取state的成员

      // 直接获取成员
      const stateMemberManage = () => {
        const store = Vuex.useStore()
        
        // 看看state的类型
        let memberCount = store.state.count // 失去响应性
        const memberObject = store.state.myObject
        console.log('memberCount', memberCount)
        console.log('memberObject', memberObject)
        console.log('================')
    
        // 定时修改 count 看响应性
        setTimeout(() => {
          memberCount += 101 
          // const 定义的会报错,不允许赋值,常量。
          // let 定义的可以修改,但是没有相应性
        }, 1000)
        
        return {
          memberCount,
          memberObject
        }
      }
    
    006state的成员.png

    间接获取state

     // 间接获取
      const indirectManage = () => {
        const store = Vuex.useStore()
    
        // 用toRef获取 count,有相应性,可以直接修改state
        const refCount = Vue.toRef(store.state, 'count')
        // 计算属性获取count,有相应性,不可以直接修改state
        const comCount = Vue.computed(() => store.state.count)
        // 只读的对象,有相应性,浅层不可以修改,但是深层还是可以修改。
        const readonlyObject = Vue.readonly(store.state.myObject)
    
        console.log('refCount:', refCount)
        console.log('comCount:', comCount)
        console.log('readonlyObject:', readonlyObject)
        console.log('================')
    
        // 定时修改 count 看响应性
        setTimeout(() => {
          // store.commit('setCount')
          // refCount.value += 200 // 会直接改vuex的state
        }, 2000)
       
        return {
          refCount,
          comCount,
          readonlyObject
        }
      }
    
    007间接获取state的成员.png

    因为引用类型会自动变成reactive的形式,而reactive又可以直接修改state,那么就有可能误操作,导致修改state的情况,那么如何预防呢?可以使用vue提供的readonly。

    处理后返回state

      // 处理后返回
      const operationManage = () => {
        const store = Vuex.useStore()
        // 计算属性获取count
        const addCount = '' // Vue.computed(() => store.state.count++)
        const getAddCount = store.getters.getAddCount
        const comGetAddCount = Vue.computed(() => store.getters.getAddCount)
        const filterArray = store.getters.filterArray(2)
        const comFilterArray = Vue.computed(() => store.getters.filterArray(2))
    
        console.log('addCount :', addCount)
        console.log('getAddCount :', getAddCount)
        console.log('comGetAddCount :', comGetAddCount)
        console.log('filterArray :', filterArray)
        console.log('================')
      
        return {
          addCount,
          getAddCount,
          comGetAddCount,
          filterArray,
          comFilterArray
        }
      }
    
    008运算后获取.png

    封装读取方式

    看到上面这些读取方式,是不是一个头两个大?
    这也太复杂了吧,我到底应该用什么方式?
    我觉得把这个问题留给组件里面决定,这是不负责任的方式。
    我们应该做一个独立的js文件,然后在里面根据公司的要求,或者事先的约定,依据业务需求、项目需求来确定采用哪种方式。

    在js文件里面封装好之后,组件里直接调用就好,不用管其他。

    这样就好维护多了。

    我们做一个js文件

    const map = () => {
      const store = Vuex.useStore()
        
      /**
      * 获取count,
      * 用computed实现相应
      */
      const getCount = () => {
        return Vue.computed(() => store.state.count)
      }
    
      /**
      * 获取count,
      ** 用 ref 实现相应
      */
      const getRefCount = () => {
        return Vue.ref(store.state.count)
      }
    
      /**
      * 获取对象
      */
      const getObject = () => {
        return store.state.myObject
      }
    
      /**
      * 获取只读对象
      */
      const getReadonlyObject = () => {
        return Vue.readonly(store.state.myObject)
      }
    
      /**
      * 获取数组
      */
      const getArray = () => {
        return store.state.myArray
      }
      /**
      * 获取只读数组
      */
      const getReadonlyArray = () => {
        return Vue.readonly(store.state.myArray)
      }
    
      /**
      * 查询数组
      ** id:要查询的数据
      */
      const filterArray = (id) => {
        return  Vue.computed(() => store.getters.filterArray(id))
      }
    
      return {
        getCount,
        getRefCount,
        getObject,
        getReadonlyObject,
        getArray,
        getReadonlyArray,
        filterArray
      }
    }
    
    export default map
    

    这样就可以把麻烦事交给抽离处理的js来处理了,组件只需要调用就好,不用管其他的。

    setup 组合API的威力

    最后附一下setup的代码,vue3的composition的方式实在是太方便了

        setup() { // 传说中的setup
          const store = Vuex.useStore()
      
          // 状态的控制事件
          const setCount = () =>{
            store.commit('setCount')
            store.commit('setTime')
            store.commit('setArray')
            
            store._mutations.setCount[0] // 这是什么?
    
          }
    
          // 获取state
          const { allState } = allStateManage()
          // 直接获取成员
          const { memberCount, memberObject } = stateMemberManage()
          // 间接获取成员
          const { refCount, comCount, readonlyObject } = indirectManage()
          // 间接获取成员
          const { addCount, getAddCount, comGetAddCount, filterArray, comFilterArray } = operationManage()
    
          // 通过map 获取 count
          // 可以使用别名
          const {
            getCount: mapGetCount
          } = map()
    
          const mapCount = mapGetCount()
    
          return {  // 返回给模板,否则模板访问不到。
            // 直接获取state
            allState,
            // 直接获取成员
            memberCount,memberObject,
            // 间接获取
            refCount, comCount,readonlyObject,
            // 操作后获取
            addCount,getAddCount,comGetAddCount,filterArray,comFilterArray,
            // map
            mapCount,
            // 设置state
            setCount
          }
        }
    

    具体实现方式写成各种函数,可以放在单独的js文件里面。
    setup只需要引用进来就行,然后return给模板。
    这样代码就不乱了。

    可否放飞自我?

    对于简单的项目来说,应该可以把state当作大号data来做,负责组件直接的数据传递,不用管是不是状态,是不是可以直接修改的要求。

    这个总比自己去研究 Provide / Inject 要好吧,因为这个也是想利用reactive的响应性来实现需求的呀,那么state就是一个reactive,我们何必另起炉灶呢?

    另外,以后需求变化了,业务更复杂了,自己想的解决方法就要不断的改进,到最后就会发现,自己又实现了一个vuex。

    在线演示:

    https://naturefwvue.github.io/nf-vue-cnd/cnd/project-vuex/

    源码:

    https://github.com/naturefwvue/nf-vue-cnd/tree/main/cnd/project-vuex

    附录:getters

    经过几天的思考,感觉getter 是 vue2.x时代的产物,是针对vue2.x不好实现复用而设计的。

    现在是vue3.x了,getter就有点鸡肋了。

    因为在vue2.x里面,computed只能放在组件里面,不能拿到外面使用,于是vuex只好自己做了一个类似computed的东东,而且为了配合组件里面的computed,没有自己带上相应性,这样可以“方便”的放在组件的computed里面,而且还为此提供了语法糖——mapGetters

    而现在vue3可以把computed拿出来放在单独的js文件里面,这样可以直接对state进行操作(比如过滤、查询、统计数量等)。而且页自带复用性。

    这样的话,getter就没啥必要了。如果直接获取state,直接写就行,如果要进行过滤,在单独的js文件,使用computed来处理就行。

    真的想不出来在vue3环境里面,哪里还需要getter。

    所以我的封装函数里面就没有用getter,而是直接使用state。

    相关文章

      网友评论

        本文标题:Vuex4.x(二)读取state状态的方式汇总(改)

        本文链接:https://www.haomeiwen.com/subject/gpvhxltx.html