【Vuex 源码学习】第二篇 - vuex 插件安装 install 逻辑

350 阅读1分钟

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


一,前言

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

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

本篇,继续介绍 vuex 插件安装 install 逻辑;


二,vuex 插件安装逻辑的实现

1,前文回顾

上一篇,在介绍 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({
  ...
});

// 导出 store 实例,传入根组件
export default store; 

new Vue初始化时,将 store 容器实例注入到 vue 根实例中:

// src/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 的基本使用:

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

从 Vuex 的使用方式进行简单分析:

  • Vuex 是一个对象;
  • Vuex 中包含了一个 Store 类;
  • Vuex 内部有一个 install 安装方法,当执行Vue.use(Vuex)时被执行;
  • Vuex 与 VueRouter 相似,插件安装时向所有组件混入(共享)store 容器实例;

2,创建 vuex 插件

基于以上 Vuex 插件特点,创建 vuex 目录和入口文件 src/vuex/index.js,其中包含 Store 类和 install 方法并导出:

// src/vuex/index.js

// 容器的初始化
class Store {

}

// 插件安装逻辑:当Vue.use(Vuex)时执行
const install =()=>{

}

export default {
    Store,
    install
}

3,模块化设计

  • Store 类的作用:容器的初始化,产生一个 store 容器实例;
  • install 方法的作用:插件安装逻辑;当Vue.use(Vuex)时被调用;
  • src/vuex/index.js 文件的作用:作为 vuex 插件入口,导出方法供外部调用;

Vuex 的模块化设计:将 Store 类、install 方法进行抽离,使 index.js 入口仅做方法的聚合并向外导出:

所以,新建 src/vuex/store.js,用于创建并实现 Store 类、install 方法并向外导出:

// src/vuex/store.js

// 容器的初始化
export class Store {

}

// 插件安装逻辑:当Vue.use(Vuex)时执行
export const install =()=>{

}

在 src/vuex/index.js 入口文件中,进行能力聚合并继续向插件外部的应用层导出:

// src/vuex/index.js

import { Store, install } from './store';

export default {
    Store,
    install
}

这样做的好处是:vuex 插件提供的能力可统一通过此方式对外导出,如:mapState 等方法;

4,install 插件安装逻辑

vuex 与 vue-router 的插件安装逻辑相似;

当执行Vue.use(Vuex)时,install 方法中会传入 Vue 构造函数,使 vuex 插件依赖项目使用的 Vue 版本:

// src/vuex/store.js

// 导出传入的 Vue 的构造函数,供插件内部的其他文件使用
export let Vue;

/**
 * 插件安装逻辑:当Vue.use(Vuex)时执行
 * @param {*} _Vue Vue 的构造函数
 */
export const install = (_Vue) => {
  Vue = _Vue;
}

同时,在new Vue初始化(根组件)时,注入的 store 容器实例,混入到所有组件中,为所有组件添加 store 属性:

创建 src/vuex/mixin.js,使用 Vue.mixin 实现全局混入:

// src/vuex/mixin.js

/**
 * 将根组件中注入store实例,混入到所有子组件上
 * @param {*} Vue 
 */
export default function applyMixin(Vue) {
  // 通过 beforeCreate 生命周期,在组件创建前,实现全局混入
  Vue.mixin({
    beforeCreate: vuexInit,// vuexInit 为初始化混入逻辑
  });
}

在 src/vuex/store.js 中引入 applyMixin 方法,在 install 方法中调用:

// src/vuex/store.js

import applyMixin from "./mixin";

export const install = (_Vue) => {
  Vue = _Vue;
  applyMixin(Vue);
}

组件混入(共享)store 实例的原理:

组件的渲染是先渲染父组件再渲染子组件,所以,根组件中注入的 store 实例,就可以被混入到 App.vue 组件上。

同理,逐层地,由父组件向子组件传递 store 实例,实现所有组件共享 store 容器实例;

function vuexInit() {
  const options = this.$options;
  // 若选项中存在 store 属性,说明是根实例;否则是子实例;
  if (options.store) {// 根实例
    // 为根实例添加 $store 属性,指向 store 实例
    this.$store = options.store;
  } else if (options.parent && options.parent.$store) { // 子实例
    // 儿子通过父亲拿到 $store 属性,放到自己身上继续提供给儿子
    this.$store = options.parent.$store;
  }
}

5,测试混入效果

使用新建的 vuex 作为状态管理插件:

// src/store/index.js

import Vue from 'vue';
// import Vuex from 'vuex';   // vuex 官方插件
import Vuex from '@/vuex';    // 自建 Vuex 插件

在 App.vue 中,打印组件上的 $store 属性,查看是否成功混入了来自根组件的 store 实例:

// src/App.vue

<script>
export default {
  mounted(){
    console.log(this.$store);
  }
}
</script>

执行 npm run serve 启动服务:

输出结果:Store {}

打印结果是一个 Store 对象,说明属性混入成功!


三,结尾

本篇,介绍了 vuex 的 install 插件安装逻辑,包含以下几个点:

  • 创建 vuex 插件目录;
  • Vuex 插件的模块化设计;
  • 实现插件安装 install 方法,store 实例混入逻辑;
  • 混入效果测试;

下一篇,介绍 Vuex 中 State 状态的实现;


维护日志

  • 20210918:
    • 调整了部分措辞与代码注释;
    • 优化了流程相关的描述;
    • 针对 store 实例共享与组件属性混入进行扩充;
    • 更新文章摘要;