目錄
util.js
#index.jsindex.esm.js
解析:
首頁 web前端 js教程 淺析VueX的原始碼內容

淺析VueX的原始碼內容

Jul 20, 2018 pm 03:30 PM

Vuex是一個專門為Vue.js應用程式開發的狀態管理模式。針對組件繁多互動複雜的單一頁面應用,Vuex提供了一種便利、準確和可預測的狀態管理方式,方便組件之間的資料共享和修改。

檔案架構如下

  • /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 !== '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(&#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##<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>import { Store, install } from &amp;#39;./store&amp;#39; import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from &amp;#39;./helpers&amp;#39; export default { Store, install, version: &amp;#39;__VERSION__&amp;#39;, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers }</pre><div class="contentsignin">登入後複製</div></div>

index.esm.js

<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>import { Store, install } from &amp;#39;./store&amp;#39; import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from &amp;#39;./helpers&amp;#39; export default { Store, install, version: &amp;#39;__VERSION__&amp;#39;, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } export { Store, install, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers }</pre><div class="contentsignin">登入後複製</div></div>解析:

    #差異就是
  • index.esm.js

    index. js多了一個導入模式

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

    :有兩種方式導入

  • import Vuex from 'index.js'

    :只有一種方式導入

  • 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

    是將全域註冊的store注入到目前元件中,在建立該元件之前

  • # #\(options是`new Vue(options)`的options,\)
  • options中有store

  • 由於beforeCreateVue的週期鉤子,this指向目前元件實例,所以this.$store可以把store直接注入目前元件

  • 所有元件都是繼承於一個全域Vue的,全域mixin元件週期鉤子beforeCreate,這樣每個元件都能自動注入store,也也就是每個元件都能直接透過$store拿到全域Vuenew Vue({ el: 'app', store, router })store


相關推薦:

##談談我對vuex的理解

Vue.js之vuex(狀態管理)

以上是淺析VueX的原始碼內容的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

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調試,專注於設置斷點,使用控制台和分析性能。

如何有效地使用Java的收藏框架? 如何有效地使用Java的收藏框架? Mar 13, 2025 pm 12:28 PM

本文探討了Java收藏框架的有效使用。 它強調根據數據結構,性能需求和線程安全選擇適當的收集(列表,設置,地圖,隊列)。 通過高效優化收集用法

如何使用源地圖調試縮小JavaScript代碼? 如何使用源地圖調試縮小JavaScript代碼? Mar 18, 2025 pm 03:17 PM

本文說明瞭如何使用源地圖通過將其映射回原始代碼來調試JAVASCRIPT。它討論了啟用源地圖,設置斷點以及使用Chrome DevTools和WebPack之類的工具。

開始使用Chart.js:PIE,DONUT和BUBBLE圖表 開始使用Chart.js:PIE,DONUT和BUBBLE圖表 Mar 15, 2025 am 09:19 AM

本教程將介紹如何使用 Chart.js 創建餅圖、環形圖和氣泡圖。此前,我們已學習了 Chart.js 的四種圖表類型:折線圖和條形圖(教程二),以及雷達圖和極地區域圖(教程三)。 創建餅圖和環形圖 餅圖和環形圖非常適合展示某個整體被劃分為不同部分的比例。例如,可以使用餅圖展示野生動物園中雄獅、雌獅和幼獅的百分比,或不同候選人在選舉中獲得的投票百分比。 餅圖僅適用於比較單個參數或數據集。需要注意的是,餅圖無法繪製值為零的實體,因為餅圖中扇形的角度取決於數據點的數值大小。這意味著任何占比為零的實體

誰得到更多的Python或JavaScript? 誰得到更多的Python或JavaScript? Apr 04, 2025 am 12:09 AM

Python和JavaScript開發者的薪資沒有絕對的高低,具體取決於技能和行業需求。 1.Python在數據科學和機器學習領域可能薪資更高。 2.JavaScript在前端和全棧開發中需求大,薪資也可觀。 3.影響因素包括經驗、地理位置、公司規模和特定技能。

See all articles