(昨天有点匆忙写的不够细致,今天完善一下)
为啥还来个汇总
写本篇的时候发现个问题,获取状态的方式怎么越写越多呢?各种方式好乱呀。所以不得不用脑图整理了一下,并且思考一个问题,为啥会这么多?每种方式的适应场景又都是啥?
看看这个图,是不是好复杂?
组件里 获取 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。
网友评论