如何使用Vuex
虽然大家对于如何使用Vuex都不陌生,但是在分析源码是如何注册store之前,我们还是来简单回顾一下。
-
安装 vuex :npm install vuex --save
-
在 vue 项目中的 src 目录下创建 store 目录,并在该目录下新建文件——index.js,用于创建 store 实例。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex) // 安装Vuex插件
const state = {}
const mutations = {}
const actions = {}
const getters = {}
// 创建 store 实例对象并导出。仅为举例,就定义了些空对象。
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
- 在 main.js 入口文件中引入
import Vue from 'vue'
import App from './App.vue'
import store from './index'
new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
render: h => h(App)
})
经过回顾,大家必然都有了印象,接下来就步入正题吧。
注册 store
使用过 Vuex 的同学们都知道:Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex))。而通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store
访问到。
为了弄明白原理,我们需要先了解一下 Vue.use 。
Vue.use 定义
引用自官方定义:
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。
该方法需要在调用 new Vue() 之前被调用。 当 install 方法被同一个插件多次调用,插件将只会被安装一次。
从上面的定义中,我们不难明白。要安装 Vue 插件,则此插件必须提供 install 方法或本身是一个函数。所以,我们在使用 Vue.use(Vuex) 安装 Vuex 插件时,实际上调用的是其内部的 install 方法。
Vue.use 方法
Vue.use 是 vue 源码中定义的方法,用于安装 Vue 插件。
export function initUse (Vue: GlobalAPI) {
// plugin是对象或函数
Vue.use = function (plugin: Function | Object) {
// 定义存储插件的数组变量
const installedPlugins = (this._installedPlugins || (this._installedPlugins =
[]))
// 判断vue是否已经注册过这个插件
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// toArray,是 vue 源码中定义的一个工具方法
// toArray(arguments, 1) 会把 arguments(类数组) 转为数组,同时会把
// 除第一个参数外(即插件plugin)的其他参数全都存储到一个数组中。
const args = toArray(arguments, 1)
// 将 vue 对象插入到 args 数组的第一位
args.unshift(this)
// 判断插件是否有 install 方法。
// 如果有,调用 install 方法并将参数数组传入,改变 this 指针为该组件
//如果没有,则直接调用,改变 this 指针为 null
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
// 将 plugin 添加到 installedPlugins 数组中,用于检测是否安装过此插件
installedPlugins.push(plugin)
return this
}
}
toArray 方法
toArray 是 vue 源码中定义的一个工具方法。
// 将一个类似数组的对象转换为一个真正的数组
export function toArray (list: any, start?: number): Array<any> {
start = start || 0
let i = list.length - start
const ret: Array<any> = new Array(i)
while (i--) {
ret[i] = list[i + start]
}
return ret
}
从 Vue.use 的源码中我们可以看到:plugin 若是'object',则调用其内的 install 方法,若是 'function',则将自身作为 install 方法调用。现在,想必大家已经理清了 Vue.use 的源码实现,接下来,就是 Vuex 源码中暴露的 install 方法了。
Vuex 中的 install
install 方法的核心在于调用 applyMixin 方法。
// 在 Vuex 中的文件路径:src/store.js
export function install(_Vue) {
// 避免重复安装
if (Vue && _Vue === Vue) {
if (__DEV__) {
console.error(
// vuex已经安装了。Vue.use(Vuex)应该只调用一次。
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
// 首次加载,将 _Vue 变量赋值给 Vue,并用于检测是否重复安装
Vue = _Vue
// applyMixin 是将 store 实例注入到根组件下的所有子组件中的关键所在
applyMixin(Vue)
}
applyMixin
applyMixin,真正实现将 store 实例注入到根组件下的所有子组件中的方法。并对 vue1.0 和 2.0 版本做了不同的处理。
// 在 Vuex 中的文件路径:src/mixin.js
export default function (Vue) {
const version = Number(Vue.version.split('.')[0]) // 获取 vue 版本号
if (version >= 2) {
// Vue.mixin 全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。
// 插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用。
// 将 vuexInit 混入到 Vue 的 beforeCreacte 钩子中。
// 这样,就会在每个 Vue 实例中执行 vuexInit 方法。
Vue.mixin({
beforeCreate: vuexInit
})
} else {
// 覆盖初始化并注入 vuex 初始化过程
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {}) {
options.init = options.init ? [vuexInit].concat(options.init) : vuexInit
_init.call(this, options)
}
}
// Vuex 初始化钩子,注入到每个实例的初始化钩子列表中
function vuexInit() {
const options = this.$options
// store 注入,若 options.store 存在,则当前组件为根组件,否则为子组件
if (options.store) {
this.$store = typeof options.store === 'function' ?
options.store() : options.store
} else if (options.parent && options.parent.$store) {
// 子组件引用其父组件中的 $store 。这样,在任意组件中通过 this.$store
// 就能访问到根实例的 store
this.$store = options.parent.$store
}
}
}
结束语
对于解析源码这种事,本人也是新手上路。若是有说错或不严谨的地方,希望小伙伴们能够指出,以便大家一起愉快的掉头发。同时,建议初学的小伙伴,在阅读源码前,最好已学习并使用过Vuex。只有如此,才能更加清晰地理解它的工作流程和原理。