国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入學(xué)習(xí)Vuex

funnyZhang / 2648人閱讀

摘要:深入學(xué)習(xí)作為配合使用的數(shù)據(jù)狀態(tài)管理庫,針對解決兄弟組件或多層級組件共享數(shù)據(jù)狀態(tài)的痛點問題來說,非常好用。至此,構(gòu)造函數(shù)部分已經(jīng)過了一遍了。

深入學(xué)習(xí)Vuex

vuex作為配合vue使用的數(shù)據(jù)狀態(tài)管理庫,針對解決兄弟組件或多層級組件共享數(shù)據(jù)狀態(tài)的痛點問題來說,非常好用。本文以使用者的角度,結(jié)合源碼來學(xué)習(xí)vuex。其中也參考了許多前輩的文章,參見最后的Reference

Vue加載Vuex(Vue.use(Vuex))

Vue加載Vuex還是很簡單的,讓我們以官方文檔上實例為切入點來開始認(rèn)識Vuex

import Vue from "vue"
import Vuex from "vuex"
import cart from "./modules/cart"
import products from "./modules/products"
import createLogger from "../../../src/plugins/logger"

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== "production"

export default new Vuex.Store({
  modules: {
    cart,
    products
  },
  strict: debug,
  plugins: debug ? [createLogger()] : []
})

這段代碼我們再熟悉不過了,就是Vue加載Vuex插件,然后new了一個Vuex實例。
我們一步一步來看,首先看一下Vue如何加載的Vuex,也就是Vue.use(Vuex)發(fā)生了什么。

Vue.use = function (plugin: Function | Object) {
    /* istanbul ignore if */
    /*標(biāo)識位檢測該插件是否已經(jīng)被安裝*/
    if (plugin.installed) {
      return
    }
    // additional parameters
    const args = toArray(arguments, 1)
    /*將this(Vue構(gòu)造函數(shù))加入數(shù)組頭部*/
    args.unshift(this)
    if (typeof plugin.install === "function") {
      /*install執(zhí)行插件安裝*/
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === "function") {
      plugin.apply(null, args)
    }
    //標(biāo)記插件已安裝
    plugin.installed = true
    return this
  }

主要做了幾件事:

驗證是否已安裝,避免重復(fù)

如果插件提供install方法,則執(zhí)行否則把插件當(dāng)作function執(zhí)行

最后標(biāo)記插件已安裝

那么Vuex提供install方法了嗎?答案是肯定的

let Vue // bind on install
export function install (_Vue) {
  if (Vue) {
    /*避免重復(fù)安裝(Vue.use內(nèi)部也會檢測一次是否重復(fù)安裝同一個插件)*/
    if (process.env.NODE_ENV !== "production") {
      console.error(
        "[vuex] already installed. Vue.use(Vuex) should be called only once."
      )
    }
    return
  }
  /*保存Vue,同時用于檢測是否重復(fù)安裝*/
  Vue = _Vue//Vue構(gòu)造函數(shù)
  /*將vuexInit混淆進(jìn)Vue的beforeCreate(Vue2.0)或_init方法(Vue1.0)*/
  applyMixin(Vue)
}

看mixin之前我們可以先思考一個問題,我們在訪問Vuex的數(shù)據(jù)的時候基本都是這樣訪問的,比如this.user = this.$store.state.global.user,this.$store是什么時候加到Vue實例上的?applyMixin會給出答案,讓我們繼續(xù)看applyMixin發(fā)生了什么

// applyMixin:
export default function (Vue) {
  /*獲取Vue版本,鑒別Vue1.0還是Vue2.0*/
  const version = Number(Vue.version.split(".")[0])

  if (version >= 2) {
    /*通過mixin將vuexInit混淆到Vue實例的beforeCreate鉤子中*/
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    /*將vuexInit放入_init中調(diào)用*/
    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.
   */
   /*Vuex的init鉤子,會存入每一個Vue實例等鉤子列表*/
  function vuexInit () {
    // this  = vue object
    const options = this.$options
    // store injection
    if (options.store) {
      /*存在store其實代表的就是Root節(jié)點,直接執(zhí)行store(function時)或者使用store(非function)*/
      this.$store = typeof options.store === "function"
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      /*子組件直接從父組件中獲取$store,這樣就保證了所有組件都公用了全局的同一份store*/
      this.$store = options.parent.$store
    }
  }
}

我們這里就只看2.0了,思路就是通過Vue.mixin把掛載$store的動作放在beforeCreate鉤子上,由此實現(xiàn)了每個組件實例都可以通過this.$store來直接訪問數(shù)據(jù)。
注意:mixin的細(xì)節(jié)

同名鉤子函數(shù)將混合為一個數(shù)組,因此都將被調(diào)用。另外,混入對象的鉤子將在組件自身鉤子之前調(diào)用。

使用全局混入對象,將會影響到 所有 之后創(chuàng)建的 Vue 實例。

至此,Vue.use(Vuex)我們已經(jīng)了解完了。

Vuex結(jié)構(gòu)總覽

順著我們實例代碼的思路,接下來我們應(yīng)該開始看構(gòu)造器了,不過開始看之前,我們先看一下Vuex.store Class都定義了些什么。

export class Store {
  constructor (options = {}) {
  }
    
  // state 取值函數(shù)(getter)
  get state () {
  }
  //存值函數(shù)(setter)
  set state (v) {
  }

  /* 調(diào)用mutation的commit方法 */
  commit (_type, _payload, _options) {
  }

  /* 調(diào)用action的dispatch方法 */
  dispatch (_type, _payload) {
  }

  /* 注冊一個訂閱函數(shù),返回取消訂閱的函數(shù) */
  subscribe (fn) {
  }

  /* 觀察一個getter方法 */
  watch (getter, cb, options) {
  }

  /* 重置state */
  replaceState (state) {
  }

  /* 注冊一個動態(tài)module,當(dāng)業(yè)務(wù)進(jìn)行異步加載的時候,可以通過該接口進(jìn)行注冊動態(tài)module */
  registerModule (path, rawModule) {
  }

  /* 注銷一個動態(tài)module */
  unregisterModule (path) {
  }

  /* 熱更新 */
  hotUpdate (newOptions) {
  }

  /* 保證通過mutation修改store的數(shù)據(jù) */
  // 內(nèi)部使用,比如當(dāng)外部強(qiáng)行改變state的數(shù)據(jù)時直接報錯
  _withCommit (fn) {
  }
}

以上就是定義的接口了,官方文檔上實例屬性和方法都在這里找得到。
來看一張大神畫的圖有助于理解思路

出處見最后。
接下來我們繼續(xù)實例思路

export default new Vuex.Store({
  modules: {
    cart,
    products
  },
  strict: debug,
  plugins: debug ? [createLogger()] : []
})

來看一下構(gòu)造函數(shù)

constructor (options = {}) {
    // Auto install if it is not done yet and `window` has `Vue`.
    // To allow users to avoid auto-installation in some cases,
    // this code should be placed here. See #731
    /*
      在瀏覽器環(huán)境下,如果插件還未安裝(!Vue即判斷是否未安裝),則它會自動安裝。
      它允許用戶在某些情況下避免自動安裝。
    */
    if (!Vue && typeof window !== "undefined" && window.Vue) {
      install(window.Vue) // 將store注冊到實例或conponent
    }

    if (process.env.NODE_ENV !== "production") {
      assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
      assert(typeof Promise !== "undefined", `vuex requires a Promise polyfill in this browser.`)
      //檢查是不是new 操作符調(diào)用的
      assert(this instanceof Store, `Store must be called with the new operator.`)
    }

    const {
      /*一個數(shù)組,包含應(yīng)用在 store 上的插件方法。這些插件直接接收 store 作為唯一參數(shù),可以監(jiān)聽 mutation(用于外部地數(shù)據(jù)持久化、記錄或調(diào)試)或者提交 mutation (用于內(nèi)部數(shù)據(jù),例如 websocket 或 某些觀察者)*/
      plugins = [],
      /*使 Vuex store 進(jìn)入嚴(yán)格模式,在嚴(yán)格模式下,任何 mutation 處理函數(shù)以外修改 Vuex state 都會拋出錯誤。*/
      strict = false
    } = options

    /*從option中取出state,如果state是function則執(zhí)行,最終得到一個對象*/
    let {
      state = {}
    } = options
    if (typeof state === "function") {
      state = state()
    }

    // store internal state
    /* 用來判斷嚴(yán)格模式下是否是用mutation修改state的 */
    this._committing = false
    /* 存放action */
    this._actions = Object.create(null)
    /* 存放mutation */
    this._mutations = Object.create(null)
    /* 存放getter */
    //包裝后的getter
    this._wrappedGetters = Object.create(null)
    /* module收集器 */
    this._modules = new ModuleCollection(options)
    /* 根據(jù)namespace存放module */
    this._modulesNamespaceMap = Object.create(null)
    /* 存放訂閱者 外部插件使用 */
    this._subscribers = []
    /* 用以實現(xiàn)Watch的Vue實例 */
    this._watcherVM = new Vue()

    // bind commit and dispatch to self
    /*將dispatch與commit調(diào)用的this綁定為store對象本身,否則在組件內(nèi)部this.dispatch時的this會指向組件的vm*/
    const store = this
    const {dispatch, commit} = this
    /* 為dispatch與commit綁定this(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)
    }

    // strict mode
    /*嚴(yán)格模式(使 Vuex store 進(jìn)入嚴(yán)格模式,在嚴(yán)格模式下,任何 mutation 處理函數(shù)以外修改 Vuex state 都會拋出錯誤)*/
    this.strict = strict

    // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    /*初始化根module,這也同時遞歸注冊了所有子modle,收集所有module的getter到_wrappedGetters中去,this._modules.root代表根module才獨(dú)有保存的Module對象*/
    installModule(this, state, [], this._modules.root)

    // initialize the store vm, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    /* 通過vm重設(shè)store,新建Vue對象使用Vue內(nèi)部的響應(yīng)式實現(xiàn)注冊state以及computed */
    resetStoreVM(this, state)

    // apply plugins
    /* 調(diào)用插件 */
    plugins.forEach(plugin => plugin(this))

    /* devtool插件 */
    if (Vue.config.devtools) {
      devtoolPlugin(this)
    }
  }

Vuex的源碼一共就一千行左右,構(gòu)造函數(shù)吃透基本掌握至少一半了,構(gòu)造函數(shù)中主要是初始化各種屬性。簡單的詳見注釋,這里我們主要看如何解析處理modules,首先來看this._modules = new ModuleCollection(options),ModuleCollection結(jié)構(gòu)如下

import Module from "./module"
import { assert, forEachValue } from "../util"

/*module收集類*/
export default class ModuleCollection {
  constructor (rawRootModule) { // new store(options)
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }

  /*獲取父級module*/
  get (path) {
  }

  /*
    獲取namespace,當(dāng)namespaced為true的時候會返回"moduleName/name"
    默認(rèn)情況下,模塊內(nèi)部的 action、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)。
    如果希望你的模塊更加自包含或提高可重用性,你可以通過添加 namespaced: true 的方式使其成為命名空間模塊。
    當(dāng)模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據(jù)模塊注冊的路徑調(diào)整命名。
  */
  getNamespace (path) {
  }

  update (rawRootModule) {
  }

  /*注冊*/
  register (path, rawModule, runtime = true) {
    if (process.env.NODE_ENV !== "production") {
      assertRawModule(path, rawModule)
    }

    /*新建一個Module對象*/
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
      /*path為空數(shù)組的代表跟節(jié)點*/
      this.root = newModule
    } else {
      /*獲取父級module*/
      const parent = this.get(path.slice(0, -1))//排除倒數(shù)第一個元素的數(shù)組,
      /*在父module中插入一個子module*/
      parent.addChild(path[path.length - 1], newModule)
    }

    // register nested modules
    /*遞歸注冊module*/
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        // concat不改變源數(shù)組,返回合并后的數(shù)組
        this.register(path.concat(key), rawChildModule, runtime)
      })
    }
  }

  /*注銷*/
  unregister (path) {
  }
}
/*Module構(gòu)造類*/
export default class Module {
  constructor (rawModule, runtime) {
    this.runtime = runtime
    this._children = Object.create(null)
    /*保存module*/
    this._rawModule = rawModule
    /*保存modele的state*/
    const rawState = rawModule.state
    this.state = (typeof rawState === "function" ? rawState() : rawState) || {}
  }

  /* 獲取namespace */
  get namespaced () {
  }

  /*插入一個子module,存入_children中*/
  addChild (key, module) {
    this._children[key] = module
  }

  /*移除一個子module*/
  removeChild (key) {
  }

  /*根據(jù)key獲取子module*/
  getChild (key) {
    return this._children[key]
  }

  /* 更新module */
  update (rawModule) {
  }
  /* 遍歷child  */
  forEachChild (fn) {
  }

  /* 遍歷getter */
  forEachGetter (fn) {
  }

  /* 遍歷action */
  forEachAction (fn) {
  }

  /* 遍歷matation */
  forEachMutation (fn) {
  }
}

ModuleCollection的作用就是收集和管理Module,構(gòu)造函數(shù)中對我們傳入的options的module進(jìn)行了注冊(register 是重點,詳見注釋,注冊方法中使用了遞歸,以此來注冊所有的module)。我們給出的實例經(jīng)過ModuleCollection的收集之后變成了什么樣子呢?

//this._modules簡單結(jié)構(gòu)示例
{
  root: {
    state: {},
    _children: {
      cart: {
        ...
      },
      products: {
        ...
      }
    }
  }
}

收集完了之后,我們看一下是如何初始化這些module的installModule(this, state, [], this._modules.root)

function installModule (store, rootState, path, module, hot) {
  /* 是否是根module */
  const isRoot = !path.length
  /* 獲取module的namespace */
  const namespace = store._modules.getNamespace(path)

  // register in namespace map
  /* 如果有namespace則在_modulesNamespaceMap中注冊 */
  if (module.namespaced) {
    store._modulesNamespaceMap[namespace] = module
  }

  // set state
  if (!isRoot && !hot) {
    /* 獲取父級的state */
    const parentState = getNestedState(rootState, path.slice(0, -1))//深度取值,并返回取到的值
    /* module的name */
    const moduleName = path[path.length - 1]
    store._withCommit(() => {// 添加響應(yīng)式屬性
      /* 將子module設(shè)置稱響應(yīng)式的 */
      Vue.set(parentState, moduleName, module.state)
    })
  }
  // 當(dāng)前模塊上下文信息
  const local = module.context = makeLocalContext(store, namespace, path)

  /* 遍歷注冊mutation */
  //mutation:key對應(yīng)的handler
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  /* 遍歷注冊action */
  module.forEachAction((action, key) => {
    const namespacedType = namespace + key
    registerAction(store, namespacedType, action, local)
  })

  /* 遍歷注冊getter */
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })

  /* 遞歸安裝mudule */
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}

在這里構(gòu)造了各個module的信息也就是localConext,包括各個模塊的mutation,action ,getter ,mudule ,其中運(yùn)用到了許多包裝的技巧(主要為了計算模塊的路徑),還有代理的方式訪問數(shù)據(jù),詳見注釋

resetStoreVM方法思路就是借助Vue響應(yīng)式來實現(xiàn)Vuex的響應(yīng)式

/* 通過vm重設(shè)store,新建Vue對象使用Vue內(nèi)部的響應(yīng)式實現(xiàn)注冊state以及computed */
function resetStoreVM (store, state, hot) {
  /* 存放之前的vm對象 */
  const oldVm = store._vm

  // bind store public getters
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}

  /* 通過Object.defineProperty為每一個getter方法設(shè)置get方法,比如獲取this.$store.getters.test的時候獲取的是store._vm.test,也就是Vue對象的computed屬性 */
  // key = wrappedGetters的key,fn = wrappedGetters的key對應(yīng)的value
  forEachValue(wrappedGetters, (fn, key) => {
    // use computed to leverage its lazy-caching mechanism
    computed[key] = () => fn(store)
    // store.getter并沒有像state一樣在class直接注冊了getter,setter,而是在這里定義的
    // 用過代理的方式借助Vue計算屬性實現(xiàn)了Vuex的計算屬性
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key], // 獲取計算蘇屬性(響應(yīng)式)
      enumerable: true // for local getters
    })
  })

  // use a Vue instance to store the state tree
  // suppress warnings just in case the user has added
  // some funky global mixins
  const silent = Vue.config.silent
  /* Vue.config.silent暫時設(shè)置為true的目的是在new一個Vue實例的過程中不會報出一切警告 */
  Vue.config.silent = true
  /*  這里new了一個Vue對象,運(yùn)用Vue內(nèi)部的響應(yīng)式實現(xiàn)注冊state以及computed*/
  //通過Vue的數(shù)據(jù)劫持,創(chuàng)造了dep,在Vue實例中使用的話Watcher會收集依賴,以達(dá)到響應(yīng)式的目的
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
  Vue.config.silent = silent

  // enable strict mode for new vm
  /* 使能嚴(yán)格模式,保證修改store只能通過mutation */
  if (store.strict) {
    enableStrictMode(store)
  }

  if (oldVm) {
    /* 解除舊vm的state的引用,以及銷毀舊的Vue對象 */
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldVm._data.$$state = null
      })
    }
    Vue.nextTick(() => oldVm.$destroy())
  }
}

在這里只要是把Vuex也構(gòu)造為響應(yīng)式的,store._vm指向一個Vue的實例,借助Vue數(shù)據(jù)劫持,創(chuàng)造了dep,在組件實例中使用的話Watcher會收集依賴,以達(dá)到響應(yīng)式的目的。
至此,構(gòu)造函數(shù)部分已經(jīng)過了一遍了。

最后

本文主要是學(xué)習(xí)了Vuex的初始化部分,實際的Vuex的api接口的實現(xiàn)也有相關(guān)的中文注釋,我已經(jīng)把主要部分中文注釋代碼放在這里,需者自取中文注釋代碼
學(xué)習(xí)過程中參考了許多大神的文章,一并感謝。

Reference

Vue.js 源碼解析(參考了大神的許多注釋,大神寫的太詳盡了,我只補(bǔ)充了一部分)
Vuex框架原理與源碼分析

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/52589.html

相關(guān)文章

  • 一張思維導(dǎo)圖輔助你深入了解 Vue | Vue-Router | Vuex 源碼架構(gòu)

    摘要:前言本文內(nèi)容講解的內(nèi)容一張思維導(dǎo)圖輔助你深入了解源碼架構(gòu)。總結(jié)以上內(nèi)容是筆者最近學(xué)習(xí)源碼時的收獲與所做的筆記,本文內(nèi)容大多是開源項目技術(shù)揭秘的內(nèi)容,只不過是以思維導(dǎo)圖的形式來展現(xiàn),內(nèi)容有省略,還加入了筆者的一點理解。1.前言 本文內(nèi)容講解的內(nèi)容:一張思維導(dǎo)圖輔助你深入了解 Vue | Vue-Router | Vuex 源碼架構(gòu)。 項目地址:github.com/biaochenxuy… 文...

    weij 評論0 收藏0
  • 深入學(xué)習(xí)Vuex

    摘要:深入學(xué)習(xí)作為配合使用的數(shù)據(jù)狀態(tài)管理庫,針對解決兄弟組件或多層級組件共享數(shù)據(jù)狀態(tài)的痛點問題來說,非常好用。至此,構(gòu)造函數(shù)部分已經(jīng)過了一遍了。 深入學(xué)習(xí)Vuex vuex作為配合vue使用的數(shù)據(jù)狀態(tài)管理庫,針對解決兄弟組件或多層級組件共享數(shù)據(jù)狀態(tài)的痛點問題來說,非常好用。本文以使用者的角度,結(jié)合源碼來學(xué)習(xí)vuex。其中也參考了許多前輩的文章,參見最后的Reference Vue加載Vuex...

    codercao 評論0 收藏0
  • 深入理解js

    摘要:詳解十大常用設(shè)計模式力薦深度好文深入理解大設(shè)計模式收集各種疑難雜癥的問題集錦關(guān)于,工作和學(xué)習(xí)過程中遇到過許多問題,也解答過許多別人的問題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實現(xiàn)方式 延遲加載也稱為惰性加載,即在長網(wǎng)頁中延遲加載圖像。用戶滾動到它們之前,視口外的圖像不會加載。本文詳細(xì)介紹了三種延遲加載的實現(xiàn)方式。 詳解 Javascript十大常用設(shè)計模式 力薦~ ...

    caikeal 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<