Home > Article > Web Front-end > A brief analysis of the source code content of VueX
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
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:
Filters list list
, and finally takes the first element of the filtered list.
/**
* 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 !== 'object') {
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('deepCopy: nornal structure', () => { const original = { a: 1, b: 'string', c: true, d: null, e: undefined } const copy = deepCopy(original) expect(copy).toEqual(original) }) // 嵌套结构 it('deepCopy: nested structure', () => { const original = { a: { b: 1, c: [2, 3, { d: 4 }] } } const copy = deepCopy(original) expect(copy).toEqual(original) }) // 循环引用结构 it('deepCopy: circular structure', () => { const original = { a: 1 } original.circular = original const copy = deepCopy(original) expect(copy).toEqual(original) })
Analysis:
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.
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.
. The circular reference and the referenced content are the same. Caching is used to avoid duplication. Clone (the content is the same)
is a circular reference, original
is a circular reference
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.
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
/** * forEach for object */ export function forEachValue (obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)) }
Test case it('forEachValue', () => {
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)
export function isObject (obj) { return obj !== null && typeof obj === 'object' }
Test Case it('isObject', () => {
expect(isObject(1)).toBe(false)
expect(isObject('String')).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 === 'function' }Test case
it('isPromise', () => { 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
assert
export function assert (condition, msg) { if (!condition) throw new Error(`[vuex] ${msg}`) }Test case:
it('assert', () => { expect(assert.bind(null, false, 'Hello')).toThrowError('[vuex] Hello') })Analysis: Assertion function, assertion does not pass an Error that throws a custom error message
index.js
and
index.js
import { Store, install } from './store' import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' export default { Store, install, version: '__VERSION__', mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers }
index.esm.js
import { Store, install } from './store' import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' export default { Store, install, version: '__VERSION__', mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } export { Store, install, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers }Analysis:
index.esm.js
is better thanimport Vuex, { mapState } from 'index.esm.js'
import Vuex from 'index.js'
##mixin.js<pre class="brush:js;toolbar:false;">export default function (Vue) {
const version = Number(Vue.version.split(&#39;.&#39;)[0])
if (version >= 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 === &#39;function&#39;
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}</pre>
Analysis:
store
Vue2 directly uses the mixin and hook function beforeCreate, and Vue1 uses the appearance (decorator) mode to rewrite the Vue._init function.
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
Since beforeCreate
is the cycle hook of Vue
, this
points to the current component instance, so this.$store
The 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!