ホームページ > 記事 > ウェブフロントエンド > VueX のソースコード内容の簡単な分析
Vuex は、Vue.js アプリケーション専用に開発された状態管理パターンです。 Vuex は、多数のコンポーネントと複雑な対話を含む単一ページ アプリケーションの場合、コンポーネント間のデータ共有と変更を容易にする、便利で正確かつ予測可能な状態管理方法を提供します。
ファイル構造は以下の通りです
/module
/module
/plugins
helpers.js
index.esm.js
index.js
store.js
util.js
先从最简单的工具函数开始。
find函数
/** * 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] }
find函数的测试用例
it('find', () => { const list = [33, 22, 112, 222, 43] expect(find(list, function (a) { return a % 2 === 0 })).toEqual(22) })
解析:
先用断言函数f
过滤列表list
,最后取过滤后列表的第一个元素。
deepCopy函数
/** * 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 }
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) })
解析:
功能:支持循环引用的深克隆函数
第一个if判断obj === null || typeof obj !== 'object'
判断如果不是引用类型直接返回(基本类型是值拷贝),也是递归的一个出口。
第二个判断hit
是判断是不是循环引用,由于是循环引用,在cache中应该有缓存到一份拷贝,直接取cache的,避免再次重复拷贝一份。
什么是循环引用看测试用例第三个original.circular = original
,循环引用和被引用的内容是一样的,用缓存就是避免重复的克隆(内容一样)
original.circular
是循环引用,original
是被循环引用
先把cope
放到cache
中,是在递归的时候,如果遇到循环引用,要确保cache中有一份被循环引用的copy
,但是copy
必须是引用类型。
为什么cope
必须是引用类型?循环引用
保存的是引用不是内容(这时候还没拷贝完),在递归的时候并未完成拷贝,只有递归跑完了才完成拷贝,这样未来被循环引用
的内容改变时(拷贝完),循环引用
的内容同步改变
所以const copy = Array.isArray(obj) ? [] : {}
必须是引用类型。
最后Object.keys
可以遍历对象和数组的所有键名(只返回实例的属性,不包含原型链和Symbol),实现递归克隆。
一共两个出口,一个是基本类型,另一个是循环引用。
forEachValue
/** * forEach for object */ export function forEachValue (obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)) }
测试用例
it('forEachValue', () => { let number = 1 function plus (value, key) { number += value } const origin = { a: 1, b: 3 } forEachValue(origin, plus) expect(number).toEqual(5) })
解析:
一个遍历对象的函数(支持对象和数组)
fn(value, key)
但是回调函数第一个参数是值,第二个参数是键值
isObject
export function isObject (obj) { return obj !== null && typeof obj === 'object' }
测试用例
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) })
解析:
判断是不是对象,这里没有判断是不是原生对象,数组也是通过的。
由于typeof null === 'object'要先判断是不是null
isPromise
export function isPromise (val) { return val && typeof val.then === 'function' }
测试用例
it('isPromise', () => { const promise = new Promise(() => {}, () => {}) expect(isPromise(1)).toBe(false) expect(isPromise(promise)).toBe(true) expect(isPromise(new Function())).toBe(false) })
解析:
判断是不是Promise
首先判断val不是undefined,然后才可以判断val.then,避免报错
判断依据是val.then是不是函数
assert
export function assert (condition, msg) { if (!condition) throw new Error(`[vuex] ${msg}`) }
测试用例:
it('assert', () => { expect(assert.bind(null, false, 'Hello')).toThrowError('[vuex] Hello') })
解析:
断言函数,断言不通过抛出一个自定义错误信息的Error
index.js
和index.esm.js
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 }
解析:
区别就是index.esm.js
比index.js
多了个一个导入模式
import Vuex, { mapState } from 'index.esm.js'
:有两种方式导入
import Vuex from 'index.js'
:只有一种方式导入
export default function (Vue) { const version = Number(Vue.version.split('.')[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 === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store } } }
解析:
为什么每个组件都拥有(store属性,也即每个组件都能拿到)store
Vue2直接用mixin和钩子函数beforeCreate,Vue1用外观(装饰者)模式重写Vue._init函数。
vuexInit
/plugins
helpers.js
🎜🎜🎜 🎜index.esm.js
🎜🎜🎜🎜index.js
🎜🎜🎜🎜store.js
🎜🎜🎜🎜util.js
🎜🎜assert function f
を使用してlist list
をフィルタリングし、最後にフィルタリングされたリストの最初の要素。 🎜🎜🎜🎜deepCopy関数🎜🎜rrreee🎜deepCopyのテストケース🎜rrreee🎜分析:🎜🎜🎜🎜機能:循環参照をサポートするディープクローニング機能🎜🎜🎜🎜First if判定obj = == null || typeof obj !== 'object'
参照型でない場合は、直接返されます (基本型は値のコピーです)。これは再帰の出口でもあります。 hit
は、循環参照であるかどうかを判断します。循環参照なので、キャッシュにキャッシュされているコピーがあるはずです。再度同じことを繰り返さないように、重複したコピーを作成します。 🎜🎜🎜🎜 循環参照とは何ですか? 3 番目のテスト ケース original.circular =original
を参照してください。 キャッシュは、複製の繰り返しを避けるために使用されます。同じ)🎜 🎜🎜🎜original.circular
は循環参照、original
は循環参照です 🎜🎜🎜🎜まず cope
を に入れます>キャッシュ、再帰時に循環参照が発生した場合は、キャッシュ内に循環参照される <code>copy
があることを確認してください。ただし、copy
は参照型。 🎜🎜🎜🎜なぜ cope
は参照型でなければならないのですか? 循環参照
は、コンテンツではなく参照を保存します (この時点ではコピーは完了していません)。この場合、コピーは再帰が完了した後でのみ完了します。ちなみに、 は将来循環参照されます の内容が変更されると (コピーが完了すると)、<code>循環参照
の内容も同時に変更されます🎜🎜🎜🎜ので const copy = Array.isArray(obj) ? [] : {} code> は参照型である必要があります。 🎜🎜🎜🎜最後に、<code>Object.keys
は、オブジェクトと配列のすべてのキー名を走査して (プロトタイプ チェーンとシンボルを除く、インスタンスの属性のみを返します)、再帰的クローン作成を実装します。 🎜🎜🎜🎜出口は合計 2 つあり、1 つは基本タイプ、もう 1 つは循環参照です。 🎜🎜🎜🎜forEachValue🎜🎜rrreee🎜テストケース🎜rrreee🎜分析: 🎜🎜🎜🎜オブジェクトを走査する関数(オブジェクトと配列をサポート)🎜🎜🎜🎜fn(value, key) code>しかし、コールバック関数の最初のパラメータは値であり、2 番目のパラメータはキー値です🎜🎜🎜🎜isObject🎜🎜rrreee🎜テストケース🎜rrreee🎜分析: 🎜🎜🎜🎜オブジェクトです。ここには何もありません。ネイティブ オブジェクトかどうかを判断するために、配列も渡します。 🎜🎜🎜🎜typeof null === 'object' はまず null かどうかを判断する必要があるため🎜🎜🎜🎜isPromise🎜🎜rrreee🎜テストケース🎜rrreee🎜分析: 🎜🎜🎜🎜お約束🎜🎜🎜 🎜最初にvalが未定義ではないかどうかを判断し、次にval.thenを判断してエラーの報告を避けることができます🎜🎜🎜🎜判断の根拠はval.thenが関数であるかどうかです🎜🎜🎜🎜assert 🎜🎜rrreee🎜テストケース: 🎜rrreee🎜分析: 🎜🎜🎜🎜Assert関数、カスタムエラーメッセージError🎜🎜<h4>
<code>index.js
およびindex.esm.js code>
🎜index.js
🎜rrreee🎜index.esm.js
🎜rrreee🎜分析: 🎜🎜🎜🎜違いは、index.esm.js
には index.js
よりもインポート モードが 1 つ多いことです🎜🎜🎜🎜import Vuex、{mapState} from 'index.esm.js '
: 2 つあります インポートする方法は 1 つだけです 🎜🎜🎜🎜import Vuex from 'index.js'
: インポートする方法は 1 つだけです 🎜🎜mixin.jsrrreee🎜分析: 🎜🎜 🎜🎜なぜすべてのコンポーネントには 🎜(ストア属性、つまり、すべてのコンポーネントが取得できる) があるのですか🎜store🎜🎜🎜🎜Vue2 は、Create の前に mixin とフック関数を直接使用します。 , 一方、Vue1 は、_init 関数を使用して Vue を書き換えます。 🎜🎜🎜🎜vuexInit
は、コンポーネントを作成する前に、グローバルに登録されたストアを現在のコンポーネントに挿入します🎜🎜🎜🎜🎜(オプションは `new Vue(options)` のオプションです)🎜オプションがありますお店🎜beforeCreate
は Vue
のサイクル フックであるため、this
は現在のコンポーネント インスタンスを指しているため、this.$store ストアは現在のコンポーネントに直接注入できます<code>beforeCreate
是Vue
的周期钩子,this
指向当前组件实例,所以this.$store
可以把store直接注入当前组件
所有组件都是继承于一个全局Vue的,全局mixin组件周期钩子beforeCreate
,这样每个组件都能自动注入store,也即每个组件都能直接通过$store
拿到全局Vuenew Vue({ el: 'app', store, router })
的store
相关推荐:
すべてのコンポーネントはグローバル Vue、グローバル ミックスイン コンポーネント サイクル フック beforeCreate
から継承されるため、各コンポーネントは自動的にストアを注入できますつまり、すべてのコンポーネントは、$ を介してグローバル Vue<code>new Vue({ el: 'app', store, router })
の store
を直接取得できます。ストア
関連の推奨事項:
🎜🎜vuex についての私の理解について話します
🎜🎜🎜🎜Vue.js vuex (状態管理)🎜🎜以上がVueX のソースコード内容の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。