1. 程式人生 > >深入理解Vuex 框架

深入理解Vuex 框架

Vuex是一個專為Vue服務,用於管理頁面資料狀態、提供統一資料操作的生態系統。它集中於MVC模式中的Model層,規定所有的資料操作必須通過 action – mutation – state change 的流程來進行,再結合Vue的資料檢視雙向繫結特性來實現頁面的展示更新。統一的頁面狀態管理以及操作處理,可以讓複雜的元件互動變得簡單清晰,同時可在除錯模式下進行時光機般的倒退前進操作,檢視資料改變過程,使code debug更加方便。

最近在開發的專案中用到了Vuex來管理整體頁面狀態,遇到了很多問題。決定研究下原始碼,在答疑解惑之外,能深入學習其實現原理。

先將問題丟擲來,使學習和研究更有針對性:

  1. 使用Vuex只需執行 Vue.use(Vuex),並在Vue的配置中傳入一個store物件的示例,store是如何實現注入的?
  2. state內部是如何實現支援模組配置和模組巢狀的?
  3. 在執行dispatch觸發action(commit同理)的時候,只需傳入(type, payload),action執行函式中第一個引數store從哪裡獲取的?
  4. 如何區分state是外部直接修改,還是通過mutation方法修改的?
  5. 除錯時的“時空穿梭”功能是如何實現的?

注:本文對有Vuex有實際使用經驗的同學幫助更大,能更清晰理解Vuex的工作流程和原理,使用起來更得心應手。初次接觸的同學,可以先參考Vuex

官方文件進行基礎概念的學習。

一、框架核心流程

進行原始碼分析之前,先了解一下官方文件中提供的核心思想圖,它也代表著整個Vuex框架的執行流程。
vuex-core
如圖示,Vuex為Vue Components建立起了一個完整的生態圈,包括開發中的API呼叫一環。圍繞這個生態圈,簡要介紹一下各模組在核心流程中的主要功能:

  • Vue Components:Vue元件。HTML頁面上,負責接收使用者操作等互動行為,執行dispatch方法觸發對應action進行迴應。
  • dispatch:操作行為觸發方法,是唯一能執行action的方法。
  • actions:操作行為處理模組。負責處理Vue Components接收到的所有互動行為。包含同步/非同步操作,支援多個同名方法,按照註冊的順序依次觸發。向後臺API請求的操作就在這個模組中進行,包括觸發其他action以及提交mutation的操作。該模組提供了Promise的封裝,以支援action的鏈式觸發。
  • commit:狀態改變提交操作方法。對mutation進行提交,是唯一能執行mutation的方法。
  • mutations:狀態改變操作方法。是Vuex修改state的唯一推薦方法,其他修改方式在嚴格模式下將會報錯。該方法只能進行同步操作,且方法名只能全域性唯一。操作之中會有一些hook暴露出來,以進行state的監控等。
  • state:頁面狀態管理容器物件。集中儲存Vue components中data物件的零散資料,全域性唯一,以進行統一的狀態管理。頁面顯示所需的資料從該物件中進行讀取,利用Vue的細粒度資料響應機制來進行高效的狀態更新。
  • getters:state物件讀取方法。圖中沒有單獨列出該模組,應該被包含在了render中,Vue Components通過該方法讀取全域性state物件。

Vue元件接收互動行為,呼叫dispatch方法觸發action相關處理,若頁面狀態需要改變,則呼叫commit方法提交mutation修改state,通過getters獲取到state新值,重新渲染Vue Components,介面隨之更新。

二、目錄結構介紹

開啟Vuex專案,看下原始碼目錄結構。

dir_structure

Vuex提供了非常強大的狀態管理功能,原始碼程式碼量卻不多,目錄結構劃分也很清晰。先大體介紹下各個目錄檔案的功能:

  • module:提供module物件與module物件樹的建立功能;
  • plugins:提供開發輔助外掛,如“時光穿梭”功能,state修改的日誌記錄功能等;
  • helpers.js:提供action、mutations以及getters的查詢API;
  • index.js:是原始碼主入口檔案,提供store的各module構建安裝;
  • mixin.js:提供了store在Vue例項上的裝載注入;
  • util.js:提供了工具方法如find、deepCopy、forEachValue以及assert等方法。

三、初始化裝載與注入

瞭解大概的目錄及對應功能後,下面開始進行原始碼分析。index.js中包含了所有的核心程式碼,從該檔案入手進行分析。

3.1 裝載例項

先看個簡單的例子:

   
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** *  store.js檔案 *  建立store物件,配置state、action、mutation以及getter *   **/   import Vue from 'vue' import Vuex from 'vuex'   // install Vuex框架 Vue.use(Vuex)   // 建立並匯出store物件。為了方便,不配置任何引數 export default new Vuex.Store()

store.js檔案中,載入Vuex框架,建立並匯出一個空配置的store物件例項。

   
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /** *  vue-index.js檔案 *   * **/   import Vue from 'vue' import App from './../pages/app.vue' import store from './store.js'   new Vue({   el: '#root',   router,   store,   render: h => h(App) })

然後在index.js中,正常初始化一個頁面根級別的Vue元件,傳入這個自定義的store物件。

問題1所述,以上例項除了Vue的初始化程式碼,只是多了一個store物件的傳入。一起看下原始碼中的實現方式。

3.2 裝載分析

index.js檔案程式碼執行開頭,定義區域性 Vue 變數,用於判斷是否已經裝載和減少全域性作用域查詢。

   
1 let Vue

然後判斷若處於瀏覽器環境下且載入過Vue,則執行install方法。

   
1 2 3 4 // auto install in dist mode   if (typeof window !== 'undefined' && window.Vue) {   install(window.Vue)   }

install方法將Vuex裝載到Vue物件上,Vue.use(Vuex) 也是通過它執行,先看下Vue.use方法實現:

   
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function (plugin: Function | Object) {   /* istanbul ignore if */   if (plugin.installed) {     return   }   // additional parameters   const args = toArray(arguments, 1)   args.unshift(this)   if (typeof plugin.install === 'function') {     // 實際執行外掛的install方法     plugin.install.apply(plugin, args)   } else {     plugin.apply(null, args)   }   plugin.installed = true   return this }

若是首次載入,將區域性Vue變數賦值為全域性的Vue物件,並執行applyMixin方法,install實現如下:

   
1 2 3 4 5 6 7 8 9 10 function install (_Vue) {   if (Vue) {     console.error(       '[vuex] already installed. Vue.use(Vuex) should be called only once.'     )     return   }   Vue = _Vue   applyMixin(Vue) }

來看下applyMixin方法內部程式碼。如果是2.x.x以上版本,可以使用 hook 的形式進行注入,或使用封裝並替換Vue物件原型的_init方法,實現注入。

   
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 export default function (Vue) {   const version = Number(Vue.version.split('.')[0])     if (version >= 2) {     const usesInit = Vue.config._lifecycleHooks.indexOf('init') > -1     Vue.mixin(usesInit ? { init: vuexInit } : { 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)     }   }

具體實現:將初始化Vue根元件時傳入的store設定到this物件的$store屬性上,子元件從其父元件引用$store屬性,層層巢狀進行設定。在任意元件中執行 this.$store 都能找到裝載的那個store物件,vuexInit方法實現如下:

   
1 2 3 4 5 6 7 8 9 function vuexInit () {   const options = this.$options   // store injection   if (options.store) {     this.$store = options.store   } else if (options.parent && options.parent.$store) {     this.$store = options.parent.$store   } }

看個圖例理解下store的傳遞。

頁面Vue結構圖:
cart_vue_structure

對應store流向:
cart_vue_structure

四、store物件構造

上面對Vuex框架的裝載以及注入自定義store物件進行分析,解決了問題1。接下來詳細分析store物件的內部功能和具體實現,來解答 為什麼actions、getters、mutations中能從arguments[0]中拿到store的相關資料? 等問題。

store物件實現邏輯比較複雜,先看下構造方法的整體邏輯流程來幫助後面的理解:

cart_vue_structure

4.1 環境判斷

開始分析store的建構函式,分小節逐函式逐行的分析其功能。

   
1 2 3 constructor (options = {}) {   assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)   assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)

在store建構函式中執行環境判斷,以下都是Vuex工作的必要條件:

  1. 已經執行安裝函式進行裝載;
  2. 支援Promise語法。

assert函式是一個簡單的斷言函式的實現,一行程式碼即可實現。

   
1 2 3 function assert (condition, msg) {   if (!condition) throw new Error(`[vuex] ${msg}`) }

 

4.2 資料初始化、module樹構造

環境判斷後,根據new構造傳入的options或預設值,初始化內部資料。

   
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const {     state = {},     plugins = [],     strict = false } = options   // store internal state this._committing = false // 是否在進行提交狀態標識 this._actions = Object.create(null) // acitons操作物件 this._mutations = Object.create(null) // mutations操作物件 this._wrappedGetters = Object.create(null) // 封裝後的getters集合物件 this._modules = new ModuleCollection(options) // Vuex支援store分模組傳入,儲存分析後的modules this._modulesNamespaceMap = Object.create(null) // 模組名稱空間map this._subscribers = [] // 訂閱函式集合,Vuex提供了subscribe功能 this.

相關推薦

深入理解Vuex 框架

Vuex是一個專為Vue服務,用於管理頁面資料狀態、提供統一資料操作的生態系統。它集中於MVC模式中的Model層,規定所有的資料操作必須通過 action – mutation – state change 的流程來進行,再結合Vue的資料檢視雙向繫結特性來實現頁面的展示更新。統一的頁面狀態管理以及操作處理

深入理解 Koa 框架中介軟體原理

Node 主要用在開發 Web 應用,koa 是目前 node 裡最流行的 web 框架。 在 Node 開啟一個 http 服務簡直易如反掌,官網 demo。 const http = require("http"); const server = http.createServer(

深入理解集合框架層次結構(篇二)

本篇我們來談對映(Map) Map 對映資料結構是用來儲存具有對映關係的資料,當要檢視一個元素時,需要查詢元素的精確副本。因此Map集合裡存放著兩組值key(鍵)和value(值)。其中key值唯一,不能重複,value值可重複。Java類庫為對映提供了兩個通用實現:Ha

深入理解express框架

寫在前面 Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你建立各種 Web 應用,和豐富的 HTTP 工具。使用 Express 可以快速地搭建一個完整功能的網站。http://jafeney.com/2016/01/1

深入理解flask框架(4):session

flask中session的實現是基於cookie。 開啟flask原始碼的session.py檔案,我們可以看到最後的介面類中,主要有open_session,save_session兩個函式。 class SecureCookieSessionInter

深入理解Spring4框架(一) 簡介

                        &nbs

Java深入理解集合框架Set

前言 Java集合框架中Set介面主要包括HashSet、TreeSet,這裡只介紹HashSet HashSet 主要方法 HashSet通過add remove新增和刪除資料,通過迭代器查詢資料 構造方法 private

深入理解Spring框架(一)

Spring設計理念與整體架構 1、Spring是一個非入侵性框架,其目標是使應用程式程式碼對框架的以來最小化,應用程式碼可以在沒有 Spring或者其他容器的情況下使用。 2、

【重拾】深入理解express框架

寫在前面 Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你建立各種 Web 應用,和豐富的 HTTP 工具。使用 Express 可以快速地搭建一個完整功能的網站。 Express 框架核心特性: 可以設定

深入理解 ORM框架

如果 object 移植 相關 XML 關系模型 技術 基於 也會 1、ORM是什麽? object Relational mapping 對象關系映射,是一種為了解決面向對象與關系型數據庫存在不匹配現象的技術,簡單說,orm通過描述對象和數據庫之間映射的元數據,將程序中的

教你寫Http框架(二)——三個樣例帶你深入理解AsyncTask

func implement oncreate 其它 層疊 worker dcl 例如 人員 這個標題大家不要奇怪,扯Http框架怎麽扯到AsyncTask去了,有兩個原因:首先是Http框架除了核心http理論外。其技術實現核心也是線程池 + 模板 +

深入理解IOC模式及Unity框架

理解 rain 框架 播放器 url 播放 builder 說明 title 深入理解IOC模式及Unity框架 研究了下,有幾篇博客確實已經說得很清楚了 1、IoC模式:http://www.cnblogs.com/qqlin/archive/

深入理解Java集合框架》系列文章

stack 數據結構 tro www. rpe ack 不能 一個 標準 https://www.cnblogs.com/CarpenterLee/p/5545987.html Introduction 關於C++標準模板庫(Standard Template Libr

深入理解Java集合框架】紅黑樹講解(上)

時間復雜度 row lee tel framework 關系 eight logs return 來源:史上最清晰的紅黑樹講解(上) - CarpenterLee 作者:CarpenterLee(轉載已獲得作者許可,如需轉載請與原作者聯系) 文中所有圖片點擊之後均可查看大

Vuex深入理解

+= payload put 深入 store mut urn div this store下的index.js: 1 import Vue from ‘vue‘ 2 import Vuex from ‘vuex‘ 3 4 Vue.use(Vuex)

深入理解Plasma(1):Plasma 框架

這一系列文章將圍繞以太坊的二層擴容框架,介紹其基本執行原理,具體操作細節,安全性討論以及未來研究方向等。本篇文章作為開篇,主要目的是理解 Plasma 框架。 Plasma 作為以太坊的二層擴容框架,自從 2017 年被 Joseph Poon(Lightning N

安卓專案實戰之強大的網路請求框架okGo使用詳解(二):深入理解Callback之自定義JsonCallback

前言 JSON是一種取代XML的資料結構,和xml相比,它更小巧但描述能力卻不差,由於它的小巧所以網路傳輸資料將減少更多流量從而加快了傳輸速度,目前客戶端伺服器返回的資料大多都是基於這種格式的,相應的我們瞭解的關於json的解析工具主要有兩個:Gson(Google官方出的)和fas

安卓專案實戰之強大的網路請求框架okGo使用詳解(一):實現get,post基本網路請求,下載上傳進度監聽以及對Callback自定義的深入理解

1.新增依賴 //必須使用 compile 'com.lzy.net:okgo:3.0.4' //以下三個選擇新增,okrx和okrx2不能同時使用,一般選擇新增最新的rx2支援即可 compile 'com.lzy.net:okrx:1.0.2' compile 'com.lzy

ML之預測:以某個資料集為例從0到1深入理解科學預測之分類問題的思路框架

ML之預測:以某個資料集為例從0到1深入理解科學預測之分類問題的思路框架 總體思路設計框架         1、獲取資料集,並確定新資料集的規模 資料集規模為(208*61) 2、確定資料集每個屬性的型別 &

由裝飾者模式來深入理解Java I/O整體框架

前言 Java裡面的I/O這一部分看過很多遍,每次看完之後特別混亂,又是輸入流,又是輸出流,又是字元流,又是位元組流,還有什麼過濾流,緩衝流。每次看得我如入雲裡霧裡,直到後面看了設計模式這一塊,才算真正的對Java I/O這一塊有了整體的瞭解,理解起Java流也就容易許多。這篇部落格先介紹裝飾者模式,然後結