vuevue前端开发那些事儿

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

2021-02-11  本文已影响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的,成员又是什么样子的呢?

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。

上一篇 下一篇

猜你喜欢

热点阅读