在vue烂在满大街的情况下,面试中问到vue
核心插件实现原理是很平常的事情。
抱着学习的态度,我们来学习下vuex
的源码。
在学习之前,我们希望得到什么答案呢?
在很长的时间,我们都在讨论vuex
的关键的几个可以被用于单向流动的固定节点:dispatch->acition(commit)->mutation->state
。所以本坑希望我们读完源码后,我们知道为什么vuex一定要遵循这几个节点单向的流动,状态是怎么衔接过去的?。
第二个问题就是,vuex
它如何实现响应式的状态变化?。
第三个问题就是,我们是否能够准确口述vuex
大致实现和管理策略呢?
从头开始
vue
的入口文件是src\index.js
,打开看看:
和其他插件一样,在项目通过use(vuex)
来调用install
方法来安装vuex
的,除此之外还提供其他方法,比如常常在各个模块使用的map类函数。
import { Store, install } from './store'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers'
export default {
Store,
install,
version: '__VERSION__',
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers
}
我们先看看install
方法,当运行Vue.use(vuex)
便开始启动install
。为了防止多次调用install方法引发的多次安装,代码中通过赋值比较的方式来实现。看似简单却解决了这个问题。这里有个小点:如果多次Vue.use(vuex)
,封装的use代码里会先检查Vue
的_installedPlugins
数组是否有该插件,如果有则直接返回,并不会进入插件install
里。
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
可以看到代码根据版本来使用不同的方式来applyMixin
,如果是2.0
版本则把vuexInit
初始化的方法混入beforeCreate钩子里,如果不是,则合并到_init
原型方法里头。vuexInit
方法能够让实例中各个层级能够访问到和根节点一样的store
.自此install过程就完成了。
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)
}
}
/**
* Vuex init hook, injected into each instances init hooks list.
*/
function vuexInit () {
const 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
}
}
}
下一节,store的构造函数说起。