Vuex 基础教程系列 🎉
安装
Vuex 提供了三种安装方式:CDN、NPM or Yarn、Dev Build(自行构建)。
node_modules/vuex既将 Vuex 克隆到当前项目node_modules中的vuex目录中。
开始
Store 是 Vuex 应用的核心,我们可以将其理解为 仓库 或者是 商店,用来存储所有的全局状态以及围绕着状态而提供的一系的管理方法。
基础示例:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="app">
<!--通过注入的 $store 来获取 state 的值-->
<p>{{$store.state.count}}</p>
<button @click="add">increment</button>
</div>
<script>
//创建一个 Vuex 应用
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
});
new Vue({
el: '#app', store, methods: {
add() { //变更 increment mutation 的方法。
store.commit('increment') // 显示提交 mutation。
}
}
})
</script>
</body>
</html>
在这个示例中包含了以下几个我们必须要掌握的 Vuex 知识点:
- 通过
new Vuex.Store({...})构造函数创建一个 Vuex 应用,每一个 store 都是一个全局唯一的单例,因此我们更推荐一个应用只创建一个。 state中声明的是作为全局的状态,mutations对象则保存着对应状态的变更方法。- 通过
store.state可以访问状态中的值,但不能通过此种方式直接变更状态的值。 - 变更状态只能通过显示
commit mutation方式变更 state , 不能直接store.state的方式变更。 - Vuex 支持像 VueRouter 注入
$route与$router对象那样注入一个$store对象。
如果想更深入的了解 Vuex 的注入机制,则必须要了解以下 Vuex 中的相关源码处理。
首先,观察 Vuex.install 方法。
function install(_Vue) {
if (Vue && _Vue === Vue) {
{
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
);
}
return
}
Vue = _Vue;
applyMixin(Vue);
}
然后定位到 applyMinxin 方法。
function applyMixin(Vue) {
var version = Number(Vue.version.split('.')[0]);
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit });
} else {
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
var _init = Vue.prototype._init;
Vue.prototype._init = function (options) {
if (options === void 0) options = {};
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit;
_init.call(this, options);
};
}
/**
* Vuex init hook, injected into each instances init hooks list.
*/
function vuexInit() {
var options = this.$options;
//....
}
上述代码中最核心的当属 vuexInit 方法,但是我们先放过,重点关注源码中的条件判断,可以看到 Vuex 再注入 $store 时会基于不同的 Vue 版本采用不同的注入方式。
- 对于
≥ Vue 2.x版本大于等于 2 的会使用 Vue 的mixin方式进行混入。 - 对于
< Vue2.x版本则通过覆写Vue.prototype.__init方法来实现在组件实例初始化时向组件实例this绑定$store,这是一种兼容 Vue1.x 的写法,这里只提供一些上下文中最核心的代码片段。-
Vue.prototype.__init方法会合并组件自带的$options与传入的options对象。Vue.prototype._init = function (options) { options = options || {} //... // merge options. this.$options = mergeOptions( this.constructor.options, options, this ) //.... // call init hook this._callHook('init') } -
通过执行
inithook,从而来执行我们传入的vuexInit方法。Vue.prototype._callHook = function (hook) { this.$emit('pre-hook:' + hook) var handlers = this.$options[hook] if (handlers) { for (var i = 0, j = handlers.length; i < j; i++) { handlers[i].call(this) } } this.$emit('hook:' + hook) }
-
最后在 vuexInit 方法的源码中,我们可以发现每次方法被执行时都会判断当前组件的 $options 是否已经存在了 store 选项,如果有则将其赋值给 this.$store 选项(当然通常有值的肯定是根组件实例)。如果没有则通过 parent.$store 选项去获取,然后同样赋值给自己的 this.$store 选项,只有这样才能保证自己的子组件也能通过 parent 获取 $store,从而使获取 $store 选项的链条不会发生断裂。
采用这种,根组件.store ← 组件.$store ← ... 链条的方式来获取
store本质还是依托与 Vue 组件树嵌套的结构原理。
function vuexInit() {
var options = this.$options;
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store;
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store;
}
}