Vuex源碼閱讀(一) new Vuex.Store()內部探究

来源:https://www.cnblogs.com/polk6/archive/2020/06/09/13065289.html
-Advertisement-
Play Games

Vuex源碼閱讀(一) ,介紹Vuex的執行順序,以及new Vuex.Store()的時候內部都幹了什麼。 ...


1. 前言

Vuex版本:3.4.0

Vuex倉庫:https://github.com/vuejs/vuex

Vux文檔:https://vuex.vuejs.org/zh/guide/

文章時間:2020-06-09

 

2. 執行順序

首先看個簡單的代碼塊:

// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);

let baseStore = new Vuex.Store({
    state: {
        count: 0
    },
});
export default baseStore;

// app.js
import Vue from 'vue'
import store from './store'

new Vue({
    el: '#app',
    store
})

 

2.1 第一步:Vue.use(Vuex)

說明:這一步是Vuex在Vue的beforeCreate事件內增加一個回調函數,其目的是為把初始化後的store對象掛載到this.$store,即Vue.$store。

代碼

Vue.mixin({ beforeCreate: vuexInit });

function vuexInit() {
    const options = this.$options;
    // store injection store註入
    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;
    }
}

  

2.2 第二步:new Vuex.Store({})

說明:初始化具體的store對象。

 

2.3 第三步:new Vue({ store })

說明:這裡主要是為了執行第一步的代碼,因為第一步的Vue.use(Vuex)只是註入一個回調,內部的條件判斷options.store 和 options.parent && options.parent.$store都沒有生效,只有在這一步時才會生效,其目的就向上面說的把初始化後的store對象掛載到this.$store,這樣所有子組件就可以通過this.$store調用store對象。

代碼

new Vue({
	el: '#app',
	router,
	components: { App },
	store: {
		baseStore: baseStore
	},
	template: '<App/>'
});

 

3. 探究new Vuex.Store({})

說明:這裡將著重介紹new Vuex.Store({})都幹了什麼。

註意:此處的講解都是以使用單一狀態樹為前提條件,沒有Module以及Module內的namespaced等知識點,modules這塊會單獨講解。

 

3.1 重新綁定dispatch、commit

說明:此處重新綁定dispatch、commit方法,把store自身插入到第一個參數前面。

這也是為什麼我們在外部調用this.$store.dispatch('actionName')時,所創建的action第一個參數為store本身。

代碼

this.dispatch = function boundDispatch (type, payload) {
    return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
    return commit.call(store, type, payload, options)
}

 

3.2 轉換為Module對象

說明:在這裡Vuex將傳入的Vuex代碼解析為Model對象(後面將以options表示傳入的代碼塊):

new Vuex.Store({
    state: {
        count: 0
    },
    getters,
    actions,
    mutations
});

在Vuex源碼中,會將options轉換為Module集合:

代碼

// src/store.js
this._modules = new ModuleCollection(options)

其this._modules,即Model對象初始化後的欄位含義為:

root: { // Module對象
    state:{
    count: 0
  } // 傳入Vuex({options})內的state
    _children: {} // 內部嵌套Module
    _rawModule: options // 傳入的options對象
}

 

3.3 installModule

說明:在這裡將對Module進行封裝處理。

處理步驟

1) 若module.namespaced = true : 此Module將被加入store._modulesNamespaceMap內,其key為Module嵌套的路徑。

if (module.namespaced) {
    store._modulesNamespaceMap[namespace] = module
}

2) 非root Module時:子Module.state註入到父節點的state對象里。

 if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1)) // path.slice(0, -1) 表示只返回前面的父節點
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
        Vue.set(parentState, moduleName, module.state)
    })
}

3)  對store進行局部話,這裡主要對module.namespaced= true 的module進行另外處理,其內部的成員都需要進行namespace路徑處理處理。

官方說明:預設情況下,模塊內部的 action、mutation 和 getter 是註冊在全局命名空間的——這樣使得多個模塊能夠對同一 mutation 或 action 作出響應。 如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。當模塊被註冊後,它的所有 getter、action 及 mutation 都會自動根據模塊註冊的路徑調整命名。

4) 對module的mutation進行封裝:

  ①添加到store._mutations數組內,_mutations的key預設為mutation的名稱,如果module.namespaced = true,那麼key就為namespace+mutation的名稱。可多個module註冊同名。

  ②將module.mutation第二個參數修改為局部化的state。

const entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function wrappedMutationHandler(payload) {
    handler.call(store, local.state, payload);
});            

5) 對module的action進行封裝:

  ①添加到store._actions數組內,_actions的key預設為action的名稱,如果module.namespaced = true,那麼key就為namespace+action的名稱。可多個module註冊同名。

  ②將module.action第二個參數修改為局部化和root的state。

onst entry = store._actions[type] || (store._actions[type] = []);
entry.push(function wrappedActionHandler(payload) {
	let res = handler.call(
		store,
		{
			dispatch: local.dispatch,
			commit: local.commit,
			getters: local.getters,
			state: local.state,
			rootGetters: store.getters,
			rootState: store.state
		},
		payload
	);
	if (!isPromise(res)) {
		res = Promise.resolve(res);
	}
	if (store._devtoolHook) {
		return res.catch((err) => {
			store._devtoolHook.emit('vuex:error', err);
			throw err;
		});
	} else {
		return res;
	}
});

6) 對module的getter進行封裝:

  ①添加到store._wrappedGetters數組內,_wrappedGetters的key預設為action的名稱,如果module.namespaced = true,那麼key就為namespace+action的名稱。只能單一註冊。

  ②module.mutation第一個參數修改為局部化和root的state。

if (store._wrappedGetters[type]) {
	if (__DEV__) {
		console.error(`[vuex] duplicate getter key: ${type}`);
	}
	return;
}
store._wrappedGetters[type] = function wrappedGetter(store) {
	return rawGetter(
		local.state, // local state
		local.getters, // local getters
		store.state, // root state
		store.getters // root getters
	);
};

7) 若當前module含有子module時,遍歷當前model的_children屬性,迭代執行installModule。

 

3.4 resetStoreVM

說明:初始化storevm,並負責將getter註冊為計算屬性,並保存緩存特性。

處理步驟:

1) 遍歷wrappedGetters,封裝到store.getters里。

forEachValue(wrappedGetters, (fn, key) => {
    // 使用computed來利用其延遲緩存機制
    // 直接內聯函數的使用將導致保留oldVm的閉包。
    // 使用partial返回只保留閉包環境中的參數的函數。
    // use computed to leverage its lazy-caching mechanism
    // direct inline function use will lead to closure preserving oldVm.
    // using partial to return function with only arguments preserved in closure environment.
    computed[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
})

2) 初始化store._vm,傳入data和computed。

說明:這裡主要把state與getter(computed)進行綁定,state有更改時,getter(computed)進行自動更新,採用的方式就是本地創建一個Vue對象。

store._vm = new Vue({
    data: {
        $$state: state
    },
    computed
})

 

3.5 註冊插件

 

4. commit()執行了什麼

當在action內調用了commit方法時,其內部步驟如下:

1) 從store._mutations[]數組內讀取對應的mutation名稱的數組。

2) 從獲取到的_mutations[]數組遍歷執行對應的mutation處理程式。

3) 觸發store._subscribers對應的回調。

 

5. dispatch()執行了什麼

在通過this.$store.dispatch()調用對應的action時,其其內部步驟如下:

1) 與commit()調用的方法類似,從store._actions[]數組內獲取對應的acticon數組。

2) 執行active對應的處理成語並以Promise返回。

 

End Web開發之路系列文章 菜單載入中...
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文源碼:GitHub·點這裡 || GitEE·點這裡 一、索引簡介 1、基本概念 首先要明確索引是什麼:索引是一種數據結構,數據結構是電腦存儲、組織數據的方式,是指相互之間存在一種或多種特定關係的數據元素的集合,例如:鏈表,堆棧,隊列,二叉樹等等。 其次要清楚索引的作用:索引可以使存儲引擎快速 ...
  • MySQL在對結果集排序的時候,可以根據某些欄位排序,也可以通過field函數自定義任意排序。 語法 ... order by field(value,str1,str2,str3,str4,,,strn) 示例 select * from score order by field(level,'A ...
  • 有一個 ? 遇到這樣一個疑問:當where查詢中In一個索引欄位作為條件,那麼在查詢中還會使用到索引嗎? SELECT * FROM table_name WHERE column_index in (expr) 上面的sql語句檢索會使用到索引嗎?帶著這個問題,在網上查找了很多文章,但是有的說 i ...
  • 概述 連接docker中的mysql終端(docker連接終端) 修改mysql配置 (vim使用和安裝) mysql大小寫簡單介紹 (庫名/錶面 欄位/內容 大小寫使用) docker下mysql終端操作(執行命令) 連接docker中的mysql終端 使用 查詢docker下的鏡像用來查看mys ...
  • 一、活動里的方法 (1)onCreate方法在活動創建之後必定執行的方法。 (2)Andorid程式講究​邏輯和視圖相分離; (3)setContentView()靜態方法,用於引入​視圖; (4)Android程式不推薦在程式中對字元串進行硬編碼​,最好的做法就是一般把字元串定義到res/valu ...
  • ##BindView ButterKnife 優勢 綁定組件方便,使用簡單 處理點擊事件方便,如adapter中的viewholder 同時父組件綁定後子組件無需綁定 註意 在setcontentview之後使用,且子空間不可再使用static final屬性 ##在不改變按鈕圖片大小的情況,擴大點 ...
  • 新聞 Android 11特性調整:安裝外部來源應用需要重啟APP Google Messages beta版迎來一個新的搜索框 Android開發者生態永遠比不上iOS?“聯盟與公約”們正改變這一點 谷歌新款Android TV串流設備外形曝光:代號Sabrina Android 11 Beta引 ...
  • 前言 大白(Baymax),迪士尼動畫《超能陸戰隊》中的健康機器人,是一個體型胖胖的充氣機器人,因呆萌的外表和善良的本質獲得大家的喜愛,被稱為“萌神”。 Baymax項目是為了減少開發人員在開發中一些不規範的代碼編寫造成的記憶體泄露,界面卡頓,耗電等問題而來的一個監控系統。 現在Baymax迎來了它新 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...