Vuex原始碼學習(八)模組的context如何被建立以及它的作用

酸楚與甘甜發表於2019-04-02

你不知道action與mutation怎麼被呼叫的?趕緊回去看啊Vuex原始碼學習(七)action和mutation如何被呼叫的(呼叫篇))

上兩個小節已經講述了commit與dispatch如何呼叫mutation與action的,但是action中有幾個引數感覺涉及到了一些我們遺漏(故意不講)的點。

模組的context

Vuex原始碼學習(八)模組的context如何被建立以及它的作用

在installModule的時候 給每個模組繫結了一個屬性context。 通過makeLocalContext函式建立的,在註冊action、mutation和getters都有使用。這個context是什麼呢?

makeLocalContext函式建立了一個什麼東西

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
返回值local物件 由兩個方法、兩個屬性構成的。

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
這個目的是什麼?建立區域性模組的dispatch、commit、getters、state

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
也就是這個東西

我們按照型別分析

  1. dispatch與commit
// 檢視全名,如果沒有全名 可能是根模組或者沒有設定名稱空間
const noNamespace = namespace === '';
// 如果沒有全名 就使用全域性(store)上的disptach
// 有全名的話 構建一個新的dispatch 
// 這個新的dispatch仍然接收三個引數(與store上的dispatch一樣)
// 
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => { 
      //unifyObjectStyle 對額外傳入的_options沒有任何處理 只是確定一下位置
      const args = unifyObjectStyle(_type, _payload, _options)
      // options 值沒有發生變化
      const { payload, options } = args
      let { type } = args
      // 在disptach的時候是否指定選擇root(根)
      // 如果options設定為{root : true} 那麼就會跳過下面
      if (!options || !options.root) {
        // 拼接真正的名字
        type = namespace + type
        if (process.env.NODE_ENV !== 'production' && !store._actions[type]) {
          console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
          return
        }
      }
      // 呼叫(補全名字後)的action
      return store.dispatch(type, payload)
    },
複製程式碼

這段程式碼我們可以看出來,local物件(也就是模組的context屬性)中的dispacth會在未指定使用根模組名字時,會把dispatch呼叫的名字強行加上這個模組的全名,用這個dispatch呼叫的action都會變成你這個模組下的action

所以local中的dispatch與store中的disptach有什麼不同

通俗的講 我們想要呼叫A模組(有名稱空間的某個action B)需要做的是

this.$store.dispatch('A模組的全名/B的名字'); 
複製程式碼

在A模組的action中想要使用dispatch來做一些事情。

actions.js
export const ajaxGetUserName = ({dispatch})=>{
    // 這個時候用dispatch呼叫自己模組內的其餘的action不需要加上全名
    dispatch('ajaxGetUserAge');
    // 想要變成和根模組一樣的dispatch 需要加上一個options註明{root : true}
    // 這個時候dispatch就會變成全域性的 不會主動幫你拼接全名了
}

export const ajaxGetUserAge = () => {
    // do something
}
複製程式碼

同理local物件下的commit也是做了同樣的事情,

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
這裡就不多加解釋了,相信聰明的你早就可以舉一反三了。

兩個方法說完了,下面該講兩個屬性了

  1. getters與state

Vuex原始碼學習(八)模組的context如何被建立以及它的作用

這兩個屬性就是我們的getters與state,但是這是我們local物件中,也就是區域性模組下的getters與state。getters與state如何建立的 getters 首先判斷全名是不是為空,為空就返回store物件的getters,有的話就建立區域性getters。與其說是建立不如說是代理

如何建立區域性的getters? 代理的方式

makeLocalGetters原始碼

function makeLocalGetters (store, namespace) {
  // 設計思想
  //其實我們並不需要建立一套getters,
  // 只要我們在local中通過getters來獲取一些區域性模組的值的時候,
  // 可以被代理到真正存放這些getters的地方。
  
  // 建立代理物件
  const gettersProxy = {}
  // 找到切割點
  const splitPos = namespace.length
  Object.keys(store.getters).forEach(type => {
    // skip if the target getter is not match this namespace
    // 得去getters裡面找一下有沒有這個namespace為字首的getter。
    // 沒有就找不到了
    if (type.slice(0, splitPos) !== namespace) return

    // extract local getter type
    // 拿到模組內註冊的那個區域性的getter名字
    // 全名是set/getName
    // localType就是getName
    const localType = type.slice(splitPos)

    // Add a port to the getters proxy.
    // Define as getter property because
    // we do not want to evaluate the getters in this time.
    // 完成代理任務,
    // 在查詢區域性名字是被代理到對應的store.getters中的(全名)getter
    Object.defineProperty(gettersProxy, localType, {
      get: () => store.getters[type],
      enumerable: true
    })
  })
  //返回代理物件
  return gettersProxy
}
複製程式碼

建立區域性的getters就是一個代理的過程,在使用模組內使用(沒有加上名稱空間的)getters的名字,會被代理到,store例項上那個真正的(全名的)getters。

state 這個相對來說就簡單很多了

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
與代理類似,只是state只需要代理到state中對應那個模組的state,這個就比較簡單了。

建立完畢

context是如何被建立的大家已經比較瞭解了。context的作用是什麼? (local就是contenxt)之前說過註冊mutation、action、getters都用到了local。用他們幹什麼?一一介紹

1. 註冊mutation

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
我們註冊的mutation在被commit呼叫時,使用的state是區域性的state,當前模組內的state,所以不用特殊方式mutation無法更新父(祖先)模組和兄弟模組的內容。

2. 註冊dispatch

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
dispatch是可以呼叫到模組內的mutation、disptach,也就是說它有更新模組內資料的能力,

但是隻給了dispatch傳入了store的getters與state(雖然有了這倆你想要什麼放在vuex的資料都能得到),並沒有給store的dispatch與mutation。

這就說名dispatch可以檢視store中的所有資料,你放在vuex裡面的資料我都可以看,但是你想改不使用特殊手段,不好意思只能改自己模組的。

3. 註冊getters

Vuex原始碼學習(八)模組的context如何被建立以及它的作用
getters並沒有改變資料的能力,你願意怎麼運算元據都可以,模組內的資料,全模組的資料都可以給你,你願意怎麼計算都可以。

在註冊中我們可以看到,vuex對這個改變資料的許可權控制的很嚴格,但是檢視資料控制的很鬆,改只能改自己模組的,查你願意怎麼看都OK。

總結

  1. context(也是local)是經過一個makeLocalContext的函式建立的,裡面有區域性的dispatch、commit方法和getters、state屬性。
  2. 區域性的方法屬性都是隻能訪問區域性模組內的,除非在使用時額外傳入options({root:true})來解開區域性模組的限制。
  3. 區域性的getters是通過makeLocalGetters來實現的,主要思想是依靠代理的方式,把區域性的名字的getter代理到store的getters中那個全名的getter。
  4. context 的作用可以幫助dispatch與commit控制更新資料的許可權,幫助模組內getters拿到區域性與全模組的資料。

這個章節結束,我們所有和模組有關的內容就已經完結了。 對於模組做一個小的總結。

模組的意義

  1. 模組與模組連結把Vuex初始化傳入的內容,整理成一個方便處理的模組樹(方便)
  2. 模組讓action、mutation、getters、state都有了自己的全名(設定namespaced為true),起名字不再被約束,減少了命名衝突。
  3. 模組還給action、mutation、getters提供了區域性上下文(context)讓模組內的這些方法和屬性,可以方便的修改模組內的資料以及獲取全模組與模組內的資料。
  4. dispatch與commit也對模組進行了全力的支援(不支援不白做了嗎),

所以模組為Vuex提供了很多方便,方便的去獲取資料、修改資料。那麼Vuex真正的資料倉儲在哪裡?資料都儲存在哪裡? 我們下一章見

我是一個應屆生,最近和朋友們維護了一個公眾號,內容是我們在從應屆生過渡到開發這一路所踩過的坑,已經我們一步步學習的記錄,如果感興趣的朋友可以關注一下,一同加油~

個人公眾號:鹹魚正翻身

相關文章