VUEX 3.x源码分析——3. 理解Mutations

这是对vuex3.x版本的源码分析。
本次分析会按以下方法进行:

    按官网的使用文档顺序,围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码,更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升,甚至面试时的回答都非常有帮助。

    在围绕某个功能展开讲解时,所有不相干的内容都会暂时去掉,等后续涉及到对应的功能时再加上。这样最大的好处就是能循序渐进地学习,同时也不会被不相干的内容影响。省略的内容都会在代码中以…表示。

    每段代码的开头都会说明它所在的文件目录,方便定位和查阅。如果一个函数内容有多个函数引用,这些都会放在同一个代码块中进行分析,不同路径的内容会在其头部加上所在的文件目录。

本章只讲解vuex中的Mutations,这也是vuex官网中“核心概念”的第三个。

初始化

  • 在 installModule 函数中,将每个定义的mutation按key-value遍历,存入store._mutations中.

  • 注意这里的每个store._mutations[k]都是一个数组,数组中的元素是一个函数(wrappedMutationHandler,接收payload参数),此函数负责执行mutation。

// store-util.js export function installModule(store, rootState, path, module, hot) { ... // forEachMutation方法和getter中的forEachGetter类似 module.forEachMutation((mutation, key) => { const namespacedType = namespace + key registerMutation(store, namespacedType, mutation, local) }) } function registerMutation(store, type, handler, local) { const entry = store._mutations[type] || (store._mutations[type] = []) //短路求值 entry.push(function wrappedMutationHandler(payload) { handler.call(store, local.state, payload) }) }

commit的调用

  • 在Store类上,定义了commit方法。

  • 调用commit方法后,按传入的参数_type从store._mutations[type]中取出所有的执行函数

  • 调用_withCommit方法依次执行mutation函数

// store.js export class Store { ... commit(_type, _payload, _options) { const {type, payload, options} = unifyObjectStyle(_type, _payload, _options) const mutation = {type, payload} const entry = this._mutations[type] if (!entry) {...} // _withCommit 是添加了额外的逻辑,这里先不管具体细节,等于执行entry.forEach this._withCommit(() => { entry.forEach(function commitItertor(handler) { handler(payload) }) }) } } // store-util.js export function unifyObjectStyle(type, payload, options) { if (isObject(type) && type.type) { options = payload payload = type type = type.type } if (__DEV__) {assert(...)} return {type, payload, options} // 给外层赋值解构用 }

至此,可以对官网中的所有内容做相关解释:

    为什么mutation有两种提交方式:
    unifyObjectStyle 函数处理了两种提交逻辑

store.commit({ type: 'increment', amount: 10 }) mutations: { increment (state, payload) { state.count += payload.amount } } // store-util.js export function unifyObjectStyle(type, payload, options) { if (isObject(type) && type.type) { options = payload payload = type type = type.type } if (__DEV__) {assert(...)} return {type, payload, options} // 给外层赋值解构用 }

    为什么mutation必须是同步函数
    mutation在内部是没有针对异步函数做特殊处理,如常见的async-awailt promise等。

    为什么只能使用commit的形式提交mutation,而不是直接调用mutation来提交:
    store初始化时没有mutation属性,只有一个_mutations数据结构,这个数据并不执行mutation,只是存储相关的函数。

另外,我们还能通过源码发现一些官网没有写出,但实际存在的功能:

  • 在执行mutation时制定了上下文为store,所以必然可以使用getter和其他内容,

// store-util.js function registerMutation(store, type, handler, local) { const entry = store._mutations[type] || (store._mutations[type] = []) //短路求值 entry.push(function wrappedMutationHandler(payload) { // hander 就是我们定义的某个mutation,它使用了call指定上下文为store handler.call(store, local.state, payload) }) }

如何开发一个mutation的订阅插件

  • 在执行commit时,将对应的mutation执行完毕后,还执行了相关的订阅函数。

  • store定义了subscribe方法,方便用户来订阅某些内容,执行一些额外的操作,比如日志记录、时间旅行调试等。

  • genericSubscribe 函数主要有两个功能:1. 将用户定义的额外操作(即fn)加入到this._subscribers中。2.返回一个函数,这个函数的作用是取消订阅,即将fn从this._subscribers删除。

// ./store.js export class Store { ... constructor(options = {}) { this._subscribers = [] } commit(_type, _payload, _options) { const {type, payload, options} = unifyObjectStyle(_type, _payload, _options) const mutation = {type, payload} const entry = this._mutations[type] if (!entry) {...} this._withCommit(() => { entry.forEach(function commitItertor(handler) { handler(payload) }) }) this._subscribers.slice().forEach(sub => sub(mutation, this.state)) } subscribe(fn, options) { return genericSubscribe(fn, this._subscribers, options) } } // store-util.js export function genericSubscribe (fn, subs, options) { if (subs.indexOf(fn) < 0) { options && options.prepend ? subs.unshift(fn) : subs.push(fn) } return () => { const i = subs.indexOf(fn) if (i > -1) { subs.splice(i, 1) } } }

  • 了解了上述源码后,就可以开发一个订阅mutation的插件

  • 这里也解释了为什么vuex不建议直接修改state的内容,而是通过mutation来修改。因为vuex有订阅的功能,方便用户开发额外功能或者测试调试。而如果指定修改state则不会触发这些功能。

const createLoggingPlugin = (mutationType) => { return (store) => { store.subscribe((mutation, state) => { if (mutation.type === mutationType) { console.log(111); } }) } } // 使用插件 const store = new Vuex.Store({ ... plugins: [createLoggingPlugin('increment')] })

以上就是官网上Mutation的相关源码,下一遍会分析Mutations的实现源码。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
zest
作者分享
最近业务需要,计划招多名短期兼职,时长半个月左右,做数据标注任务。 要求理解标注规则,(修改一下)按数量付钱,多劳多得,但是必须在某个规定时间内完成,需要有充足的时间。最好有标注经验。 可远程,有需要的结束后可开实习证明。 感兴趣的加我微信详谈。
7
有没有学习 Python 的鱼友,有用 Python 开发鱼皮项目的经验。 boss 上看了一堆都没匹配上,在这里随缘发一发。 应届或者毕业一年以内,计算机基础扎实,刷过题,有项目经验。 base 深圳南山区,薪资 15k-20k。 除了后端,还能接触到前端和大模型的方向,对职业发展非常有帮助。 感兴趣并且符合要求的加我微信 15773283322
5
新年伊始,招聘一名全栈(偏前端)工程师
5
站在面试官的角度,分析如何更好地应对面试
15
我又来招人了~ 这次是招一名实习生,做 AI 大模型 agent 应用开发。 基于上次招人的情况,我这里做了一些规定和要求,希望有意向的小伙伴能认真看完下面的内容并考虑清楚后再来投递简历。 岗位名称:大模型应用开发工程师 地点:深圳南山科技园 时间:至少实习3个月,周末双休 年限要求:应届实习 岗位要求: 1. 计算机相关专业,最好是大四或研二; 2. 熟悉 Python 语言,具有扎实的数据结构和算法功底; 3. 对基于AI大模型的应用开发感兴趣; 4. 时间比较充裕,能到公司坐班; 5. 良好的沟通能力、学习能力、和团队协作精神; 工作内容: 1. 基于大模型的 agent 应用开发; 2. 基于场景对 Prompt 进行设计和测评,提升大模型效果和稳定性; 3. 根据公司业务需求,与开发团队、产品运营团队紧密配置,持续改进应用; 感兴趣的小伙伴可以添加我微信 15773283322 --------------------------------------------------------------- 随便说一下,上次发布的招聘,也成功地在编程导航上找到了满意的候选人,目前已经入职半个月多了。 上次招人收到了很多人的简历,但是就简历质量而言,真的是一言难尽。 这周末我找时间输出一篇站在面试官的角度看待招聘过程的感受,写一些对焦虑难找工作的人的一些建议。 这里提一句就是真的要好好准备简历啊!
9
下载 APP