上一篇 我们实现了状态管理仓库的 state, pinia和vuex中都有计算属性getters, 接下来我们就来实现计算对象 getters
我们都知道getters具有计算属性,依赖于其他state和 getter 值进行计算新的值
如何在store中实现这个效果,vue3中有一个cumputed api可以让我们使用
上代码:
依然是创建一个store对象,store新增一个getters对象
我们写getters的时候是一个函数,函数的返回值才是最终使用的数据
那么我们可以在useStore 第一次执行的时候利用 computed 将getters 里面的所有函数执行并返回一个新的getters对象,该对象也是具有响应式的
得到新的getters对象后我们将getters对象合并到store.state 对象上
组件中使用getters上的属性时可以直接通过 store.state[gettersNmae] 访问
const store = {
state: {
val: 0
},
getters: {
computedVal (state) {
return state.val + 1
// 或者直接通过this访问
// 为何可以通过this访问,请查看getters合并到state的操作
// 原理就是利用call函数将this对象指向了store.state 对象
// return this.val + 1
},
computedVal2 (state) {
// 直接访问 计算属性 computedVal
return this.computedVal + 1
}
},
useStore () {
const setUp = () => {
if (!Vue.isReactive(this.state)) {
// 以下的操作只需要执行一次
// 因为执行setup做响应式依赖收集的时候会在每个组件执行,访问的是同一个state,该state 对象的代理实现只需要第一次就够了
this.state = Vue.reactive(this.state)
const _this = this
// 将this.getters里面的函数取出,存到 getters 这个对象里面的值变为具有computed 计算属性的一个对象
const getters = Object.keys(_this.getters || {}).reduce(function (conmputedGetters, name) {
// 利用computed 计算this.getters里面的值, 并记录下来
conmputedGetters[name] = Vue.computed(() => {
return _this.getters[name].call(_this.state, _this.state)
})
return conmputedGetters
}, {})
// 合并 getters对象 到 this.state
Object.assign(this.state, getters)
}
}
const scope = Vue.effectScope()
scope.run(setUp)
return this.state
}
}
然后我们创建两个子组件并使用这个store的getters
<body>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.global.min.js"
integrity="sha512-rCO3ZZnxh9j/Y725Iq2Cqr2lc9fi83zVeN3PFTUosktylZsCFjD13PDbKrzKjKO/idjM4KlMQC52AsoGFTAe6A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
>script>
<div id="app">div>
<script>
// 实现一个getters
const store = {
state: {
val: 0
},
getters: {
computedVal (state) {
return state.val + 1
// 或者直接通过this访问
// 为何可以通过this访问,请查看getters合并到state的操作
// 原理就是利用call函数将this对象指向了store.state 对象
// return this.val + 1
},
computedVal2 (state) {
// 直接访问 计算属性 computedVal
return this.computedVal + 1
}
},
useStore () {
const setUp = () => {
if (!Vue.isReactive(this.state)) {
// 以下的操作只需要执行一次
// 因为执行setup做响应式依赖收集的时候会在每个组件执行,访问的是同一个state,该state 对象的代理实现只需要第一次就够了
this.state = Vue.reactive(this.state)
const _this = this
// 将this.getters里面的函数取出,存到 getters 这个对象里面的值变为具有computed 计算属性的一个对象
const getters = Object.keys(_this.getters || {}).reduce(function (conmputedGetters, name) {
// 利用computed 计算this.getters里面的值, 并记录下来
conmputedGetters[name] = Vue.computed(() => {
return _this.getters[name].call(_this.state, _this.state)
})
return conmputedGetters
}, {})
// 合并 getters对象 到 this.state
Object.assign(this.state, getters)
}
}
const scope = Vue.effectScope()
scope.run(() => {
// 执行setup函数,收集响应式依赖
return setUp()
})
return this.state
}
}
// 子组件1
const childrenOne = Vue.defineComponent({
name: 'children-one',
setup () {
// 为什么要执行useStore,因为要通过 useStore 内的setup 函数为该组件提供响应式的依赖搜集
// storeA 已经是一个包含getters的最基本的状态管理对象了
// 不过我们一般不建议直接修改store的值,后续会提供actions去帮助大家更改state的值
const storeA = store.useStore()
const add = () => {
storeA.val += 1
}
return {
add,
storeA
}
},
template: `
children-one
计算属性 computedVal: {{storeA.computedVal}}
计算属性 computedVa2l: {{storeA.computedVal2}}
`
})
// 子组件2
const childrenTwo = Vue.defineComponent({
name: 'children-two',
setup () {
const storeB = store.useStore()
const remove = () => {
storeB.val -= 1
}
return {
remove,
storeB
}
},
template: `
children-two
计算属性 computedVal: {{storeB.computedVal}}
计算属性 computedVa2l: {{storeB.computedVal2}}
`
})
const app = Vue.createApp({
name: 'app',
components: {
'children-one': childrenOne,
'children-two': childrenTwo,
},
template: `
`
})
app.mount('#app')
script>
body>
我们来看下效果:
这样一个我们就实现计算属性 getters
但是实际应用中并不推荐我们直接更改store.state的值
包括vuex中在mutions之外更改state的值也会警告
况且pinia中组件里面gettes属性的读取都是通过store.state对象直接读取的
在开发过程中 store.state.val = xxx 这样更改状态是非常不可控的
如果getters的computedVal值被这样赋值 store.state.computedVal = xxx,他将会失去计算属性和响应式
所以我们还需要一个能够修改状态的actions,这就是我在下一篇讲要讲的内容了