最简实现vuex系列2- dispatch / getters

前言

昨天写了 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 类型

总体来说,还是比较清晰的,也比较简单

相关推荐
极客密码7 小时前
感谢雷总!Mimo大模型价值¥659/月的 MAX 套餐,让我免费领到了!
前端·ai编程·claude
深念Y8 小时前
我明白为什么B站没法在浏览器开直播了——Windows Chrome推流踩坑全记录
前端·chrome·webrtc·浏览器·srs·直播·flv
zhangxingchao8 小时前
AI应用开发七:可以替代 RAG 的技术
前端·人工智能·后端
Sun@happy8 小时前
现代 Web 前端渗透——基础篇(1)
前端·web安全
希冀1239 小时前
【CSS学习第十一篇】
前端·css·学习
隔窗听雨眠9 小时前
doctype、charset、meta如何控制整个渲染流水线
java·服务器·前端
kyriewen9 小时前
写组件文档写到吐?我用AI自动生成Storybook,同事以后直接抄
前端·javascript·面试
excel9 小时前
🧠 Prisma 表名大写 vs SQL 导出小写问题深度解析(附踩坑与解决方案)
前端·后端
周淳APP9 小时前
【前端工程化原理通识:从源头到运行时的理论阐述】
前端·编译·打包·前端工程化
五点六六六10 小时前
你敢信这是非Native页面写出来的渐变效果吗🌝(底层原理解析
前端·javascript·面试