8、vuex原理解析
vuex的思想:
Vuex维护着一个全局的对象,使用到单列数据模式,在这个对象中所有的数据都是响应式的,任意属性进行改变,都会让这个属性所有的依赖进行更新,并且只能通过
Mutations
进行改变。实现了单向数据流。
8.1、vuex的安装
vuex和vue0router的安装方式很一样都是通过vue.use()方法进行安装,内部通过调用install方法进行插件的安装。下面看下vuex的install安装函数。
/*暴露给外部的插件install方法,供Vue.use调用安装插件*/
export function install (_Vue) {
/*避免重复安装(Vue.use内部也会检测一次是否重复安装同一个插件)*/
if (Vue && _Vue === Vue) {
if (__DEV__) {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
/*保存Vue,同时用于检测是否重复安装*/
Vue = _Vue
/*将vuexInit混淆进Vue的beforeCreate(Vue2.0)或_init方法(Vue1.0)*/
applyMixin(Vue)
}
install代码做了两件事
- 通过Vue变量去防止vuex重复安装
- 执行applyMixin函数,目的是执行vueinit方法去初始化vuex,Vuex针对Vue1.0与2.0分别进行了不同的处理,如果是Vue1.0,Vuex会将vuexInit方法放入Vue的_init方法中,而对于Vue2.0,则会将vuexinit混淆进Vue的beforeCreacte钩子中。来看一下vuexInit的代码。
export default function (Vue) {
const 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.
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {
}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
}
function vuexInit () {
//获取vue实例的options
const options = this.$options
// store injection
//如果是根节点则直接执行store或者将store赋值给this.$store
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
//子组件直接从父组件中获取$store,这样就保证了所有组件都公用了全局的同一份store
this.$store = options.parent.$store
}
}
}
第一个导出的就是applyMixin函数, vuexInit会尝试从options中获取store,如果当前组件是根组件(Root节点),则options中会存在store,直接获取赋值给 s t o r e 即 可 。 如 果 当 前 组 件 非 根 组 件 , 则 通 过 o p t i o n s 中 的 p a r e n t 获 取 父 组 件 的 store即可。如果当前组件非根组件,则通过options中的parent获取父组件的 store即可。如果当前组件非根组件,则通过options中的parent获取父组件的store引用。这样一来,所有的组件都获取到了同一份内存地址的Store实例,于是我们可以在每一个组件中通过this.$store愉快地访问全局的Store实例了。
8.2、Store实例化
Store 对象的构造函数接收⼀个对象参数,它包含actions 、 getters 、 state 、 mutations 、 modules 等 Vuex 的核⼼概念。
constructor (options = {
}) {
// Auto install if it is not done yet and `window` has `Vue`.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #731
/*
在浏览器环境下,如果插件还未安装(!Vue即判断是否未安装),则它会自动安装。
它允许用户在某些情况下避免自动安装。
*/
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
if (process.env.NODE_ENV !== 'production') {
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
assert(this instanceof Store, `Store must be called with the new operator.`)
}
const {
/*一个数组,包含应用在 store 上的插件方法。这些插件直接接收 store 作为唯一参数,可以监听 mutation(用于外部地数据持久化、记录或调试)或者提交 mutation (用于内部数据,例如 websocket 或 某些观察者)*/
plugins = [],
/*使 Vuex store 进入严格模式,在严格模式下,任何 mutation 处理函数以外修改 Vuex state 都会抛出错误。*/
strict = false
} = options
/*从option中取出state,如果state是function则执行,最终得到一个对象*/
let {
state = {
}
} = options
if (typeof state === 'funct