上一篇我们实现了状态管理仓库的 getters, 但是实际应用中并不推荐我们直接更改store.state的值,所以我们需要实现一个actions来更新我们的状态
actions 的实现原理非常简单,只需要把action暴露出去给组件使用就行,并且actions支持同步和异步
接着上代码:
依然是创建一个store对象,store新增一个actions对象
该对象只需要在useStore的时候暴露给组件使用的store.state上就行
我们在执行useStore的时候将getters、store.actions 合并到store.state 就能实现
然后我们可以直接通过store.state[actionsName] 访问对应的actions
const store = {
state: {
val: 0
},
getters: {
},
actions: {
add (value) {
this.val += value
},
remove (value) {
this.val -= value
}
},
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
}, {})
// 直接将 this.actions 合并到 this.state 上就好了, action 函数里面可以直接通过 this 访问 state 对象
Object.assign(this.state, getters, this.actions)
}
}
const scope = Vue.effectScope()
scope.run(setUp)
return this.state
}
}
然后我们创建两个子组件
不通过直接更改store.state的值去更新组件
而是通过store的 actions 去更新组件
<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>
const store = {
state: {
val: 0
},
getters: {
},
actions: {
add (value) {
this.val += value
},
remove (value) {
this.val -= value
}
},
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
}, {})
// 直接将 this.actions 合并到 this.state 上就好了, action 函数里面可以直接通过 this 访问 state 对象
Object.assign(this.state, getters, this.actions)
}
}
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.add(1)
}
return {
add,
storeA
}
},
template: `
children-one
val : {{storeA.val}}
`
})
// 子组件2
const childrenTwo = Vue.defineComponent({
name: 'children-two',
setup () {
const storeB = store.useStore()
const remove = () => {
storeB.remove(2)
}
return {
remove,
storeB
}
},
template: `
children-two
val : {{storeB.val}}
`
})
const app = Vue.createApp({
name: 'app',
components: {
'children-one': childrenOne,
'children-two': childrenTwo,
},
template: `
`
})
app.mount('#app')
script>
body>
我们来看下效果:
这样一个我们就实现了 actions
到这里一个基础的青铜版pinia基本就差不多实现了,基本实现原理就是这么多
要想达到和pinia一样的效果,比如多模块等其他优秀特性
我们还需要调整store的生成方式和数据组成等比较繁杂的内容,我们只是简单了解pinia实现原理
其他的就不深入研究了,有兴趣的小伙伴可以自己研究改造一下