Vuex initialization

  • Vuex export default an object{}, which contains many objects, includingStoreThis is a constructor that accepts configured parameters such as state, mutation, and so oncreateStoreSo you can’t repeat new.
Vuex is an object, not a function-- the constructor has an install method inside and defines $store for global use.
import { createApp } from 'vue'
import { createStore } from 'vuex'

// Create a new store instance,
const store = createStore({
  state () {
    return {
      count: 0}},mutations: {
    increment (state) {
      state.count++
    }
  }
})

const app = createApp({ /* Root component */ })

// Install the Store instance as a plug-in
app.use(store)
Copy the code

Devtools, Vuex panel

  • Add it to the VUE development panel at the same time as install to synchronously observe changes in the data in real time and get a snapshot of it. Turn DevTools on or off for a particular Vuex instance. For instances passed false the Vuex Store does not subscribe to the DevTools plug-in. This is useful when there are multiple stores on a page.
const useDevtools = this._devtools ! = =undefined
  ? this._devtools
  : __DEV__ || __VUE_PROD_DEVTOOLS__

if (useDevtools) {
  addDevtools(app, this)}Copy the code

ModuleCollection Collects default modules for naming differentiation

  • ModuleCollection registers modules, including embedded modules, for naming differentiation. Identify the data of each module.

  • New Module(rawModule, Runtime) is used to initialize the contents of each Module. The length of path is used to determine whether the Module is a root Module. Rawmodule. modules is used to determine whether the current Module contains sub-modules. Then loop the submodule to register again.

register (path, rawModule, runtime = true) {
  if (__DEV__) {
    assertRawModule(path, rawModule)
  }

  const newModule = new Module(rawModule, runtime) // Initialize the data for each module
  if (path.length === 0) { // This is the root directory
    this.root = newModule
  } else {
    const parent = this.get(path.slice(0, -1))
    parent.addChild(path[path.length - 1], newModule)
  }

  // Register nested modules
  if (rawModule.modules) {
    forEachValue(rawModule.modules, (rawChildModule, key) = > {
      this.register(path.concat(key), rawChildModule, runtime)
    })
  }
}
Copy the code

InstallModule Installs modules

  • The named module collected by the ModuleCollection is initialized, and its children are also recursed and initialized.

  • Each layer of MoDUL has the Context property, which stores all data from the current Module in context.

const local = module.context = makeLocalContext(store, namespace, path)
Copy the code
  • Traverse through the module of the current space registration, forEachMutation, forEachAction, forEachGetter registered its three properties, respectively, for the son and the module of need through forEachChild to traverse the module, Call installModule to initialize.

  • The following uses mutation as an example to illustrate how the parameter passing of the mutation method is used.

mutation: {
  test (state, payload) {
    // State is the state data in the current module. You can modify state directly
    // Payload is the parameter that is passed in}}/*** source code parsing *@params {Object} store- Current store instance *@params {String} type- Namespace type *@params {Function} handler- the corresponding mutation in the function *@params {Object} local- Current processed context **/
function registerMutation (store, type, handler, local) {
  const entry = store._mutations[type] || (store._mutations[type] = [])
  entry.push(function wrappedMutationHandler (payload) {
    handler.call(store, local.state, payload) // the input definition of the mutation method can be found here})}Copy the code

Commit usage

  • There are two ways to commit a commit
// The first parameter is type, and the second parameter is payload
store.commit('increment', {
  amount: 10
})

// Add type and payload to an object
store.commit({
  type: 'increment'.amount: 10
})

// source code for the two ways to do compatible, the second is to more appropriate user habits
export function unifyObjectStyle (type, payload, options) {
  if (isObject(type) && type.type) { // Extra processing is done when type is an object
    options = payload
    payload = type
    type = type.type
  }
  return { type, payload, options }
}
Copy the code
  • If you want to access global content in a module with a namespace, you can add it in optionsroot = true
commit('rootMutation'.null, { root: true }) // -> 'rootMutation', which accesses the top-level method directly

/ / the source code
commit: noNamespace ? store.commit : (_type, _payload, _options) = > {
  const args = unifyObjectStyle(_type, _payload, _options)
  const { payload, options } = args
  let { type } = args
  // Check whether root in options is true. If not, use namespace to get method name.
  if(! options || ! options.root) { type = namespace + typeif(__DEV__ && ! store._mutations[type]) {console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)
      return
    }
  }

  store.commit(type, payload, options)
}
Copy the code

Listen for data through resetStoreState

  • Use the Reactive method in VUe3 to convert all states in the Store to reactive data
store._state = reactive({
  data: state
})
Copy the code
  • Collect all the gettersthis._wrappedGettersDefineProperty (object.defineProperty); getters (Object.defineProperty); getters (Object.defineProperty);
scope.run(() = > {
  forEachValue(wrappedGetters, (fn, key) = > {
    // use computed to leverage its lazy-caching mechanism
    // direct inline function use will lead to closure preserving oldState.
    // using partial to return function with only arguments preserved in closure environment.
    computedObj[key] = partial(fn, store)
    computedCache[key] = computed(() = > computedObj[key]())
    Object.defineProperty(store.getters, key, {
      get: () = > computedCache[key].value, // Only get is supported
      enumerable: true // for local getters})})})Copy the code

The use of plugins

  • Vuex allows developers to create their own plugins, which take only one parameter, the Store instance object. The developer can modify it through the instance object.
plugins.forEach(plugin= > plugin(this)) A plugin must be a function, unlike vue, which accepts objects or functions
Copy the code

Use of auxiliary functions

  • For example, mapMutations, the auxiliary function can quickly access the method of VUex mutation in vUE component.

  • If a namespace exists, check whether it exists:

function normalizeNamespace (fn) {
  return (namespace, map) = > {
    if (typeofnamespace ! = ='string') { // There is no namespace
      map = namespace
      namespace = ' '
    } else if (namespace.charAt(namespace.length - 1)! = ='/') { // There is a namespace for piecing together paths
      namespace += '/'
    }
    return fn(namespace, map)
  }
}
Copy the code
  • In the absence of namespaces, there are two methods, one isarrayType, one isobjectType:
import { mapMutations } from 'vuex'

// There is no namespace
export default {
  // ...
  methods: {
    ...mapMutations([
      'increment'.// Map 'this.increment()' to 'this.store.com MIT ('increment')'

      // 'mapMutations' also supports payloads:
      'incrementBy' // Map 'this.incrementBy(amount)' to 'this. codestore.com MIT ('incrementBy', amount)'
    ]),
    ...mapMutations({
      add: 'increment' // Map 'this.add()' to 'this.store.mit ('increment')'}}})// This has a namespace
export default {
  methods: {
  ...mapmutations([
    'some/nested/module/foo'.// -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()])},... mapmutations('some/nested/module'['foo'.// -> this.foo()
    'bar' // -> this.bar()])}/ / the source code
function normalizeMap (map) { // This method determines whether it is an array or an object type and pieces it together
  if(! isValidMap(map)) {return[]}return Array.isArray(map)
    ? map.map(key= > ({ key, val: key }))
    : Object.keys(map).map(key= > ({ key, val: map[key] }))
}
Copy the code
  • MapMutations source, by obtaining the store matching mutation inside the method, and then return out.
export const mapMutations = normalizeNamespace((namespace, mutations) = > {
  const res = {}
  if(__DEV__ && ! isValidMap(mutations)) {console.error('[vuex] mapMutations: mapper parameter must be either an Array or an Object')
  }
  normalizeMap(mutations).forEach(({ key, val }) = > {
    res[key] = function mappedMutation (. args) { // The method defined by the component is matched here
      // Get the commit method from store
      let commit = this.$store.commit
      if (namespace) {
        const module = getModuleByNamespace(this.$store, 'mapMutations', namespace)
        if (!module) {
          return
        }
        commit = module.context.commit
      }
      return typeof val === 'function'
        ? val.apply(this, [commit].concat(args))
        : commit.apply(this.$store, [val].concat(args))
    }
  })
  return res
})
Copy the code