目次
util.js
index.jsindex.esm.js
mixin.js
ホームページ ウェブフロントエンド jsチュートリアル VueX のソースコード内容の簡単な分析

VueX のソースコード内容の簡単な分析

Jul 20, 2018 pm 03:30 PM

Vuex は、Vue.js アプリケーション専用に開発された状態管理パターンです。 Vuex は、多数のコンポーネントと複雑な対話を含む単一ページ アプリケーションの場合、コンポーネント間のデータ共有と変更を容易にする、便利で正確かつ予測可能な状態管理方法を提供します。

ファイル構造は以下の通りです

  • /module/module

  • /plugins

  • helpers.js

  • index.esm.js

  • index.js

  • store.js

  • util.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 !== &#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
}
ログイン後にコピー

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)
  })
ログイン後にコピー

解析:

  • 功能:支持循环引用的深克隆函数

  • 第一个if判断obj === null || typeof obj !== &#39;object&#39;判断如果不是引用类型直接返回(基本类型是值拷贝),也是递归的一个出口。

  • 第二个判断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(&#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)
  })
ログイン後にコピー

解析:

  • 一个遍历对象的函数(支持对象和数组)

  • fn(value, key)但是回调函数第一个参数是值,第二个参数是键值

isObject

export function isObject (obj) {
  return obj !== null && typeof obj === &#39;object&#39;
}
ログイン後にコピー

测试用例

  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)
  })
ログイン後にコピー

解析:

  • 判断是不是对象,这里没有判断是不是原生对象,数组也是通过的。

  • 由于typeof null === 'object'要先判断是不是null

isPromise

export function isPromise (val) {
  return val && typeof val.then === &#39;function&#39;
}
ログイン後にコピー

测试用例

  it(&#39;isPromise&#39;, () => {
    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(&#39;assert&#39;, () => {
    expect(assert.bind(null, false, &#39;Hello&#39;)).toThrowError(&#39;[vuex] Hello&#39;)
  })
ログイン後にコピー

解析:

  • 断言函数,断言不通过抛出一个自定义错误信息的Error

index.jsindex.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
}
ログイン後にコピー

解析:

  • 区别就是index.esm.jsindex.js多了个一个导入模式

  • import Vuex, { mapState } from &#39;index.esm.js&#39;:有两种方式导入

  • import Vuex from &#39;index.js&#39;:只有一种方式导入

mixin.js

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
    }
  }
}
ログイン後にコピー

解析:

  • 为什么每个组件都拥有(store属性,也即每个组件都能拿到)store

  • Vue2直接用mixin和钩子函数beforeCreate,Vue1用外观(装饰者)模式重写Vue._init函数。

  • vuexInit

  • /plugins

    helpers.js 🎜🎜🎜 🎜index.esm.js🎜🎜🎜🎜index.js🎜🎜🎜🎜store.js🎜🎜🎜🎜util.js🎜🎜

util.js

🎜最も単純なユーティリティ関数から始めます。 🎜🎜🎜find関数🎜🎜rrreee🎜find関数のテストケース🎜rrreee🎜分析: 🎜🎜🎜🎜最初にassert function fを使用してlist listをフィルタリングし、最後にフィルタリングされたリストの最初の要素。 🎜🎜🎜🎜deepCopy関数🎜🎜rrreee🎜deepCopyのテストケース🎜rrreee🎜分析:🎜🎜🎜🎜機能:循環参照をサポートするディープクローニング機能🎜🎜🎜🎜First if判定obj = == null || typeof obj !== 'object' 参照型でない場合は、直接返されます (基本型は値のコピーです)。これは再帰の出口でもあります。
🎜🎜🎜🎜 2 番目の 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)` のオプションです)🎜オプションがありますお店🎜
  • beforeCreateVue のサイクル フックであるため、this は現在のコンポーネント インスタンスを指しているため、this.$store ストアは現在のコンポーネントに直接注入できます<code>beforeCreateVue的周期钩子,this指向当前组件实例,所以this.$store可以把store直接注入当前组件

  • 所有组件都是继承于一个全局Vue的,全局mixin组件周期钩子beforeCreate,这样每个组件都能自动注入store,也即每个组件都能直接通过$store拿到全局Vuenew Vue({ el: 'app', store, router })store


  • 相关推荐:

    谈谈我对vuex的理解

    すべてのコンポーネントはグローバル Vue、グローバル ミックスイン コンポーネント サイクル フック beforeCreate から継承されるため、各コンポーネントは自動的にストアを注入できますつまり、すべてのコンポーネントは、$ を介してグローバル Vue<code>new Vue({ el: 'app', store, router })store を直接取得できます。ストア

    🎜🎜関連の推奨事項: 🎜🎜vuex についての私の理解について話します🎜🎜🎜🎜Vue.js vuex (状態管理)🎜🎜

    以上がVueX のソースコード内容の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    AI Hentai Generator

    AI Hentai Generator

    AIヘンタイを無料で生成します。

    ホットツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい

    ゼンドスタジオ 13.0.1

    ゼンドスタジオ 13.0.1

    強力な PHP 統合開発環境

    ドリームウィーバー CS6

    ドリームウィーバー CS6

    ビジュアル Web 開発ツール

    SublimeText3 Mac版

    SublimeText3 Mac版

    神レベルのコード編集ソフト(SublimeText3)

    独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? 独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? Mar 18, 2025 pm 03:12 PM

    記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

    ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? Mar 18, 2025 pm 03:14 PM

    この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

    フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? Apr 04, 2025 pm 02:42 PM

    フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

    ブラウザ開発者ツールを使用してJavaScriptコードを効果的にデバッグするにはどうすればよいですか? ブラウザ開発者ツールを使用してJavaScriptコードを効果的にデバッグするにはどうすればよいですか? Mar 18, 2025 pm 03:16 PM

    この記事では、ブラウザ開発者ツールを使用した効果的なJavaScriptデバッグについて説明し、ブレークポイントの設定、コンソールの使用、パフォーマンスの分析に焦点を当てています。

    誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

    スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

    ソースマップを使用して、マイナイドJavaScriptコードをデバッグするにはどうすればよいですか? ソースマップを使用して、マイナイドJavaScriptコードをデバッグするにはどうすればよいですか? Mar 18, 2025 pm 03:17 PM

    この記事では、ソースマップを使用して、元のコードにマッピングすることにより、Minified JavaScriptをデバッグする方法について説明します。ソースマップの有効化、ブレークポイントの設定、Chrome DevtoolsやWebpackなどのツールの使用について説明します。

    Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Apr 04, 2025 pm 05:12 PM

    Console.log出力の違いの根本原因に関する詳細な議論。この記事では、Console.log関数の出力結果の違いをコードの一部で分析し、その背後にある理由を説明します。 �...

    初心者向けのタイプスクリプト、パート2:基本データ型 初心者向けのタイプスクリプト、パート2:基本データ型 Mar 19, 2025 am 09:10 AM

    エントリーレベルのタイプスクリプトチュートリアルをマスターしたら、TypeScriptをサポートするIDEで独自のコードを作成し、JavaScriptにコンパイルできるはずです。このチュートリアルは、TypeScriptのさまざまなデータ型に飛び込みます。 JavaScriptには、NULL、未定義、ブール値、数字、文字列、シンボル(ES6によって導入)とオブジェクトの7つのデータ型があります。 TypeScriptはこれに基づいてより多くのタイプを定義し、このチュートリアルではすべてを詳細に説明します。 ヌルデータ型 JavaScriptのように、Typescriptのnull

    See all articles