Home  >  Article  >  Web Front-end  >  A brief analysis of the source code content of VueX

A brief analysis of the source code content of VueX

不言
不言Original
2018-07-20 15:30:471822browse

Vuex is a state management pattern developed specifically for Vue.js applications. For single-page applications with many components and complex interactions, Vuex provides a convenient, accurate and predictable state management method to facilitate data sharing and modification between components.

The file structure is as follows

  • /module

  • /plugins

  • helpers.js

  • ##index.esm.js

  • index.js

  • store.js

  • util.js

util.js

Start with the simplest tool function.

find function

/**
 * Get the first item that pass the test
 * by second argument function
 *
 * @param {Array} list
 * @param {Function} f
 * @return {*}
 */
export function find (list, f) {
  return list.filter(f)[0]
}

Test case for find function

it('find', () => {
  const list = [33, 22, 112, 222, 43]
  expect(find(list, function (a) { return a % 2 === 0 })).toEqual(22)
})

Analysis:

    ##Use first
  • Assertion function f

    Filters list list, and finally takes the first element of the filtered list.

deepCopy function

/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array<Object>} cache
 * @return {*}
 */
export function deepCopy (obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== &#39;object&#39;) {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}
test case of deepCopy

  // 普通结构
  it(&#39;deepCopy: nornal structure&#39;, () => {
    const original = {
      a: 1,
      b: &#39;string&#39;,
      c: true,
      d: null,
      e: undefined
    }
    const copy = deepCopy(original)

    expect(copy).toEqual(original)
  })
  
  // 嵌套结构
  it(&#39;deepCopy: nested structure&#39;, () => {
    const original = {
      a: {
        b: 1,
        c: [2, 3, {
          d: 4
        }]
      }
    }
    const copy = deepCopy(original)

    expect(copy).toEqual(original)
  })
  
  // 循环引用结构
  it(&#39;deepCopy: circular structure&#39;, () => {
    const original = {
      a: 1
    }
    original.circular = original

    const copy = deepCopy(original)

    expect(copy).toEqual(original)
  })

Analysis:

    Function: Deep cloning function that supports circular references
  • The first if judgment
  • obj === null || typeof obj !== 'object'

    Judgment If it is not a reference type and is returned directly (the basic type is a value copy), it is also an exit from recursion.

  • The second judgment
  • hit

    is to judge whether it is a circular reference. Since it is a circular reference, there should be a copy cached in the cache, directly Get it from the cache to avoid making a duplicate copy again.

  • What is a circular reference? See the third test case
  • original.circular = original

    . The circular reference and the referenced content are the same. Caching is used to avoid duplication. Clone (the content is the same)

  • original.circular

    is a circular reference, original is a circular reference

  • First put
  • cope

    into cache. When recursing, if you encounter a circular reference, make sure there is a copy of the circularly referenced copy in the cache. , but copy must be a reference type.

  • Why
  • cope

    must be a reference type? Circular reference What is saved is the reference, not the content (the copy has not been completed at this time). The copy is not completed during the recursion. The copy is completed only after the recursion is completed, so that will be circularly referenced in the future## When the content of # changes (copying is completed), the content of circular reference changes simultaneously

    So
  • const copy = Array.isArray(obj) ? [] : {}
  • must be a reference type.

    Finally
  • Object.keys
  • You can traverse all key names of objects and arrays (only the properties of the instance are returned, excluding the prototype chain and Symbol) to implement recursive cloning.

    There are two exits, one is a basic type and the other is a circular reference.
  • ##forEachValue
/**
 * forEach for object
 */
export function forEachValue (obj, fn) {
  Object.keys(obj).forEach(key => fn(obj[key], key))
}

Test case

  it(&#39;forEachValue&#39;, () => {
    let number = 1

    function plus (value, key) {
      number += value
    }
    const origin = {
      a: 1,
      b: 3
    }

    forEachValue(origin, plus)
    expect(number).toEqual(5)
  })
Analysis:

A Function that traverses objects (supports objects and arrays)

  • fn(value, key)
  • But the first parameter of the callback function is the value, and the second parameter is the key Value
  • isObject
export function isObject (obj) {
  return obj !== null && typeof obj === &#39;object&#39;
}

Test Case

  it(&#39;isObject&#39;, () => {
    expect(isObject(1)).toBe(false)
    expect(isObject(&#39;String&#39;)).toBe(false)
    expect(isObject(undefined)).toBe(false)
    expect(isObject({})).toBe(true)
    expect(isObject(null)).toBe(false)
    expect(isObject([])).toBe(true)
    expect(isObject(new Function())).toBe(false)
  })
Resolution:

Determine whether it is an object. There is no judgment here whether it is a native object. Arrays also pass.

  • Because typeof null === 'object' must first determine whether it is null

  • ##isPromise

  • export function isPromise (val) {
      return val && typeof val.then === &#39;function&#39;
    }
Test case

  it(&#39;isPromise&#39;, () => {
    const promise = new Promise(() => {}, () => {})
    expect(isPromise(1)).toBe(false)
    expect(isPromise(promise)).toBe(true)
    expect(isPromise(new Function())).toBe(false)
  })
Analysis:

Determine whether it is a Promise

  • First determine whether val is not undefined, and then You can judge val.then to avoid reporting errors

  • The basis for judgment is whether val.then is a function

  • assert

  • export function assert (condition, msg) {
      if (!condition) throw new Error(`[vuex] ${msg}`)
    }
Test case:

  it(&#39;assert&#39;, () => {
    expect(assert.bind(null, false, &#39;Hello&#39;)).toThrowError(&#39;[vuex] Hello&#39;)
  })
Analysis:

Assertion function, assertion does not pass an Error

that throws a custom error message
  • index.js

    and
  • index.esm.js

index.js
import { Store, install } from &#39;./store&#39;
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from &#39;./helpers&#39;

export default {
  Store,
  install,
  version: &#39;__VERSION__&#39;,
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

index.esm.js

import { Store, install } from &#39;./store&#39;
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from &#39;./helpers&#39;

export default {
  Store,
  install,
  version: &#39;__VERSION__&#39;,
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

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

Analysis:

The difference is that

index.esm.js

is better than
    index. js
  • There is an additional import mode

    import Vuex, { mapState } from 'index.esm.js'

    : There are two ways to import
  • import Vuex from 'index.js'

    : There is only one way to import
  • ##mixin.js<pre class="brush:js;toolbar:false;">export default function (Vue) { const version = Number(Vue.version.split(&amp;#39;.&amp;#39;)[0]) if (version &gt;= 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 === &amp;#39;function&amp;#39; ? options.store() : options.store } else if (options.parent &amp;&amp; options.parent.$store) { this.$store = options.parent.$store } } }</pre>Analysis:

Why every component has

\(store attribute, that is, every component can get\)

store

  • Vue2 directly uses the mixin and hook function beforeCreate, and Vue1 uses the appearance (decorator) mode to rewrite the Vue._init function.

  • vuexInit

    is to inject the globally registered store into the current component, before creating the component

  • \(options are options of `new Vue(options)`,\)There is store

    in options
  • Since beforeCreate is the cycle hook of Vue, this points to the current component instance, so this.$storeThe store can be directly injected into the current component

  • All components are inherited from a global Vue, the global mixin component cycle hookbeforeCreate, so that each component Can be automatically injected into the store, that is, each component can get the global Vuenew Vue({ el: 'app', store, router }) directly through $store #store


Related recommendations:

Talk about me Understanding of vuex

Vue.js-vuex (state management)

The above is the detailed content of A brief analysis of the source code content of VueX. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn