vuex插件

有了上文作为铺垫,我们就可以很轻松的来解释vuex的原理了。
Vuex仅仅是Vue的一个插件。Vuex只能使用在vue上,因为其高度依赖于Vue的双向绑定和插件系统。
Vuex的注入代码比较简单,调用了一下applyMixin方法,现在的版本其实就是调用了Vue.mixin,在所有组件的 beforeCreate生命周期注入了设置 this.$store这样一个对象。

  1. // src/store.js
  2. export function install (_Vue) {
  3. if (Vue && _Vue === Vue) {
  4. return
  5. }
  6. Vue = _Vue
  7. applyMixin(Vue)
  8. }
  9. // src/mixins.js
  10. export default function (Vue) {
  11. const version = Number(Vue.version.split('.')[0])
  12. if (version >= 2) {
  13. Vue.mixin({ beforeCreate: vuexInit })
  14. } else {
  15. const _init = Vue.prototype._init
  16. Vue.prototype._init = function (options = {}) {
  17. options.init = options.init
  18. ? [vuexInit].concat(options.init)
  19. : vuexInit
  20. _init.call(this, options)
  21. }
  22. }
  23. function vuexInit () {
  24. const options = this.$options
  25. // store injection
  26. if (options.store) {
  27. this.$store = typeof options.store === 'function'
  28. ? options.store()
  29. : options.store
  30. } else if (options.parent && options.parent.$store) {
  31. this.$store = options.parent.$store
  32. }
  33. }
  34. }

划重点:1行代码 Vue.mixin
那么 Vuex.Store 是如何实现的呢?

  1. // src/store.js
  2. constructor (options = {}) {
  3. const {
  4. plugins = [],
  5. strict = false
  6. } = options
  7. // store internal state
  8. this._committing = false
  9. this._actions = Object.create(null)
  10. this._actionSubscribers = []
  11. this._mutations = Object.create(null)
  12. this._wrappedGetters = Object.create(null)
  13. this._modules = new ModuleCollection(options)
  14. this._modulesNamespaceMap = Object.create(null)
  15. this._subscribers = []
  16. this._watcherVM = new Vue()
  17. const store = this
  18. const { dispatch, commit } = this
  19. this.dispatch = function boundDispatch (type, payload) {
  20. return dispatch.call(store, type, payload)
  21. }
  22. this.commit = function boundCommit (type, payload, options) {
  23. return commit.call(store, type, payload, options)
  24. }
  25. // strict mode
  26. this.strict = strict
  27. const state = this._modules.root.state
  28. // init root module.
  29. // this also recursively registers all sub-modules
  30. // and collects all module getters inside this._wrappedGetters
  31. installModule(this, state, [], this._modules.root)
  32. resetStoreVM(this, state)
  33. // apply plugins
  34. plugins.forEach(plugin => plugin(this))
  35. }

划重点:其实上面的代码绝大部分都不需要关注的 - -。
其实重点就是一行代码 resetStoreVM(this, state)。
那么 resetStoreVM 里面是什么呢?

  1. // src/store.js
  2. function resetStoreVM (store, state, hot) {
  3. Vue.config.silent = true
  4. store._vm = new Vue({
  5. data: {
  6. $$state: state
  7. },
  8. computed
  9. })
  10. }

划重点: 还是一行代码:new Vue。通过 Vue自己的双向绑定然后注入
你是不是以为就这样结束了呢? NoNoNo,当你再Vue中通过this如果调用store的数据呢?

  1. // 当获取state时,返回以双向绑定的$$sate
  2. var prototypeAccessors$1 = { state: { configurable: true } };
  3. prototypeAccessors$1.state.get = function () {
  4. return this._vm._data.$$state
  5. };
  6. // 将state定义在原型中
  7. Object.defineProperties( Store.prototype, prototypeAccessors$1 );

其实就是获取 this._vm._data.$$state 而已

总结

Vue的双向绑定通过调用new Vue实现,然后通过Vue.mixin注入到Vue的生命周期中,再通过劫持state.get将数据放入组件中。