Анализ исходного кода Vuex

исходный код Vue.js Vuex MVVM

написать впереди

Поскольку меня очень интересует Vue.js, а технологический стек, над которым я обычно работаю, тоже Vue.js, я потратил некоторое время на изучение и изучение исходного кода Vue.js за последние несколько месяцев и сделал резюме и вывод.

Оригинальный адрес статьи:GitHub.com/ответы на….

В процессе обучения в Vue были добавлены китайские аннотации.GitHub.com/ответы на…и аннотации VuexGitHub.com/ответы на…, я надеюсь, что это может быть полезно другим друзьям, которые хотят изучить исходный код.

Могут быть отклонения в понимании.Добро пожаловать, чтобы задать и указать, учиться вместе и добиваться прогресса вместе.

Vuex

Когда мы используем Vue.js для разработки сложных приложений, мы часто сталкиваемся с несколькими компонентами, имеющими одно и то же состояние, или несколько компонентов будут обновлять одно и то же состояние.Когда объем кода приложения невелик, мы можем общаться для поддержки и изменения данных или для передавать и изменять данные через шину событий. Однако, когда приложение станет больше, код станет трудно поддерживать.Многоуровневые вложенные данные, передаваемые из родительского компонента через проп, чрезвычайно хрупки из-за глубокого уровня, а шина событий также будет из-за увеличения компоненты и объем кода.Взаимодействие усложняется и становится трудно прояснить отношения передачи между ними.

Так почему же мы не можем отделить слой данных от слоя компонентов? Глобальное размещение слоя данных формирует единое хранилище, а слой компонентов становится тоньше, предназначенным для отображения и работы с данными. Все изменения данных должны проходить через глобальное хранилище для формирования одностороннего потока данных, что делает изменения данных «предсказуемыми».

Vuex — это библиотека, специально разработанная для платформы Vue.js для управления состоянием приложений Vue.js. Она опирается на основные идеи Flux и Redux, извлекает общие данные в глобальную среду и сохраняет их в одном экземпляре. использование адаптивного механизма Vue.js для эффективного управления состоянием и обновления. Именно потому, что Vuex использует «механизм реагирования» внутри Vue.js, Vuex является фреймворком, специально разработанным для Vue.js и хорошо совместимым с ним (преимущество в том, что он более лаконичен и эффективен, недостаток в том, что его можно соответствует использованию Vue.js). Для конкретного использования и API, пожалуйста, обратитесь кОфициальный сайт Vuex.

Давайте взглянем на эту блок-схему данных Vuex Студенты, знакомые с использованием Vuex, уже должны ее знать.

Vuex реализует односторонний поток данных и имеет состояние для глобального хранения данных. Все операции по изменению состояния должны выполняться с помощью мутации. Мутация также предоставляет режим подписки для внешних подключаемых модулей для получения обновлений данных состояния. Все асинхронные интерфейсы должны выполнять действие, которое обычно используется для вызова внутреннего интерфейса для асинхронного получения обновленных данных, и действие не может напрямую изменять состояние, и по-прежнему необходимо изменять данные состояния посредством мутации. Наконец, в соответствии с изменением состояния, оно отображается в представлении. Работа Vuex основана на внутреннем механизме двусторонней привязки данных Vue и требует нового объекта Vue для достижения «отзывчивости», поэтому Vuex — это библиотека управления состоянием, специально разработанная для Vue.js.

Установить

Друзья, которые использовали Vuex, должны знать, что установка Vuex очень проста, вам нужно только указать хранилище, а затем выполнить следующие две строки кода, чтобы завершить внедрение Vuex.

Vue.use(Vuex);

/*将store放入Vue创建时的option中*/
new Vue({
    el: '#app',
    store
});

Итак, вопрос в том, как Vuex внедряет хранилище в экземпляр Vue?

Vue.js предоставляетVue.useЭтот метод используется для установки подключаемого модуля для Vue.js, а подключаемый модуль устанавливается внутри путем вызова метода установки подключаемого модуля (если подключаемый модуль является объектом).

Давайте взглянем на реализацию установки Vuex.

/*暴露给外部的插件install方法,供Vue.use调用安装插件*/
export function install (_Vue) {
  if (Vue) {
    /*避免重复安装(Vue.use内部也会检测一次是否重复安装同一个插件)*/
    if (process.env.NODE_ENV !== 'production') {
      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)
}

Этот установочный код выполняет две функции: во-первых, предотвращает повторную установку Vuex, а во-вторых, выполняет команду applyMixin для инициализации Vuex путем выполнения метода vuexInit. Vuex по-разному обрабатывает Vue 1.0 и 2.0.Если это Vue 1.0, Vuex поместит метод vuexInit в метод _init Vue, а для Vue 2.0 он запутает vuexinit в середине хука Vue beforeCreacte. Давайте посмотрим на код vuexInit.

 /*Vuex的init钩子,会存入每一个Vue实例等钩子列表*/
  function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
      /*存在store其实代表的就是Root节点,直接执行store(function时)或者使用store(非function)*/
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      /*子组件直接从父组件中获取$store,这样就保证了所有组件都公用了全局的同一份store*/
      this.$store = options.parent.$store
    }
  }

vuexInit попытается получить хранилище из опций.Если текущий компонент является корневым компонентом (корневым узлом), в опциях будет хранилище, и вы можете напрямую получить его и присвоить $store. Если текущий компонент не является корневым компонентом, получите ссылку $store родительского компонента через parent в параметрах. Таким образом, все компоненты получают экземпляр Store с одним и тем же адресом в памяти, поэтому мы можем получить доступ к глобальному экземпляру Store через this.$store в каждом компоненте.

Итак, что такое экземпляр Store?

Store

Мы передаем корневой компонент в хранилище, которое является экземпляром Store, созданным с использованием метода Store, предоставляемого Vuex.

export default new Vuex.Store({
    strict: true,
    modules: {
        moduleA,
        moduleB
    }
});

Давайте посмотрим на реализацию Store. Во-первых, это конструктор.

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 === 'function') {
      state = state()
    }

    // store internal state
    /* 用来判断严格模式下是否是用mutation修改state的 */
    this._committing = false
    /* 存放action */
    this._actions = Object.create(null)
    /* 存放mutation */
    this._mutations = Object.create(null)
    /* 存放getter */
    this._wrappedGetters = Object.create(null)
    /* module收集器 */
    this._modules = new ModuleCollection(options)
    /* 根据namespace存放module */
    this._modulesNamespaceMap = Object.create(null)
    /* 存放订阅者 */
    this._subscribers = []
    /* 用以实现Watch的Vue实例 */
    this._watcherVM = new Vue()

    // bind commit and dispatch to self
    /*将dispatch与commit调用的this绑定为store对象本身,否则在组件内部this.dispatch时的this会指向组件的vm*/
    const store = this
    const { dispatch, commit } = this
    /* 为dispatch与commit绑定this(Store实例本身) */
    this.dispatch = function boundDispatch (type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload, options) {
      return commit.call(store, type, payload, options)
    }

    // strict mode
    /*严格模式(使 Vuex store 进入严格模式,在严格模式下,任何 mutation 处理函数以外修改 Vuex state 都会抛出错误)*/
    this.strict = strict

    // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    /*初始化根module,这也同时递归注册了所有子modle,收集所有module的getter到_wrappedGetters中去,this._modules.root代表根module才独有保存的Module对象*/
    installModule(this, state, [], this._modules.root)

    // initialize the store vm, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    /* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
    resetStoreVM(this, state)

    // apply plugins
    /* 调用插件 */
    plugins.forEach(plugin => plugin(this))

    /* devtool插件 */
    if (Vue.config.devtools) {
      devtoolPlugin(this)
    }
  }

В дополнение к инициализации некоторых внутренних переменных класс построения Store в основном выполняет installModule (инициализация модуля) и resetStoreVM (делает хранилище «отзывчивым» через виртуальную машину).

installModule

Функция installModule в основном состоит в том, чтобы зарегистрировать мутацию, действие и геттер после добавления пространства имен пространства имен (если есть) в модуль и рекурсивно установить все подмодули.

/*初始化module*/
function installModule (store, rootState, path, module, hot) {
  /* 是否是根module */
  const isRoot = !path.length
  /* 获取module的namespace */
  const namespace = store._modules.getNamespace(path)

  // register in namespace map
  /* 如果有namespace则在_modulesNamespaceMap中注册 */
  if (module.namespaced) {
    store._modulesNamespaceMap[namespace] = module
  }

  // set state
  if (!isRoot && !hot) {
    /* 获取父级的state */
    const parentState = getNestedState(rootState, path.slice(0, -1))
    /* module的name */
    const moduleName = path[path.length - 1]
    store.`_withCommit`(() => {
      /* 将子module设置称响应式的 */
      Vue.set(parentState, moduleName, module.state)
    })
  }

  const local = module.context = makeLocalContext(store, namespace, path)

  /* 遍历注册mutation */
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  /* 遍历注册action */
  module.forEachAction((action, key) => {
    const namespacedType = namespace + key
    registerAction(store, namespacedType, action, local)
  })

  /* 遍历注册getter */
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })

  /* 递归安装mudule */
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}

resetStoreVM

Прежде чем говорить о resetStoreVM, давайте посмотрим на небольшую демонстрацию.

let globalData = {
    d: 'hello world'
};
new Vue({
    data () {
        return {
            ?state: {
                globalData
            }
        }
    }
});

/* modify */
setTimeout(() => {
    globalData.d = 'hi~';
}, 1000);

Vue.prototype.globalData = globalData;

/* 任意模板中 */
<div>{{globalData.d}}</div>

Вышеприведенный код имеет globalData в глобальном, который передается в данные объекта Vue, а затем отображает переменную в любом шаблоне Vue.Поскольку globalData уже находится в прототипе Vue в это время, доступ к нему осуществляется напрямую через этот .prototype, то есть {{prototype.d}} в шаблоне. В этот момент setTimeout изменяет globalData.d через 1 с, и мы обнаруживаем, что globalData.d в шаблоне изменился. Фактически, вышеприведенная часть заключается в том, что Vuex полагается на ядро ​​​​Vue для реализации «отзывчивости» данных.

Студенты, не знакомые с адаптивным принципом Vue.js, могут пройти еще одну статью автораОтзывчивые принципыУзнайте, как Vue.js выполняет двустороннюю привязку данных.

Тогда посмотрите на код.

/* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
function resetStoreVM (store, state, hot) {
  /* 存放之前的vm对象 */
  const oldVm = store._vm 

  // bind store public getters
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}

  /* 通过Object.defineProperty为每一个getter方法设置get方法,比如获取this.$store.getters.test的时候获取的是store._vm.test,也就是Vue对象的computed属性 */
  forEachValue(wrappedGetters, (fn, key) => {
    // use computed to leverage its lazy-caching mechanism
    computed[key] = () => fn(store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

  // use a Vue instance to store the state tree
  // suppress warnings just in case the user has added
  // some funky global mixins
  const silent = Vue.config.silent
  /* Vue.config.silent暂时设置为true的目的是在new一个Vue实例的过程中不会报出一切警告 */
  Vue.config.silent = true
  /*  这里new了一个Vue对象,运用Vue内部的响应式实现注册state以及computed*/
  store._vm = new Vue({
    data: {
      ?state: state
    },
    computed
  })
  Vue.config.silent = silent

  // enable strict mode for new vm
  /* 使能严格模式,保证修改store只能通过mutation */
  if (store.strict) {
    enableStrictMode(store)
  }

  if (oldVm) {
    /* 解除旧vm的state的引用,以及销毁旧的Vue对象 */
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldVm._data.?state = null
      })
    }
    Vue.nextTick(() => oldVm.$destroy())
  }
}

resetStoreVM сначала пройдет через wrapGetters и использует метод Object.defineProperty для привязки метода get к каждому получателю, чтобы мы могли получить доступ к this.$store.getter.test в компоненте, что эквивалентно доступу к store._vm.test. .

forEachValue(wrappedGetters, (fn, key) => {
  // use computed to leverage its lazy-caching mechanism
  computed[key] = () => fn(store)
  Object.defineProperty(store.getters, key, {
    get: () => store._vm[key],
    enumerable: true // for local getters
  })
})

После этого Vuex принял новый объект Vue для реализации «отзывчивости» данных и использовал функцию двусторонней привязки данных, предоставляемую Vue.js, для реализации синхронного обновления данных хранилища и представления.

store._vm = new Vue({
  data: {
    ?state: state
  },
  computed
})

В настоящее время, когда мы обращаемся к store._vm.test, мы также получаем доступ к свойствам в экземпляре Vue.

После выполнения этих двух шагов мы можем получить доступ к тестовому свойству в vm через this.$store.getter.test.

строгий режим

Параметр класса конструктора Vuex Store имеет строгий параметр, который может управлять Vuex для выполнения строгого режима.В строгом режиме все операции, изменяющие состояние, должны быть реализованы посредством мутации, иначе будет выдана ошибка.

/* 使能严格模式 */
function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.?state }, () => {
    if (process.env.NODE_ENV !== 'production') {
      /* 检测store中的_committing的值,如果是true代表不是通过mutation的方法修改的 */
      assert(store._committing, `Do not mutate vuex store state outside mutation handlers.`)
    }
  }, { deep: true, sync: true })
}

Во-первых, в строгом режиме Vuex будет использовать метод vm $watch для наблюдения за ?state, которое является состоянием Store, и вводить обратный вызов при его изменении. Мы обнаружили, что в обратном вызове есть только одно предложение. Утверждение assert используется для обнаружения store._committing. Когда store._committing имеет значение false, утверждение будет запущено и будет выдано исключение.

Мы обнаружили, что в методе фиксации Store оператор, выполняющий мутацию, выглядит следующим образом.

this._withCommit(() => {
  entry.forEach(function commitIterator (handler) {
    handler(payload)
  })
})

Давайте посмотрим на реализацию _withCommit.

_withCommit (fn) {
  /* 调用withCommit修改state的值时会将store的committing值置为true,内部会有断言检查该值,在严格模式下只允许使用mutation来修改store中的值,而不允许直接修改store的数值 */
  const committing = this._committing
  this._committing = true
  fn()
  this._committing = committing
}

Мы обнаружили, что когда данные состояния изменяются с помощью фиксации (мутации), для фиксации будет установлено значение true перед вызовом метода мутации, а затем данные в состоянии будут изменены с помощью функции мутации. $watch запускается, чтобы подтвердить, что фиксация не является исключением. Будет выброшено исключение (в настоящее время фиксация истинна). Когда мы напрямую изменяем данные состояния, запускается обратный вызов $watch для выполнения утверждения.В это время, если фиксация ложна, будет выдано исключение. Это реализация строгого режима Vuex.

Далее давайте рассмотрим некоторые API, предоставляемые Магазином.

совершить (mutation)

/* 调用mutation的commit方法 */
commit (_type, _payload, _options) {
  // check object-style commit
  /* 校验参数 */
  const {
    type,
    payload,
    options
  } = unifyObjectStyle(_type, _payload, _options)

  const mutation = { type, payload }
  /* 取出type对应的mutation的方法 */
  const entry = this._mutations[type]
  if (!entry) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(`[vuex] unknown mutation type: ${type}`)
    }
    return
  }
  /* 执行mutation中的所有方法 */
  this._withCommit(() => {
    entry.forEach(function commitIterator (handler) {
      handler(payload)
    })
  })
  /* 通知所有订阅者 */
  this._subscribers.forEach(sub => sub(mutation, this.state))

  if (
    process.env.NODE_ENV !== 'production' &&
    options && options.silent
  ) {
    console.warn(
      `[vuex] mutation type: ${type}. Silent option has been removed. ` +
      'Use the filter functionality in the vue-devtools'
    )
  }
}

Метод фиксации найдет и вызовет методы мутации, соответствующие всем типам в _mutations в соответствии с типом, поэтому, когда нет пространства имен, метод фиксации вызовет методы мутации во всех модулях. После выполнения всех мутаций будут выполнены все подписчики в _subscribers. Давайте посмотрим, что такое _subscribers.

Store предоставляет внешнему миру метод подписки для регистрации функции подписки, отправки ее в _subscribers экземпляра Store и возврата метода для отмены регистрации подписчика из _subscribers.

/* 注册一个订阅函数,返回取消订阅的函数 */
subscribe (fn) {
  const subs = this._subscribers
  if (subs.indexOf(fn) < 0) {
    subs.push(fn)
  }
  return () => {
    const i = subs.indexOf(fn)
    if (i > -1) {
      subs.splice(i, 1)
    }
  }
}

После завершения коммита будут вызваны подписчики в этих _subscribers.Этот режим подписки предоставляет внешнюю возможность отслеживать изменения состояния. Когда состояние изменяется посредством мутации, эти изменения могут быть эффективно компенсированы.

отправлять(action)

Давайте посмотрим на реализацию диспетчеризации.

/* 调用action的dispatch方法 */
dispatch (_type, _payload) {
  // check object-style dispatch
  const {
    type,
    payload
  } = unifyObjectStyle(_type, _payload)

  /* actions中取出type对应的ation */
  const entry = this._actions[type]
  if (!entry) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(`[vuex] unknown action type: ${type}`)
    }
    return
  }

  /* 是数组则包装Promise形成一个新的Promise,只有一个则直接返回第0个 */
  return entry.length > 1
    ? Promise.all(entry.map(handler => handler(payload)))
    : entry[0](payload)
}

И что делать при регистрации действия.

/* 遍历注册action */
function registerAction (store, type, handler, local) {
  /* 取出type对应的action */
  const entry = store._actions[type] || (store._actions[type] = [])
  entry.push(function wrappedActionHandler (payload, cb) {
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload, cb)
    /* 判断是否是Promise */
    if (!isPromise(res)) {
      /* 不是Promise对象的时候转化称Promise对象 */
      res = Promise.resolve(res)
    }
    if (store._devtoolHook) {
      /* 存在devtool插件的时候触发vuex的error给devtool */
      return res.catch(err => {
        store._devtoolHook.emit('vuex:error', err)
        throw err
      })
    } else {
      return res
    }
  })
}

Поскольку действие push в _actions инкапсулируется (wrappedActionHandler) при регистрации действия, мы получаем состояние, фиксацию и другие методы в первом параметре отправки. После этого результат выполнения res будет оцениваться, является ли он промисом или нет, он будет инкапсулирован и преобразован в объект промиса. При диспетчеризации берется из _actions, а когда всего один, возвращается напрямую, иначе обрабатывается Promise.all и потом возвращается.

watch

/* 观察一个getter方法 */
watch (getter, cb, options) {
  if (process.env.NODE_ENV !== 'production') {
    assert(typeof getter === 'function', `store.watch only accepts a function.`)
  }
  return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
}

Друзья, знакомые с Vue, должны быть знакомы с методом наблюдения. Здесь используется более изобретательный дизайн: _watcherVM — это экземпляр Vue, поэтому watch может напрямую использовать функцию наблюдения внутри Vue, чтобы обеспечить способ наблюдения за изменениями получателя данных.

registerModule

/* 注册一个动态module,当业务进行异步加载的时候,可以通过该接口进行注册动态module */
registerModule (path, rawModule) {
  /* 转化称Array */
  if (typeof path === 'string') path = [path]

  if (process.env.NODE_ENV !== 'production') {
    assert(Array.isArray(path), `module path must be a string or an Array.`)
    assert(path.length > 0, 'cannot register the root module by using registerModule.')
  }

  /*注册*/
  this._modules.register(path, rawModule)
  /*初始化module*/
  installModule(this, this.state, path, this._modules.get(path))
  // reset store to update getters...
  /* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
  resetStoreVM(this, this.state)
}

registerModule используется для регистрации динамического модуля, то есть использовать этот интерфейс при регистрации модуля после создания хранилища. Внутренняя реализация на самом деле состоит только из двух шагов: installModule и resetStoreVM, которые уже упоминались ранее и не будут повторяться здесь.

unregisterModule

 /* 注销一个动态module */
unregisterModule (path) {
  /* 转化称Array */
  if (typeof path === 'string') path = [path]

  if (process.env.NODE_ENV !== 'production') {
    assert(Array.isArray(path), `module path must be a string or an Array.`)
  }

  /*注销*/
  this._modules.unregister(path)
  this._withCommit(() => {
    /* 获取父级的state */
    const parentState = getNestedState(this.state, path.slice(0, -1))
    /* 从父级中删除 */
    Vue.delete(parentState, path[path.length - 1])
  })
  /* 重制store */
  resetStore(this)
}

Точно так же метод unregisterModule, соответствующий registerModule, динамически отменяет регистрацию модуля. Способ сделать это — сначала удалить модуль из состояния, а затем использовать resetStore для воссоздания хранилища.

resetStore

/* 重制store */
function resetStore (store, hot) {
  store._actions = Object.create(null)
  store._mutations = Object.create(null)
  store._wrappedGetters = Object.create(null)
  store._modulesNamespaceMap = Object.create(null)
  const state = store.state
  // init all modules
  installModule(store, state, [], store._modules.root, true)
  // reset vm
  resetStoreVM(store, state, hot)
}

Здесь resetStore на самом деле означает, что после инициализации _actions в хранилище повторно выполните installModule и resetStoreVM, чтобы инициализировать модуль и использовать функцию Vue, чтобы сделать его «отзывчивым», что согласуется с конструктором.

плагин

Vue предоставляет очень полезный плагинVue.js devtools

/* 从window对象的__VUE_DEVTOOLS_GLOBAL_HOOK__中获取devtool插件 */
const devtoolHook =
  typeof window !== 'undefined' &&
  window.__VUE_DEVTOOLS_GLOBAL_HOOK__

export default function devtoolPlugin (store) {
  if (!devtoolHook) return

  /* devtoll插件实例存储在store的_devtoolHook上 */
  store._devtoolHook = devtoolHook

  /* 出发vuex的初始化事件,并将store的引用地址传给deltool插件,使插件获取store的实例 */
  devtoolHook.emit('vuex:init', store)

  /* 监听travel-to-state事件 */
  devtoolHook.on('vuex:travel-to-state', targetState => {
    /* 重制state */
    store.replaceState(targetState)
  })

  /* 订阅store的变化 */
  store.subscribe((mutation, state) => {
    devtoolHook.emit('vuex:mutation', mutation, state)
  })
}

Если плагин уже установлен, он покажетVUE_DEVTOOLS_GLOBAL_HOOK. Когда используется devtoolHook, он инициирует событие «vuex:init», чтобы уведомить плагин, а затем прослушивает событие «vuex:travel-to-state» с помощью метода on для сброса состояния. Наконец, подписчик добавляется с помощью метода подписки Store.После запуска метода фиксации для изменения данных мутации подписчик будет уведомлен, тем самым вызывая событие «vuex:mutation».

наконец

Vuex — очень хорошая библиотека с небольшим количеством кода и четкой структурой, которая очень подходит для изучения и изучения ее внутренней реализации. Недавняя серия чтений исходного кода также принесла мне большую пользу.Написание этой статьи также надеется помочь большему количеству студентов, которые хотят изучить и изучить принципы внутренней реализации Vuex.

о

Автор: Ран Мо

Электронная почта: answerhuto@gmail.com или answerhuto@126.com

Github: github.com/answershuto

Блог:answershuto.github.io/

Знать домашнюю страницу:ууууууу. chi.com/people/grass-…

Знать столбец:zhuanlan.zhihu.com/ranmo

Самородки:Талант /user/289926…

Ос Китай:no.OSCHINA.net/U/3161824/no…

Пожалуйста, указывайте источник при перепечатке, спасибо.

Добро пожаловать, чтобы обратить внимание на мой общедоступный номер