【Vuex 源码学习】第一篇 - Vuex 的基本使用

461 阅读4分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战


一,前言

本篇开始,进入 vuex 源码学习,本篇主要介绍一下内容:

  • 创建 vuex 源码项目;
  • 介绍 vuex 的基本使用;

二,创建 vuex 源码项目

1,使用 vue-cli 创建 vue2.x 脚手架

vue create source-learning-vuex

脚手架配置项:

Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project: 
❯◯ Choose Vue version
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

2,安装 vuex 依赖

npm install vuex

3,创建 .gitignore 文件

创建 .gitignore 文件,忽略掉不需要版本控制的文件:

// .gitignore

.DS_Store
node_modules
/dist

准备工作完成;


二,vuex 的工作流程

vuex 为项目开发提供了状态管理及数据共享能力;

在项目开发中,使用 vuex 能够将所有的数据统一存储到一个 store 容器中进行管理;

如下图所示,绿色虚线框即为 vuex 的管理范围,表示一个容器:

vuex工作流程.png

  • 在 vuex 中,进行统一的项目状态管理,可以通过 vuex 将状态映射到项目任何组件;
  • 在 vuex 中,通过 mutation 是更新状态的唯一方式;
  • 状态同步更新:通过直接将结果提交到 mutation,实现状态的同步更新;
  • 状态异步更新:通过调用 dispatch 方法,触发对应 action 执行异步操作,并将异步操作的结果提交至 mutation 实现状态的同步更新;
  • 日常项目开发中,也可以借助 vuex 的状态管理特性,实现前端的数据缓存功能;

三,vuex 的基本使用介绍

1,vuex 的核心概念

在 vuex 官方文档中,核心概念共有以下五个:

  • State:状态,可以理解为 vue 组件中的 data 数据;
  • Getter:可以理解为 vue 中的 computed 计算属性;
  • Mutation:同步函数,可以同步更改状态;
  • Action:异步函数,可以执行异步操作,配合 Mutation 实现状态的异步更新;
  • Module: vuex 的模块化,能够将臃肿复杂的 store 容器进行模块化分割,每个模块拥有独立的 state、mutation、action、getter;

2,vuex 插件配置

引入并通过Vue.use(Vuex)安装 vuex 插件,配置并导出 store 实例:

// src/store/index.js

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

// 注册 vuex 插件:内部会调用 Vuex 的 install 方法
Vue.use(Vuex);

// 实例化容器:Vuex.Store
const store = new Vuex.Store({
  // state 状态:相当于组件中的 data 数据
  state: {
    num: 10
  },
  // getters 相当于计算属性(内部实现利用了计算属性)
  getters: {
    
  },
  // 相当于 method,能够同步的更改 state
  mutations: {
    
  },
  // action作用:执行异步操作,并将结果提交给 mutations
  actions: {
    
  }
});

export default store;// 导出 store 实例,传入根组件
备注:此处为简单配置,尚未引入 Vuex 模块化及命名空间概念;

在 main.js 中导入配置完成的 store 实例;

new Vue初始化时,将 store 实例注入根组件:

// main.js

import Vue from 'vue'
import App from './App.vue'
import store from './store/index' // 引入 store 实例

new Vue({
  store,// 将 store 实例注入到 vue 中
  render: h => h(App),
}).$mount('#app');

此时,vuex 插件配置完成,可直接在页面中测试使用;

3,state 的使用

在 App.vue 中, template中直接输出 store 容器中的状态 num:

// src/App.vue

<template>
  <div id="app">
    商品数量: {{this.$store.state.num}} 个<br>
  </div>
</template>

执行 npm run serve 启动服务:

image.png

问题:this.$store.state.num是如何获取到状态的?

这里涉及 Vuex 插件初始化,与 vue-router 安装流程相似;

当执行`Vue.use(Vuex)`时,会调用 install 方法,为每个子组件都混入了当前的 store 容器实例;

new Vue 是根实例;App.vue 是根实例的子组件;

所以,new Vue 中传入的 store 实例,install 时会被混入到 App.vue 组件上;

同理,最终所有组件都将混入来自父组件的 store 实例,从而实现 store 容器实例的共享;

4,getters 的使用

getters 的内部实现依赖了 computed;

所以,可以理解为 Vue 的 computed 计算属性;

// main.js

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

// 注册 vuex 插件:内部会调用 Vuex 中的 install 方法
Vue.use(Vuex);

// 实例化容器容器:Vuex.Store
const store = new Vuex.Store({
  state: {
    num: 10
  },
  getters: {
    // 根据商品数量计算总价格
    getPrice(state) {
      return state.num * 10
    }
  },
});
export default store;

template中,使用用 getters 方法 getPrice:

// src/App.vue

<template>
  <div id="app">
    商品数量: {{this.$store.state.num}} 个<br>
    商品单价: 10 元<br>
    订单金额: {{this.$store.getters.getPrice}} 元<br>
  </div>
</template>

image.png

备注:由于 getters 具备计算属性特性,当依赖的 state 状态改变时,会触发更新;

5,mutation 的使用

在 vuex 中,通过 commit 提交 mutation 是更新状态的唯一方式;

mutation 方法:

  • 第一个参数:state 状态对象;
  • 第二个参数:payload 载荷,本次修改结果;
// src/store/index.js

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

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    num: 10
  },
  mutations: {
    // 更新 num
    changeNum(state, payload) {
      state.num += payload;
    }
  }
});
export default store;

template 中,直接调用 commit 方法提交 mutation,同步更新状态:

// src/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>
  </div>
</template>

点击同步更新按钮,触发 mutation 方法,视图更新:

image.png

6,action 的使用

组件通过调用 dispatch 触发 action 执行异步请求返回结果数据,再通过 commit 提交 mutation 完成状态更新;

在 action 方法中:

  • 可以继续调用 dispatch 触发 action;
  • 可以多次调用 commit 进行状态提交;

备注:

  • mutation 方法的目的是修改状态,所以第一个参数是 state 状态;
  • action 方法需要将结果提交到 mutation,所以第一个参数是 store(store 中包含了 commit 方法)
// src/store/index.js

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

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    num: 10
  },
  mutations: {
    changeNum(state, payload) {
      state.num += payload;
    }
  },
  // action作用:执行异步操作,并将结果提交给 mutations
  actions: {
    changeNum({ commit }, payload) {
      setTimeout(() => { // 模拟异步
        commit('changeNum', payload)
      }, 1000);
    }
  }
});

export default store;
备注:这里使用 setTimeout 模拟异步操作;

template 中,直接调用 dispatch 方法触发 action,异步更新状态:

// src/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>

点击异步更新按钮,1 秒后,视图更新:

image.png

以上,就是 vuex 几个核心概念的基本用法;


四,结尾

本篇,介绍了 vuex 的基本用法,主要包含以下几个点:

  • vuex 项目创建;
  • vuex 工作流程介绍;
  • vuex 的基本使用介绍;

下一篇,介绍 vuex 的 install 插件安装逻辑;


维护日志

  • 20210918:

    • 优化文章描述,添加了部分代码的意图与注释;