【手写 Vuex 源码】第五篇 - Vuex 中 Mutations 和 Actions 的实现

news2024/9/27 20:16:29

一,前言

上一篇,主要介绍了 Vuex 中 getters 的实现,主要涉及以下几个点:

  • 将选项中的 getters 方法,保存到 store 实例中的 getters 对象中;
  • 借助 Vue 原生 computed,实现 Vuex 中 getters 的数据缓存功能;

本篇,继续介绍 Vuex 中 Mutations 的实现;


二,前文回顾

在前面介绍了 Vuex 的基本使用:

mutation:

  • 同步更新 state 状态;
  • 通过 $store.commit(type, payload)调用 mutations 中对应的方法;
    action:
  • 异步更新 state 状态;
  • 通过 $store.dispatch(type, payload)调用 actions 中对应的方法;
  • 在 action 方法中,可以进行异步请求操作;
  • 在 action 方法中,可以继续执行 dispatch 方法,调用其他 action 方法;
  • 在 action 方法中,可以多次调用 commit 方法,执行状态更新操作;
// App.vue

<template>
  <div id="app">
    商品数量: {{this.$store.state.num}} 个<br>
    商品单价: 10 元<br>
    订单金额: {{this.$store.getters.getPrice}} 元<br>
    <button @click="$store.commit('changeNum',5)">同步更新:数量+5</button>
    <button @click="$store.dispatch('changeNum',-5)">异步更新:数量-5</button>
  </div>
</template>

在 Store 容器中,mutations 和 actions 相关配置如下:

// src/store/index.js

import Vue from 'vue';
import Vuex from '@/vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    num: 10
  },
  getters: {
    getPrice(state) {
      return state.num * 10
    }
  },
  // 同步更新状态
  mutations: {
    changeNum(state, payload) {
      state.num += payload;
    }
  },
  // 可执行异步操作,通过 mutations 更新状态
  actions: {
    changeNum({ commit }, payload) {
      setTimeout(() => {
        commit('changeNum', payload)
      }, 1000);
    }
  }
});
export default store;

三,Mutations 的实现

1,将 options.mutations 中定义的方法,绑定到 store 实例中的 mutations 对象

// src/vuex/store.js

export class Store {
  constructor(options) { // options:{state, mutation, actions}
    // 声明 store 实例中的 mutations 对象
    this.mutations = {};
    // 获取 options 选项中的 mutations 对象
    const mutations = options.mutations;

    // 将 options.mutations 中定义的方法,绑定到 store 实例中的 mutations 对象
    Object.keys(mutations).forEach(key => {
      // payload:commit 方法中调用 store 实例中的 mutations 方法时传入
      this.mutations[key] = (payload) => mutations[key](this.state, payload);
    });
  }
}

2,创建并实现 commit 方法

1)用户可以在页面执行调用 store 实例中的 commit 方法:

<button @click="$store.commit('changeNum',5)">同步更新:数量+5</button>

2)也可以在 action 方法中,解构 store 实例中的 commit 方法:

// src/store/index.js

  actions: {
    changeNum({ commit }, payload) {
      setTimeout(() => {
        commit('changeNum', payload)
      }, 1000);
    }
  }

基于以上两种使用场景,创建 commit 方法,为了确保 this 指向 store 实例,commit 方法使用箭头函数:

// src/vuex/store.js

  /**
   * 通过 type 找到 store 实例的 mutations 对象中对应的方法,并执行
   *    用户可能会解构使用{ commit }, 也有可能在页面使用 $store.commit,
   *    所以,在实际执行时,this是不确定的,{ commit } 写法 this 为空,
   *    使用箭头函数:确保 this 指向 store 实例;
   * @param {*} type mutation 方法名
   * @param {*} payload 载荷:值或对象
   */
  commit = (type, payload) => {
    // 执行 mutations 对象中对应的方法,并传入 payload 执行
    this.mutations[type](payload)
  }

当调用 commit 方法时,会根据传入的 type 值(即选项中定义的 mutation 方法名)到 store 实例中的 mutations 对象找到对应的方法(即选项中定义的 mutation 方法),传入 state 和 payload 执行此方法,实现状态的同步更新;

// src/store/index.js

new Vuex.Store({
  ...
  mutations: {
    changeNum(state, payload) {
      console.log(`进入 mutations-changeNum:state = ${JSON.stringify(state)}, payload = ${payload}`)
      state.num += payload;
    }
  }
  ...
});

测试同步更新,查看控制台输出:

image.png

打印 log,执行 mutation 方法;

同时,响应式数据的变化,触发了依赖收集中相关视图的更新渲染;


四,Actions 的实现

对比 dispatch 和 commit 两个方法,可以看出,参数是一致的,实现方法也大致相同:

<button @click="$store.commit('changeNum',5)">同步更新:数量+5</button>
<button @click="$store.dispatch('changeNum',-5)">异步更新:数量-5</button>

1,将 options.actions 中定义的方法,绑定到 store 实例中的 actions 对象

这部分逻辑和 Mutations 是完全一致的

// src/vuex/store.js

export class Store {
  constructor(options) {
    // 声明 store 实例中的 actions 对象
    this.actions = {};
    // 获取 options 选项中的 actions 对象
    const actions = options.actions;
    
    // 将 options.actions 中定义的方法,绑定到 store 实例中的 actions 对象
    Object.keys(actions).forEach(key => {
      // payload:dispatch 方法中调用 store 实例中的 actions 方法时传入
      this.actions[key] = (payload) => actions[key](this, payload);
    });
  }

2,创建并实现 dispatch 方法

1)用户可以在页面执行调用 store 实例中的 dispatch 方法:

<button @click="$store.dispatch('changeNum',-5)">异步更新:数量-5</button>

2)也可以在 action 方法中,解构 store 实例中的 dispatch 方法:

// src/store/index.js

  actions: {
    changeNum({ commit, dispatch }, payload) {
      setTimeout(() => {
        commit('changeNum', payload)
      }, 1000);
    }
  }

基于以上两种使用场景,创建 dispatch 方法,为了确保 this 指向 store 实例,dispatch 方法使用箭头函数:

// src/vuex/store.js

  /**
   * 通过 type 找到 store 实例的 actions 对象中对应的方法,并执行
   *    用户可能会解构使用{ dispatch }, 也有可能在页面使用 $store.dispatch,
   *    所以,在实际执行时,this 是不确定的,{ dispatch } 写法 this 为空,
   *    使用箭头函数:确保 this 指向 store 实例;
   * @param {*} type action 方法名
   * @param {*} payload 载荷:值或对象
   */
  dispatch = (type, payload) => {
    // 执行 actions 对象中对应的方法,并传入 payload 执行
    this.actions[type](payload)
  }

当调用 dispatch 方法时,会根据传入的 type 值(即选项中定义的 action 方法名)到 store 实例中的 actions 对象找到对应的方法(即选项中定义的 action 方法),传入 store实例 和 payload 执行此方法,实现状态的同步更新;

同时,在 action方法中可以再次执行 dispatch 方法执行其他异步操作,也可以多次执行 commit 执行状态的更新操作;

// src/store/index.js

new Vuex.Store({
  ...
  actions: {
    changeNum({ commit }, payload) {
      console.log(`进入 actions-changeNum:commit = ${JSON.stringify(commit)}, payload = ${payload}`)
      setTimeout(() => { // 模拟异步
        commit('changeNum', payload)
      }, 1000);
    }
  }
  ...
});

测试异步更新,查看控制台输出:

image.png

打印 log,先执行 action 方法,再通过 commit 执行 mutation 方法;

同时,响应式数据的变化,触发了依赖收集中相关视图的更新渲染;


五,对比 Mutations 和 Actions

通过以上 Mutations 和 Actions 实现可以看出,不管是同步还是异步更新 State 状态,做种都必须通过 commit 调用 mutation 方法,完成状态的更新操作;

这也使得 Vuex 状态的变更能够被 devtools 所跟踪和记录,实现了状态的统一管理;

vuexLogo.png

再次强调,只能通过 mutation 方法才可以更新 Vuex 中的状态;


六,结尾

本篇,主要介绍了 Vuex 中 Mutations 和 Actions 的实现,主要涉及以下几个点:

  • 将 options 选项中定义的 mutation 方法绑定到 store 实例的 mutations 对象;
  • 创建并实现 commit 方法;
  • 将 options 选项中定义的 action 方法绑定到 store 实例的 actions 对象;
  • 创建并实现 dispatch 方法;

至此,一个简易版的 Vuex 状态管理插件就完成了;

8 月更文的 31 篇文章就算水完了;9 月开始 Vue2.x 源码的第二轮学习-文章优化;Vuex 下半部分将暂时搁置一段时间;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/339659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有趣的Hack-A-Sat黑掉卫星挑战赛——跟踪卫星

国家太空安全是国家安全在空间领域的表现。随着太空技术在政治、经济、军事、文化等各个领域的应用不断增加&#xff0c;太空已经成为国家赖以生存与发展的命脉之一&#xff0c;凝聚着巨大的国家利益&#xff0c;太空安全的重要性日益凸显[1]。而在信息化时代&#xff0c;太空安…

TMC2660驱动及调试记录

TMC2660 一款优秀的电机驱动芯片&#xff0c;驱动简单。 理论就看这篇&#xff1a;TMC260/TMC2660/TMC262步进电机驱动 或者直接看手册&#xff0c;手册也不复杂。 使用SPI通信&#xff0c;通过SPI配置参数。支持直接使用SPI和Step/Dir方式控制两种控制步进电机的方式。 TMC…

【C语言】数据结构-二叉树

主页&#xff1a;114514的代码大冒险 qq:2188956112&#xff08;欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ &#xff09; Gitee&#xff1a;庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com 引入 我们之前已经学过线性数据结构&#xff0c;今天我们将介绍非线性数据结构----树 树是一种非线性的…

面试官问我:说说你对Spring MVC的理解

文章目录什么是MVC模式MVC的原理图2.1 分析工作原理3.1 组件说明&#xff1a;3.1.1 组件&#xff1a;核心架构的具体流程步骤什么是MVC模式 MVC&#xff1a;MVC是一种设计模式 MVC的原理图 2.1 分析 M-Model 模型&#xff08;完成业务逻辑&#xff1a;有javaBean构成&#x…

C++11的异步操作让多线程开发变得简单

C11的异步操作简介一、std::future1.1、future的类型1.2、future的使用1.3、使用示例二、std::packaged_task三、std::promise总结简介 C提供如下的异步操作接口&#xff1a; std::future &#xff1a;异步指向某个任务&#xff0c;然后通过future特性去获取任务函数的返回结…

1. RNN神经网络初探

目录1. 神经网络与未来智能2. 回顾数据维度和神经网络3. 文本转变为词向量1. 神经网络与未来智能 2. 回顾数据维度和神经网络 循环神经网络&#xff0c;主要用来处理时序的数据&#xff0c;它对每个词的顺序是有要求的。 循环神经网络如何保存记忆功能&#xff1f; 当前样本只…

Window10开放某个端口

需求&#xff1a;由于防火墙原因&#xff0c;开放某个端口:如9999 在开始那里搜索防火墙-进入防火墙 第一步&#xff1a;核实是否启动了防火墙&#xff0c;之后进行 第二步&#xff1a;点击“高级设置”&#xff0c;→“入站规则”→“新建规则”→“端口”→ “下一步” …

【前端领域】3D旋转超美相册(HTML+CSS)

世界上总有一半人不理解另一半人的快乐。 ——《爱玛》 目录 一、前言 二、本期作品介绍 3D旋转相册 三、效果展示 四、详细介绍 五、编码实现 index.html style.css img 六、获取源码 公众号获取源码 获取源码&#xff1f;私信&#xff1f;关注&#xff1f;点赞&…

基于微信小程序的游戏账号交易小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

【C++初阶】十二、STL---反向迭代器的实现

目录 一、反向迭代器 二、反向迭代器的实现 一、反向迭代器 之前的模拟实现vector、list 的时候&#xff0c;这些都是实现了正向迭代器&#xff0c;反向迭代器都没有实现&#xff0c;这里就要实现反向迭代器 反向迭代器也是适配器&#xff08;配接器&#xff09;的一种&#…

在阿里干了2年的测试,总结出来的划水经验

测试新人 我的职业生涯开始和大多数测试人一样&#xff0c;开始接触都是纯功能界面测试。那时候在一家电商公司做测试&#xff0c;做了一段时间&#xff0c;熟悉产品的业务流程以及熟练测试工作流程规范之后&#xff0c;效率提高了&#xff0c;工作比较轻松&#xff0c;这样我…

代码随想录第55天(动态规划):● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费

一、最佳买卖股票时机含冷冻期 题目描述: 思路和想法&#xff1a; 这道题相较于之前的题目&#xff0c;注重对状态的分析&#xff0c;这里分为四个状态。 &#xff08;1&#xff09;状态一&#xff0c;买入状态 dp[i][0] 操作一&#xff1a;前一天就是持有状态&#xff08;状…

day39【代码随想录】动态规划之

文章目录前言一、不同路径&#xff08;力扣62&#xff09;二、不同路径||&#xff08;力扣63&#xff09;三、最小路径和&#xff08;力扣64&#xff09;前言 1、不同路径 2、不同路径|| 3、最小路径和 一、不同路径&#xff08;力扣62&#xff09; 一个机器人位于一个 m x n…

MyBatis-Plus分页插件和MyBatisX插件

MyBatis-Plus分页插件和MyBatisX插件六、插件1、分页插件a>添加配置类b>测试八、代码生成器1、引入依赖2、快速生成十、MyBatisX插件1、新建spring boot工程a>引入依赖b>配置application.ymlc>连接MySQL数据库d>MybatisX逆向生成2、MyBatisX快速生成CRUD申明…

学习open62541 --- [74] 软链接的妙用

一 原理 在同一台电脑里&#xff0c;可能会有多个工程同时用到open62541&#xff0c;比较简单的办法是每个工程都拷贝一份open62541源码&#xff0c;但是这样会造成空间浪费&#xff0c;而且open62541的源码包本身也不小。 对于Linux用户来说&#xff0c;可以使用软链接来解决…

【Python学习笔记】28.Python3 错误和异常

前言 作为 Python 初学者&#xff0c;在刚学习 Python 编程时&#xff0c;经常会看到一些报错信息&#xff0c;在前面我们没有提及&#xff0c;这章节我们会专门介绍。 Python3 错误和异常 Python 有两种错误很容易辨认&#xff1a;语法错误和异常。 Python assert&#xf…

德国奔驰、博世和保时捷的员工年薪有多少?

点击 欧盟IT那些事 关注我们公告&#xff1a;因企鹅审核规定&#xff0c;本公众号从《德国IT那些事》更名为《欧盟IT那些事》。从职场新人到总裁&#xff0c;一个个盘。位于德国斯图加特的梅赛德斯-奔驰集团及其子公司梅赛德斯-奔驰是世界最知名的汽车制造商之一。奔驰车代表着…

大数据之HBase高级

文章目录前言一、HBase的架构&#xff08;一&#xff09;Client&#xff08;二&#xff09;Master Server&#xff08;三&#xff09;Region Server二、HBase的工作原理&#xff08;一&#xff09;存储数据流程&#xff08;二&#xff09;读取数据流程&#xff08;三&#xff0…

《计算机组成与设计》01. 计算机抽象及相关技术

文章目录计算机体系结构中的 8 个伟大思想面向摩尔定律的设计使用抽象简化设计加速经常性事件通过并行提高性能通过流水线提高性能存储层次通过冗余提高可靠性性能性能的度量时钟周期数和时钟周期长度与CPU时间的公式指令性能公式经典的 CPU 性能公式CPI 计算公式程序执行时间计…

微前端基础

一、什么是微前端 微前端是一种软件架构&#xff0c;可以将前端应用拆解成一些更小的能够独立开发部署的微型应用&#xff0c;然后再将这些微应用进行组合使其成为整体应用的架构模式。微前端架构类似于组件架构&#xff0c;但不同的是&#xff0c;组件不能独立构建和发布&…