vuex series - design Analysis of Vuex

I. Introduction

Before talking, we have to always remember a word: all front-end concepts are paper tigers.

Whether Vue, or React, need to manage state (state), such as between components are shared state needs. What is shared status? For example, a component designed for use status of the other component, or a component of another component needs to change state, are shared state.

If the state does not effectively manage the state at what time, for any reason, it will change how out of control, it is difficult to track and test. If you have not experienced problems in this regard, it is understood as a simple will and made a mess on the right .

In software development there, some general ideas, such as isolated changes , convention over configuration , etc., that is good abstract isolation changes, some changes easy place to find commonalities, isolate, not to affect other code. Convention over configuration is a lot of things we do not want to write a lot of configuration, such as a few of us agreed, view folder view can only be put, can not let go of the filter, the filter must be placed filter folder, then this is a convention, after a good agreement, we do not have to write a lot of configuration files, and we are looking for all views, directly on the line from where to find the folder view.

According to these ideas, ideas for solving the state management is this: the need to share state between components extracted, follow a specific convention, unified management to make changes in the state can be predicted .

 

Second, what is vuex

The official explanation : Vuex is a specially developed for Vue.js application state management. It uses the status of all components centralized storage management application, and a corresponding state rules to ensure a predictable manner changed.

Vernacular : the data (data) unified management, if related to the processing of data, to vuex inside and out of it! Like a supermarket, like unified management of goods.

Design : Vuex maintains a global object to the use of a single design pattern. In this global object, all properties are responsive to any change of the attributes, the use of the assembly will cause the property to be updated. And only by  commit way of changing the state of a unidirectional data flow patterns.

 

Third, source code interpretation

store object has a property called state. state contains all application-level status. Application of various components. With a state, it will remain up to date and synchronized. vue state is like the data, but it is the whole application data. In fact, I think vuex is not, and two-way data binding vue Like, do object.defineproperty () processing.

We start from the store object is instantiated code.

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  }
});

new Vue({
  el: '#app',
  store,
  // ...
});

store objects are instantiated by new Vuex.Store out, open the  src/index.js file, the code can be seen in the lowermost exposed vuex Store this API:

export default {
  Store,
  install,
  mapState,
  mapMutations,
  mapGetters,
  mapActions
}

 

3.1 store

Store to find this class, a very large segment of the long code. Old way, not all of the method of folding the look, but only constructor constructor. Filtered off state regardless of the code, the last code can be simplified in such a way:

constructor (options = {}) {
  const {
    state = {}
  } = options

  // init root module.
  // this also recursively registers all sub-modules
  // and collects all module getters inside this._wrappedGetters
  installModule(this, state, [], options)

  // initialize the store vm, which is responsible for the reactivity
  // (also registers _wrappedGetters as computed properties)
  resetStoreVM(this, state)
}

The moment a lot less code, then only need to focus on to achieve installModule and two methods of resetStoreVM. Then, again revealed a good news, installModule looked at, it is initialized on the module, so after research module look too late, we only need to study resetStoreVM.

 

3.2 resetStoreVM

Locate the  resetStoreVM method, filtered again, which Vue.config.silent go vue official online search is canceled vue all of logs and alerts function, so it is filtered out, and the remaining code is as follows:

function resetStoreVM (store, state) {
  // use a Vue instance to store the state tree
  store._vm = new Vue({
    data: { state }
  })
}

resetStoreVM To do is add the function to store a  _vm property, and the state as a data object vue, vue finally assign this object to the _vm. So here we know the class constructor store to add a _vm property.

 

3.3 set and get

Constructors parsed completely, then have to continue to find ways associated with the state, so to find  set and  get methods:

get state () {
  return this._vm.state
}

set state (v) {
  assert(false, `Use store.replaceState() to explicit replace store state.`)
}

set Method nothing to say, it means that the store does not give you directly modify the state (but in fact can modify the properties of an object state, first Guannameduo).

getMethod returns a constructor data just added _vm attributes (state). Read here, we should know why at this assembly a revised state.count, component b will change along with it. Because a property is a new state of the object in data vue ah.

 

 Fourth, the common API

4.1 commit resolve

If you need to change the status of the case, usually use  commit to operate, let's take a look at  commit is how to achieve change in state of.

commit(_type, _payload, _options) {
  // 检查传入的参数
  const { type, payload, options } = unifyObjectStyle(
    _type,
    _payload,
    _options
  )

  const mutation = { type, payload }
  // 找到对应的 mutation 函数
  const entry = this._mutations[type]
  // 判断是否找到
  if (!entry) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(`[vuex] unknown mutation type: ${type}`)
    }
    return
  }
  // _withCommit 函数将 _committing
  // 设置为 TRUE,保证在 strict 模式下
  // 只能 commit 改变状态
  this._withCommit(() => {
    entry.forEach(function commitIterator(handler) {
      // entry.push(function wrappedMutationHandler(payload) {
      //   handler.call(store, local.state, payload)
      // })
      // handle 就是 wrappedMutationHandler 函数
      // wrappedMutationHandler 内部就是调用
      // 对于的 mutation 函数
      handler(payload)
    })
  })
  // 执行订阅函数
  this._subscribers.forEach(sub => sub(mutation, this.state))
}

 

4.2 dispatch resolve

If you need to change the asynchronous state, we need to achieve by way of dispatch. In the dispatch call  commit functions are rewritten, you will find the mutation function within the module.

dispatch(_type, _payload) {
  // 检查传入的参数
  const { type, payload } = unifyObjectStyle(_type, _payload)

  const action = { type, payload }
  // 找到对于的 action 函数
  const entry = this._actions[type]
  // 判断是否找到
  if (!entry) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(`[vuex] unknown action type: ${type}`)
    }
    return
  }
  // 触发订阅函数
  this._actionSubscribers.forEach(sub => sub(action, this.state))

  // 在注册 action 的时候,会将函数返回值
  // 处理成 promise,当 promise 全部
  // resolve 后,就会执行 Promise.all
  // 里的函数
  return entry.length > 1
    ? Promise.all(entry.map(handler => handler(payload)))
    : entry[0](payload)
}

 

4.3 various syntactic sugar

In the assembly, if you want to use Vuex normal functions, often need to call this. $ Store.state.xxx way, we attracted a lot of inconvenience. To this end, Vuex introduced syntactic sugar features, so we can achieve the above functions in a simple way. The following order mapState for example, several other map are similar principle, not one parsed.

function normalizeNamespace(fn) {
  return (namespace, map) => {
    // 函数作用很简单
    // 根据参数生成 namespace
    if (typeof namespace !== 'string') {
      map = namespace
      namespace = ''
    } else if (namespace.charAt(namespace.length - 1) !== '/') {
      namespace += '/'
    }
    return fn(namespace, map)
  }
}
// 执行 mapState 就是执行
// normalizeNamespace 返回的函数
export const mapState = normalizeNamespace((namespace, states) => {
  const res = {}
  // normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
  // normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]
  // function normalizeMap(map) {
  //   return Array.isArray(map)
  //     ? map.map(key => ({ key, val: key }))
  //     : Object.keys(map).map(key => ({ key, val: map[key] }))
  // }
  // states 参数可以参入数组或者对象类型
  normalizeMap(states).forEach(({ key, val }) => {
    res[key] = function mappedState() {
      let state = this.$store.state
      let getters = this.$store.getters
      if (namespace) {
        // 获得对应的模块
        const module = getModuleByNamespace(this.$store, 'mapState', namespace)
        if (!module) {
          return
        }
        state = module.context.state
        getters = module.context.getters
      }
      // 返回 State
      return typeof val === 'function'
        ? val.call(this, state, getters)
        : state[val]
    }
    // mark vuex getter for devtools
    res[key].vuex = true
  })
  return res
})

 

V. Summary

These are Vuex source resolution, although Vuex overall code is not much, but it is worth reading project. Understand the state store, and know that it is a new vue through new objects _vm to listen, and this _vm is tied to the store. So through this series of relationships, and finally we can use in various components to be monitored in  the this. $ Store.state .

 

[Thank reading and subsequent starting new articles: sau exchange learning communities: https://www.mwcxs.top/ ]

Guess you like

Origin blog.csdn.net/saucxs/article/details/91967620