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') }
-
通过执行
init
hook,从而来执行我们传入的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;
}
}