前言
昨天写了 commit/state
,今天来看看 dispatch / getter
getter
有时候我们需要从 state 中得出派生状态,类似于 vue
中的 computed
属性,其实 vuex 也是利用了 computed
比如下面的这个例子:
ts
createStore ({
state:{
age:10
},
getters:{
calAge(state){
//通过 state 的值 获取新的状态
return state.age + 10
}
}
})
使用 _wrappedGetters
包装 getters
ts
class Store<T> {
constructor(options) {
// getter 集合
this._wrappedGetters = Object.create(null)
const store = this
Object.entries(options.getters).forEach(([key, fnValue]) => {
this.registerGetter(store, key, fnValue)
})
}
registerGetter(store: Store<T>, key, handler) {
store._wrappedGetters[key] = function wrappedGetter(store) {
// 不仅可以传递 state,也可以传递 getter
return handler(store.state, store.getters)
}
}
}
store.util.js
最后 _wrappedGetters
形如
js
{
calAge:
function wrappedGetter(store){
return [calAgefn](store.state,store.getters)
}
}
然后执行
ts
function partial(fn, arg) {
return function () {
return fn(arg)
}
}
class Store<T> {
constructor(options) {
Object.entries(options.getters).forEach(([key, fnValue]) => {
this.registerGetter(store, key, fnValue)
})
this.resetStoreState(store)
}
resetStoreState(store){
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computedObj = {}
const computedCache = {}
Object.entries(wrappedGetters).forEach(([key,fn])=>{
computedObj[key] = partial(fn, store)
computedCache[key] = computed(() => computedObj[key]())
Object.defineProperty(store.getters, key, {
get: () => computedCache[key].value,
enumerable: true
})
})
}
}
使用 resetStoreState
进行缓存
这样做有几点好处
- 计算函数fn被缓存在computedObj中,避免每次获取计算属性时都重新创建函数。
- 计算属性被缓存在computedCache中,Vue框架会自动缓存计算属性的计算结果。
- 通过computedObj和computedCache分离函数定义和属性读取,实现了计算函数的复用和结果缓存 最后通过
Object.defineProperty
定义 get 属性,实现了 getter的 可读
dispatch
dispatch
可以触发 在 actions
定义的方法,在 actions
中可以触发 commit/dispatch
,也可以触发异步方法
ts
class Store<T> {
constructor(options) {
this._actions = Object.create(null)
Object.entries(options.actions).forEach(([fnName, fnValue]) => {
this.registerAction(store, fnName, fnValue)
})
}
registerAction(store, type, handler,){
const entry = store._actions[type] || (store._actions[type] = []);
entry.push(function wrappedActionHandler (payload) {
let res = handler.call(store, {
dispatch: store.dispatch,
commit: store.commit,
getters: store.getters,
state: store.state,
}, payload)
if(!res || typeof res.then !== "function"){
res = Promise.resolve(res)
}
})
}
}
store-util.js
这个 dispatch
比较简单
ts
dispatch(type, payload){
const entry = this._actions[type]
if (!entry) {
return
}
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
return new Promise((resolve) => {
result.then(res => {
resolve(res)
})
})
}
类型
ts
type StoreOtpions<T> = {
state?: T
mutations?: Record<string, (s: T, payload: any) => void>,
actions?: Record<string, (s: Store<T>,payload?:any) => void>,
getters?: Record<string, (s: T, getters: StoreOtpions<T>["getters"]) => void>
}
ts 也能推导出 state 类型
总体来说,还是比较清晰的,也比较简单