免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
vuex源碼簡析

前言


基于 vuex 3.1.2 按如下流程進行分析:

Vue.use(Vuex);const store = new Vuex.Store({  actions,  getters,  state,  mutations,  modules  // ...});            new Vue({store});

Vue.use(Vuex)


Vue.use() 會執(zhí)行插件的 install 方法,并把插件放入緩存數(shù)組中。

而 Vuex 的 install 方法很簡單,保證只執(zhí)行一次,以及使用 applyMixin 初始化。

export function install (_Vue) {  // 保證只執(zhí)行一次  if (Vue && _Vue === Vue) {    if (process.env.NODE_ENV !== 'production') {      console.error(        '[vuex] already installed. Vue.use(Vuex) should be called only once.'      )    }    return  }  Vue = _Vue    // 初始化  applyMixin(Vue)}

applyMixin 方法定義在 vuex/src/mixin.js,vuex 還兼容了 vue 1 的版本,這里只關注 vue 2 的處理。

export default function (Vue) {  const version = Number(Vue.version.split('.')[0])  if (version >= 2) {    // 混入一個 beforeCreate 方法    Vue.mixin({ beforeCreate: vuexInit })  } else {    // 這里是 vue 1 的兼容處理  }  // 這里的邏輯就是將 options.store 保存在 vue 組件的 this.$store 中,  // options.store 就是 new Vue({...}) 時傳入的 store,  // 也就是 new Vuex.Store({...}) 生成的實例。  function vuexInit () {    const options = this.$options    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    }  }}

new Vuex.Store({...})


先看 Store 的構造函數(shù):

constructor (options = {}) {  const { plugins = [], strict = false } = options  this._committing = false  this._actions = Object.create(null)  this._actionSubscribers = []  this._mutations = Object.create(null)  this._wrappedGetters = Object.create(null)  // 構建 modules  this._modules = new ModuleCollection(options)  this._modulesNamespaceMap = Object.create(null)  this._subscribers = []  // store.watch  this._watcherVM = new Vue()  this._makeLocalGettersCache = Object.create(null)  // bind commit and dispatch to self  const store = this  const { dispatch, commit } = this  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)  }  // strict mode  this.strict = strict	// 安裝模塊  const state = this._modules.root.state  installModule(this, state, [], this._modules.root)  // 實現(xiàn)狀態(tài)的響應式  resetStoreVM(this, state)  // apply plugins  plugins.forEach(plugin => plugin(this))  const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools  if (useDevtools) {    devtoolPlugin(this)  }}

this._modules

vuex 為了讓結構清晰,允許我們將 store 分割成模塊,每個模塊擁有自己的 state、mutation、action、getter,而且模塊自身也可以擁有子模塊。

const moduleA = {...}const moduleB = {...}const store = new Vuex.Store({  modules: {    a: moduleA,    b: moduleB  }})store.state.a // -> moduleA 的狀態(tài)store.state.b // -> moduleB 的狀態(tài)

而模塊在 vuex 的構造函數(shù)中通過new ModuleCollection(options)生成,依然只看構造函數(shù):

// vuex/src/module/module-collection.jsexport default class ModuleCollection {  constructor (rawRootModule) {    // register root module (Vuex.Store options)    this.register([], rawRootModule, false)  }  register (path, rawModule, runtime = true) {    // 生成一個 module    const newModule = new Module(rawModule, runtime)    // new Vuex.Store() 生成的是根模塊    if (path.length === 0) {      // 根模塊      this.root = newModule    } else {      // 生成父子關系      const parent = this.get(path.slice(0, -1))      parent.addChild(path[path.length - 1], newModule)    }    // 注冊嵌套的模塊    if (rawModule.modules) {      forEachValue(rawModule.modules, (rawChildModule, key) => {        this.register(path.concat(key), rawChildModule, runtime)      })    }  }}

register 接收 3 個參數(shù),其中 path 表示路徑,即模塊樹的路徑,rawModule 表示傳入的模塊的配置,runtime 表示是否是一個運行時創(chuàng)建的模塊。

register首先 new Module() 生成一個模塊。

// vuex/src/module/module.jsexport default class Module {  constructor (rawModule, runtime) {    this.runtime = runtime    // 子模塊    this._children = Object.create(null)    // module 原始配置    this._rawModule = rawModule    const rawState = rawModule.state    // state    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}  }}

實例化一個 module 后,判斷當前的 path 的長度,如果為 0,就是是一個根模塊,所以把 newModule 賦值給 this.root,而 new Vuex.Store() 生成的就是根模塊。

如果不為 0,就建立父子模塊的關系:

const parent = this.get(path.slice(0, -1))parent.addChild(path[path.length - 1], newModule)

首先根據(jù)路徑獲取父模塊,然后再調(diào)用父模塊的 addChild 方法將子模塊加入到 this._children 中,以此建立父子關系。

register 最后會檢測是否有嵌套的模塊,然后進行注冊嵌套的模塊:

if (rawModule.modules) {  forEachValue(rawModule.modules, (rawChildModule, key) => {    this.register(path.concat(key), rawChildModule, runtime)  })}

遍歷當前模塊定義中的所有 modules,根據(jù) key 作為 path,遞歸調(diào)用 register 方法進行注冊。

installModule

注冊完模塊就會開始安裝模塊:

const state = this._modules.root.stateinstallModule(this, state, [], this._modules.root)

installModule 方法支持 5 個參數(shù): store,state,path(模塊路徑),module(根模塊),hot(是否熱更新)。

默認情況下,模塊內(nèi)部的 action、mutation 和 getter 是注冊在全局命名空間的,這樣使得多個模塊能夠對同一 mutation 或 action 作出響應。但是如果有同名的 mutation 被提交,會觸發(fā)所有同名的 mutation。

因此 vuex 提供了 namespaced: true 讓模塊成為帶命名空間的模塊,當模塊被注冊后,它的所有 action、mutation 和 getter 都會自動根據(jù)模塊注冊的路徑調(diào)整命名。例如:

const store = new Vuex.Store({  modules: {    account: {      namespaced: true,      getters: {        isAdmin () { ... } // -> getters['account/isAdmin']      },      // 進一步嵌套命名空間      posts: {        namespaced: true,        getters: {          popular () { ... } // -> getters['account/posts/popular']        }      }    }  }})

回到 installModule 方法本身:

function installModule (store, rootState, path, module, hot) {  // 是否為根模塊  const isRoot = !path.length  // 獲取命名空間,  // 比如說 { a: moduleA } => 'a/',  // root 沒有 namespace  const namespace = store._modules.getNamespace(path)  // 將有 namespace 的 module 緩存起來  if (module.namespaced) {    store._modulesNamespaceMap[namespace] = module  }  if (!isRoot && !hot) {    // 給 store.state 添加屬性,    // 假如有 modules: { a: moduleA },    // 則 state: { a: moduleA.state }    const parentState = getNestedState(rootState, path.slice(0, -1))    const moduleName = path[path.length - 1]    store._withCommit(() => {      Vue.set(parentState, moduleName, module.state)    })  }  // local 上下文,  // 本質上是重寫 dispatch 和 commit 函數(shù),  // 舉個例子,modules: { a: moduleA },  // moduleA 中有名為 increment 的 mutation,  // 通過 makeLocalContext 函數(shù),會將 increment 變成  // a/increment,這樣就可以在 moduleA 中找到定義的函數(shù)  const local = module.context = makeLocalContext(store, namespace, path)  // 下面這幾個遍歷循環(huán)函數(shù),  // 都是在注冊模塊中的 mutation、action 等等,  // moduleA 中有名為 increment 的 mutation,  // 那么會將 namespace + key 拼接起來,  // 變?yōu)?'a/increment'  module.forEachMutation((mutation, key) => {    const namespacedType = namespace + key    // 會用 this._mutations 將每個 mutation 存儲起來,    // 因為允許同一 namespacedType 對應多個方法,    // 所以同一 namespacedType 是用數(shù)組存儲的,    // store._mutations[type] = []    registerMutation(store, namespacedType, mutation, local)  })  module.forEachAction((action, key) => {    const type = action.root ? key : namespace + key    const handler = action.handler || action    // 和上面一樣,用 this._actions 將 action 存儲起來    registerAction(store, type, handler, local)  })  module.forEachGetter((getter, key) => {    const namespacedType = namespace + key    // 會用 this._wrappedGetters 存儲起來    // getters 有一點不一樣,    // 這里保存的是一個返回 getters 的函數(shù),    // 而且同一 namespacedType 只能定義一個。    registerGetter(store, namespacedType, getter, local)  })  // 遞歸安裝子模塊  module.forEachChild((child, key) => {    installModule(store, rootState, path.concat(key), child, hot)  })}

makeLocalContext

function makeLocalContext (store, namespace, path) {  // 判斷是否有 namespace  const noNamespace = namespace === ''  const local = {    // 重寫 dispatch    // 為什么要重寫,    // 舉個例子 modules: { a: moduleA }    // 在 moduleA 的 action 中使用 dispatch 時,    // 并不會傳入完整的 path,    // 只有在 vue 實例里調(diào)用 store.dispatch 才會傳入完整路徑    dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {      const args = unifyObjectStyle(_type, _payload, _options)      const { payload, options } = args      let { type } = args      if (!options || !options.root) {        type = namespace + type      }      return store.dispatch(type, payload)    },    // 重寫 commit 方法    // 同上    commit: noNamespace ? store.commit : (_type, _payload, _options) => {      const args = unifyObjectStyle(_type, _payload, _options)      const { payload, options } = args      let { type } = args      if (!options || !options.root) {        type = namespace + type      }      store.commit(type, payload, options)    }  }  Object.defineProperties(local, {    getters: {      get: noNamespace        ? () => store.getters      	// local.getters 本質上是通過匹配 namespace,      	// 從 store.getters[type] 中獲取        : () => makeLocalGetters(store, namespace)    },    state: {      // local.state 本質上是通過解析 path,      // 從 store.state 中獲取      get: () => getNestedState(store.state, path)    }  })  return local}

resetStoreVM(this, state)

初始化 store._vm,利用 Vue 將 store.state 進行響應式處理,并且將 getters 當作 Vue 的計算屬性來進行處理:

function resetStoreVM (store, state, hot) {  const oldVm = store._vm  store.getters = {}  // reset local getters cache  store._makeLocalGettersCache = Object.create(null)  const wrappedGetters = store._wrappedGetters  const computed = {}  // 遍歷 store._wrappedGetters 屬性  forEachValue(wrappedGetters, (fn, key) => {    // 這里的 partial 其實就是創(chuàng)建一個閉包環(huán)境,    // 保存 fn, store 兩個變量,并賦值給 computed    computed[key] = partial(fn, store)    // 重寫 get 方法,    // store.getters.key 其實是訪問了 store._vm[key],    // 也就是去訪問 computed 中的屬性    Object.defineProperty(store.getters, key, {      get: () => store._vm[key],      enumerable: true // for local getters    })  })  // 實例化一個 Vue 實例 store._vm,  // 用它來保存 state,computed(getters),  // 也就是利用 Vue 的進行響應式處理  const silent = Vue.config.silent  Vue.config.silent = true  // 訪問 store.state 時,  // 其實是訪問了 store._vm._data.$$state  store._vm = new Vue({    data: {      $$state: state    },    // 這里其實就是上面的 getters    computed  })  Vue.config.silent = silent  // 開啟 strict mode,  // 只能通過 commit 的方式改變 state  if (store.strict) {    enableStrictMode(store)  }  if (oldVm) {    if (hot) {      // 熱重載      store._withCommit(() => {        oldVm._data.$$state = null      })    }    // 這里其實是動態(tài)注冊模塊,    // 將新的模塊內(nèi)容加入后生成了新的 store._vm,    // 然后將舊的銷毀掉    Vue.nextTick(() => oldVm.$destroy())  }}

partial

export function partial (fn, arg) {  return function () {    return fn(arg)  }}

常用 api


commit

commit (_type, _payload, _options) {  // check object-style commit  const {    type,    payload,    options  } = unifyObjectStyle(_type, _payload, _options)  const mutation = { type, payload }  const entry = this._mutations[type]  if (!entry) {    // 沒有 mutation 會報錯并退出    return  }  this._withCommit(() => {    // 允許同一 type 下,有多個方法,    // 所以循環(huán)數(shù)組執(zhí)行 mutation,    // 實際上執(zhí)行的就是安裝模塊時注冊的 mutation    // handler.call(store, local.state, payload)    entry.forEach(function commitIterator (handler) {      handler(payload)    })  })  // 觸發(fā)訂閱了 mutation 的所有函數(shù)  this._subscribers    .slice()    .forEach(sub => sub(mutation, this.state))}

dispatch

dispatch (_type, _payload) {    // check object-style dispatch    const {      type,      payload    } = unifyObjectStyle(_type, _payload)    const action = { type, payload }    // 從 Store._actions 獲取    const entry = this._actions[type]    if (!entry) {			// 找不到會報錯      return    }  	// 在 action 執(zhí)行之前,  	// 觸發(fā)監(jiān)聽了 action 的函數(shù)    try {      this._actionSubscribers        .slice()         .filter(sub => sub.before)        .forEach(sub => sub.before(action, this.state))    } catch (e) {        console.error(e)    }  	// action 用 Promise.all 異步執(zhí)行,  	// 實際上執(zhí)行的就是安裝模塊時注冊的 action  	/*   	handler.call(store, {dispatch, commit, getters, state, rootGetters, rootState}, payload, cb) 		  	*/    const result = entry.length > 1      ? Promise.all(entry.map(handler => handler(payload)))      : entry[0](payload)    return result.then(res => {      // 在 action 執(zhí)行之后,  		// 觸發(fā)監(jiān)聽了 action 的函數(shù)      try {        this._actionSubscribers          .filter(sub => sub.after)          .forEach(sub => sub.after(action, this.state))      } catch (e) {          console.error(e)      }      return res    })  }

watch

watch (getter, cb, options) {  // new Vuex.Store() 時創(chuàng)建 _watcherVM,  // this._watcherVM = new Vue(),  // 本質就是調(diào)用 vue 的 api  return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)}

registerModule

registerModule (path, rawModule, options = {}) {  if (typeof path === 'string') path = [path]	  // 注冊模塊  this._modules.register(path, rawModule)  // 安裝模塊  installModule(this, this.state, path, this._modules.get(path), options.preserveState)  // 重新生成 vue 實例掛載到 store 上,  // 然后銷毀舊的  resetStoreVM(this, this.state)}

備注


希望疫情盡快過去吧?!?020/02/08 元宵

本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
vuex知識點
vuex——模塊化
vue.js 2.0 項目開發(fā)小結
成功使用vue store存取全局變量
在vue-element-admin中使用Vuex
簡述對vuex的理解
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服