Vuex的狀態(tài)管理——store

1.前言

狀態(tài)管理在開發(fā)中已經(jīng)算是老生常談了,本篇文章我們轉(zhuǎn)向前端方向的Vue框架,看看Vuex是怎么通過store處理數(shù)據(jù)狀態(tài)的管理的。由于Vue框架本身就是響應(yīng)式處理數(shù)據(jù)的,所以store更多的為了跨路由去管理數(shù)據(jù)的狀態(tài)。在使用store之前,我們先來說明store的兩個特性:

  • Vuex的狀態(tài)存儲是響應(yīng)式的。當(dāng)Vue組件從store中讀取狀態(tài)的時候,若store中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會相應(yīng)地得到高效更新。
  • 你不能直接改變store中的狀態(tài)。改變store中的狀態(tài)的唯一途徑就是顯式地提交(commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用。

store是由五大成員組成的,開發(fā)者可根據(jù)業(yè)務(wù)復(fù)雜程度,合理的使用它們。我們先來了解一下store的五大成員:

2.五大成員

  • StateVuexstore中的state是響應(yīng)式的,當(dāng)state中的數(shù)據(jù)發(fā)生變化時,所有依賴于該數(shù)據(jù)的組件都會自動更新。
  • GettersVuexstore中的getters可以理解為store的計(jì)算屬性,它們會基于store中的state派生出一些新的狀態(tài),供組件使用。
  • MutationsVuexstore中的mutations是同步的事件操作,它們用于更改store中的state。在Vuex中,mutations是唯一可以修改store中的state的方式。
  • ActionsVuexstore中的actions是異步的事件操作,它們用于處理異步邏輯,如API請求等。Actions可以通過提交mutations來修改store中的state。
  • ModulesVuexstore可以通過模塊化的方式組織代碼,將一個大的store拆分成多個小的模塊,每個模塊都有自己的state、gettersmutationsactions。

其中StateMutations是構(gòu)成store必不可少的成員。那么store是怎么使用的呢?我們來看下一章節(jié)。

3.Store的使用

3.1.安裝:

npm install vuex

3.2.最簡單的用法:

我們創(chuàng)建一個新的js文件,引入vuex并通過Vue.use()使用它,然創(chuàng)建一個Store對象并export

import Vuex from 'vuex'
import Vue from "vue";

Vue.use(Vuex)

const store =  new Vuex.Store({
    state: {    //在state對象建立需要數(shù)據(jù)
        count: 0
    },
    mutations: {
        add: function (state) {
            state.count++;
        }
    },
});

export default store

其中count是我們在state里聲明的需要被監(jiān)聽的變量。add是個方法,其實(shí)現(xiàn)是count++。接下來我們在main.js里注入store實(shí)例:

import store from "./store"

new Vue({
  router,
  store, 
  render: h => h(App),
}).$mount('#app')

好了我們的store已經(jīng)可以用了。我們假設(shè)有兩個路由:home pagenew page,我們看看store是怎么監(jiān)聽數(shù)據(jù)變化并跨路由共享的。
home page代碼如下:

<template>
  <div>
    <div>I am home page</div>
    <div>{{ getCount() }}</div>
    <div @click="addCount" class="button">add count</div>
    <div @click="gotoNewPage" class="button">Go to newpage</div>
  </div>
</template>
  
  <script>
export default {
  methods: {
    gotoNewPage() {
      this.$router.push({ path: "/newpage" });
    },
    getCount() {
        return this.$store.state.count;
    },
    addCount() {
        this.$store.commit('add');
    },
  },
};
</script>
  
  <style lang="scss" scoped>
.button {
  width: 100px;
  height: 50px;
  background: #527dab;
  margin-top: 15px;
}
</style>
  

其中getCount()方法從storestate里讀取count的值。addCount()方法用來調(diào)用mutationsadd()方法,實(shí)現(xiàn)count++。們點(diǎn)擊“add count”按鈕看下效果:

IMG_2940.GIF

我們可以看到count的狀態(tài)變化會被監(jiān)聽到。那么我們跳轉(zhuǎn)至new page,代碼如下:

<template>
  <div>
    <div>new page</div>
    <div>{{ getCount() }}</div>
    <div @click="back" class="button">back</div>
  </div>
</template>
  
  <script>
export default {
  methods: {
    back() {
      this.$router.push({
        path: "/home",
      });
    },
    getCount() {
        return this.$store.state.count;
    },
  },
};
</script>
  
  <style lang="scss" scoped>
.button {
  width: 100px;
  height: 50px;
  background: #527dab;
  margin-top: 15px;
}
</style>

跨路由讀取到了count的值。

IMG_2942.GIF

以上就是Store最簡單的用法,接下來我們進(jìn)階一下。

3.3.getters和actions

store增加gettersactions

const store = new Vuex.Store({
    state: {    //在state對象建立需要數(shù)據(jù)
        count: 0
    },
    getters: {
        getCount: function (state) {
            return "count是: " + state.count
        }
    },
    mutations: {
        add: function (state) {
            state.count++;
        }
    },
    actions: {
        addAction: function (context) {
            setTimeout(() => {
                context.commit('add')
            }, 500)

        }
    }
});

如果上一節(jié)所講,getters類似于計(jì)算屬性,有時候我們需要從store中的 state中派生出一些狀態(tài),那么就需要getters了。getters的返回值會根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會被重新計(jì)算。actions的核心在于可以包含任意異步操作。它提交的是mutation,不能直接修改state的值。addAction模擬了一個異步操作,500ms之后執(zhí)行add方法。我們接下來看一下使用,改造一下home pagegetCount()addCount()方法:

    getCount() {
      return this.$store.getters.getCount;
    },
    addCount() {
      this.$store.dispatch("addAction");
    },

actions需要使用store.dispatch()進(jìn)行分發(fā),我們看一下執(zhí)行結(jié)果:

IMG_2944.GIF

點(diǎn)擊add count按鈕后500ms后,count加1,通過getters得到新的顯示文本。

3.4.Modules

由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時,store對象就有可能變得相當(dāng)臃腫。
為了解決以上問題,Vuex允許我們將store分割成模塊(module)。每個模塊擁有自己的state、mutationaction、getter、甚至是嵌套子模塊。接著舉個例子:


const userStore = {
    state: {    //在state對象建立需要數(shù)據(jù)
        name: "tom"
    },
    getters: {
        getName: function (state) {
            return "name是: " + state.name
        }
    },
    mutations: {
        setName: function (state, name) {
            state.name = name;
        }
    },
    actions: {
        setNameAction: function (context,name) {
            console.log("setNameAction")
            setTimeout(() => {
                console.log("setName")
                context.commit('setName', name)
            }, 500)

        }
    }
};

export default userStore

剛才的實(shí)例代碼中,我們增加一個userStore,為其配置了state,gettersmutationsactions。然后我們?yōu)橹暗?code>store增加modules,代碼如下:

import Vuex from 'vuex'
import Vue from "vue";
import userModule from "@/store/modules/user"

Vue.use(Vuex)

const store = new Vuex.Store({
    state: {    //在state對象建立需要數(shù)據(jù)
        count: 0
    },
    getters: {
        getCount: function (state) {
            return "count是: " + state.count
        }
    },
    mutations: {
        add: function (state) {
            state.count++;
        }
    },
    actions: {
        addAction: function (context) {
            setTimeout(() => {
                context.commit('add')
            }, 500)

        }
    },
    modules:{
        user: userModule
    }
});

export default store

導(dǎo)入了userModule,我們看看這個usermodule是怎么用的。

<template>
  <div>
    <div>I am home page</div>

    <div>{{ getName() }}</div>
    <div @click="setName" class="button">set Name</div>
  </div>
</template>
  
  <script>
export default {
  methods: {
    getName(){
        return this.$store.getters.getName; 
    },
    setName() {
        this.$store.dispatch("setNameAction","bigcatduan");
    },
  },
};
</script>
  
  <style lang="scss" scoped>
.button {
  width: 100px;
  height: 50px;
  background: #527dab;
  margin-top: 15px;
}
</style>

如果是從getters里取數(shù)據(jù)用法同之前的實(shí)例一樣。如果是從state里取數(shù)據(jù)則需要指定module

    getName(){
        return this.$store.state.user.name; 
    },

需要注意的是,如果module里的getter的方法名與父節(jié)點(diǎn)store的沖突了,則運(yùn)行的時候會取父節(jié)點(diǎn)store的值,但會報duplicate getter key的錯誤。如果module里的getter的方法名與同節(jié)點(diǎn)的其它modules沖突了,則運(yùn)行的時候會根據(jù)在節(jié)點(diǎn)注冊的順序來取值,同時報duplicate getter key的錯誤。比如:

    modules:{
        user: userModule,
        product: productModule
    }

由于userModule先被注冊到modules里,所以取值時會取userModule的值。
如果父子之間或同節(jié)點(diǎn)的actionmutation的方法名相同的話,則各個節(jié)點(diǎn)同方法名的方法都會被執(zhí)行。
好了到此為止我們發(fā)現(xiàn)一個問題,如果項(xiàng)目龐大,免不了會在不同的module里定義相同的變量或方法名稱,讓開發(fā)者自己去維護(hù)龐大的方法/變量列表顯然不現(xiàn)實(shí),于是vuex引入了命名空間namespace

3.5.命名空間

默認(rèn)情況下,模塊內(nèi)部的actionmutation仍然是注冊在全局命名空間的——這樣使得多個模塊能夠?qū)ν粋€actionmutation作出響應(yīng)。Getter同樣也默認(rèn)注冊在全局命名空間,但是目前這并非出于功能上的目的(僅僅是維持現(xiàn)狀來避免非兼容性變更)。必須注意,不要在不同的、無命名空間的模塊中定義兩個相同的getter從而導(dǎo)致錯誤。
如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過添加namespaced: true的方式使其成為帶命名空間的模塊。當(dāng)模塊被注冊后,它的所有getter、actionmutation都會自動根據(jù)模塊注冊的路徑調(diào)整命名。
比如我們?yōu)閯偛诺?code>userModule設(shè)置namespaced: true。


const userStore = {
    namespaced: true,
    state: {    //在state對象建立需要數(shù)據(jù)
        name: "tom"
    },
    getters: {
        getName: function (state) {
            return "name是: " + state.name
        }
    },
    mutations: {
        setName: function (state, name) {
            state.name = name;
        }
    },
    actions: {
        setNameAction: function (context,name) {
            console.log("setNameAction")
            setTimeout(() => {
                console.log("setName")
                context.commit('setName', name)
            }, 500)

        }
    }
};

export default userStore

setget的相應(yīng)方式變化如下:

    getName() {
      return this.$store.getters["user/getName"]; 
    },
    setName() {
      this.$store.dispatch("user/setNameAction", "bigcatduan"); 
    },

getter,dispatchcommit時需要顯示的指明局部的命名空間。
接下來我們思考一下,是否可以在不同的module下讀取到其它module的內(nèi)容呢?答案是可以的。比如我們的store結(jié)構(gòu)如下:
根節(jié)點(diǎn):

import Vuex from 'vuex'
import Vue from "vue";
import userModule from "@/store/modules/user"
import productModule from "@/store/modules/product"

Vue.use(Vuex)

const store = new Vuex.Store({
    state: {    //在state對象建立需要數(shù)據(jù)
        count: 20
    },
    getters: {
        getCount: function (state) {
            return "count是: " + state.count
        }
    },
    mutations: {
        add: function (state) {
            state.count++;
        }
    },
    actions: {
        addAction: function (context) {
            setTimeout(() => {
                context.commit('add')
            }, 500)

        }
    },
    modules:{
        user: userModule,
        product: productModule
    }
});

export default store

里面包含了userproduct這兩個module。
代碼如下:

//user

const userStore = {
    namespaced: true,
    state: {    //在state對象建立需要數(shù)據(jù)
        name: "tom"
    },
    getters: {
        getName: function (state, getters, rootState, rootGetters) {
            return "name是: " + state.name
        }
    },
    mutations: {
        setName: function (state, name) {
            state.name = name;
        }
    },
    actions: {
        setNameAction: function (context,name) {
            setTimeout(() => {
                context.commit('setName', name)
            }, 500)

        }
    }
};

export default userStore
//product

const productStore = {
    namespaced: true,
    state: {    //在state對象建立需要數(shù)據(jù)
        price: 15
    },
    getters: {
        getPrice: function (state) {
            return "price是: " + state.price
        }
    },
    mutations: {
        setPrice: function (state, price) {
            state.price = price;
        }
    },
    actions: {
        setPriceAction: function (context,price) {
            setTimeout(() => {
                context.commit('setPrice', price)
            }, 500)

        }
    }
};

export default productStore

我們改造一下usergetters

//user
    getters: {
        getName: function (state, getters, rootState, rootGetters) {
            console.log("rootState count: ",rootState.count)
            console.log("product state price: ",rootState.product.price)
            console.log("root getter getCount: ",rootGetters.getCount)
            console.log("product getter getPrice: ",rootGetters["product/getPrice"])
            return "name是: " + state.name
        }
    },

除了state,vuex還為我們提供了gettersrootStaterootGetters這幾個參數(shù)。于是我們可以讀取根節(jié)點(diǎn)state相應(yīng)的變量,其他節(jié)點(diǎn)state相應(yīng)的變量,根getters里的方法,還可以找到其他getters里的方法。上面代碼的打印結(jié)果如下:

截屏2023-04-03 11.11.34.png

我們再來改造一下actions

    actions: {
        setNameActionRoot: {
            root:true,
            handler (namespacedContext, name){
                console.log("setNameAction")
                setTimeout(() => {
                    console.log("setName")
                    namespacedContext.commit('setName', name)
                }, 500)
            }

        },
    }

root:true意味著我們?yōu)檫@個帶命名空間的模塊注冊全局action,所以我們依然可以不指定命名空間執(zhí)行dispatch方法:

this.$store.dispatch("setNameActionRoot", "bigcatduan");

繼續(xù)改造一下actions

    actions: {
        setNameAction: function (context,name) {
            setTimeout(() => {
                context.commit('setName', name)
            }, 500)

        },
        setSomeActions ( { dispatch, commit, getters, rootGetters },someAction){
            console.log("root getter getCount: ",rootGetters.getCount)
            console.log("product getter getPrice: ",rootGetters["product/getPrice"])
            dispatch('setNameAction',someAction.name)
            commit('setName',someAction.name)
            commit('product/setPrice', someAction.price, {root:true})
        }
    }

可以拿到全局rootGetters。若需要在全局命名空間內(nèi)分發(fā)action 或提交 mutation,將{ root: true } 作為第三參數(shù)傳給dispatchcommit即可。
好了以上就是store的用法,更多用法大家可以繼續(xù)探索。接下來我們來講一下store的實(shí)現(xiàn)原理。

4.實(shí)現(xiàn)原理

我們先來看看構(gòu)造方法:

//Store
  constructor (options = {}) {
    if (__DEV__) {
      assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
      assert(this instanceof Store, `store must be called with the new operator.`)
    }

    const {
      plugins = [],
      strict = false,
      devtools
    } = options

    // store internal state
    this._committing = false
    this._actions = Object.create(null)
    this._actionSubscribers = []
    this._mutations = Object.create(null)
    this._wrappedGetters = Object.create(null)
    this._modules = new ModuleCollection(options)
    this._modulesNamespaceMap = Object.create(null)
    this._subscribers = []
    this._makeLocalGettersCache = Object.create(null)

    // EffectScope instance. when registering new getters, we wrap them inside
    // EffectScope so that getters (computed) would not be destroyed on
    // component unmount.
    this._scope = null

    this._devtools = devtools

    // 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

    // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    installModule(this, state, [], this._modules.root)

    // initialize the store state, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    resetStoreState(this, state)

    // apply plugins
    plugins.forEach(plugin => plugin(this))
  }

創(chuàng)建了各個成員變量,其中最重要的是_modules的創(chuàng)建。之后依次執(zhí)行installModule()resetStoreState()plugins.forEach(plugin => plugin(this))。我們先來看看_modules的創(chuàng)建。

4.1. ModuleCollection

_modules對所有的module進(jìn)行了初始化并構(gòu)造了其依賴關(guān)系。其構(gòu)造方法實(shí)現(xiàn)如下:

//ModuleCollection
  constructor (rawRootModule) {
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }

調(diào)用了register方法:

  register (path, rawModule, runtime = true) {
    if (__DEV__) {
      assertRawModule(path, rawModule)
    }

    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
      this.root = newModule
    } else {
      const parent = this.get(path.slice(0, -1))
      parent.addChild(path[path.length - 1], newModule)
    }

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

在初始化的時候,由于傳入的是根module,path.length === 0,所以會創(chuàng)建一個Module對象,并為root賦值,把它設(shè)置為根module。Module的構(gòu)造方法如下:

//Module
  constructor (rawModule, runtime) {
    this.runtime = runtime
    // Store some children item
    this._children = Object.create(null)
    // Store the origin module object which passed by programmer
    this._rawModule = rawModule
    const rawState = rawModule.state

    // Store the origin module's state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }

回到ModuleCollection,如果存在子modules,就會遍歷modules,遞歸執(zhí)行register()方法。而再次執(zhí)行register()方法時,path.length === 0false,會找到它的parent,并執(zhí)行addChild()方法:

//Module
  addChild (key, module) {
    this._children[key] = module
  }

由此實(shí)現(xiàn)了modules依賴關(guān)系的創(chuàng)建,生成了一個modules樹。接下來我們看installModule()方法的實(shí)現(xiàn):

4.2.installModule()

它的作用是對根module進(jìn)行初始化,根據(jù)上一章節(jié)生成的modules樹,遞歸注冊每一個module。代碼如下:

export function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  const namespace = store._modules.getNamespace(path)

  // register in namespace map
  if (module.namespaced) {
    if (store._modulesNamespaceMap[namespace] && __DEV__) {
      console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
    }
    store._modulesNamespaceMap[namespace] = module
  }

  // set state
  if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
      if (__DEV__) {
        if (moduleName in parentState) {
          console.warn(
            `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
          )
        }
      }
      parentState[moduleName] = module.state
    })
  }

  const local = module.context = makeLocalContext(store, namespace, path)

  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })

  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
  })

  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })

  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}

如果設(shè)置了nameSpace,則向_modulesNamespaceMap注冊。之后如果不是根module,將模塊的state添加到state鏈中,就可以按照state.moduleName進(jìn)行訪問。
接下來創(chuàng)建makeLocalContext上下文,為該module設(shè)置局部的dispatchcommit方法以及gettersstate,為的是在局部的模塊內(nèi)調(diào)用模塊定義的actionmutation,這個過程具體就不展開了。
接下來分別遍歷_mutations,_actions_wrappedGetters進(jìn)行注冊。注冊過程大同小異,我們?nèi)?code>_mutations的注冊來看看:

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

把對應(yīng)的值函數(shù)封裝后存儲在數(shù)組里面,然后作為store._mutations的屬性。store._mutations收集了我們傳入的所有mutation函數(shù)。
installModule()的分析就完成了,我們繼續(xù)看resetStoreState()的實(shí)現(xiàn)。

4.4.resetStoreState()

這個方法的作用是初始化state,并且注冊getters作為一個computed屬性。

export function resetStoreState (store, state, hot) {
  const oldState = store._state
  const oldScope = store._scope

  // bind store public getters
  store.getters = {}
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null)
  const wrappedGetters = store._wrappedGetters
  const computedObj = {}
  const computedCache = {}

  // create a new effect scope and create computed object inside it to avoid
  // getters (computed) getting destroyed on component unmount.
  const scope = effectScope(true)

  scope.run(() => {
    forEachValue(wrappedGetters, (fn, key) => {
      // use computed to leverage its lazy-caching mechanism
      // direct inline function use will lead to closure preserving oldState.
      // using partial to return function with only arguments preserved in closure environment.
      computedObj[key] = partial(fn, store)
      computedCache[key] = computed(() => computedObj[key]())
      Object.defineProperty(store.getters, key, {
        get: () => computedCache[key].value,
        enumerable: true // for local getters
      })
    })
  })

  store._state = reactive({
    data: state
  })
//...
}

遍歷store._wrappedGetters,并新建computed對象進(jìn)行存儲,通過Object.defineProperty方法為getters對象建立屬性并實(shí)現(xiàn)響應(yīng)式,使得我們通過this.$store.getters.xxxgetter能夠訪問到該getters。最后利用reactivestate實(shí)現(xiàn)響應(yīng)式。

4.5.install

vue里,通過Vue.use()掛載全局變量,所以業(yè)務(wù)組件中通過this就能訪問到store

  install (app, injectKey) {
    app.provide(injectKey || storeKey, this)
    app.config.globalProperties.$store = this

    const useDevtools = this._devtools !== undefined
      ? this._devtools
      : __DEV__ || __VUE_PROD_DEVTOOLS__

    if (useDevtools) {
      addDevtools(app, this)
    }
  }

4.6.commit和dispatch

最后我們再來看看commit和dispatch都做了什么

//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]
//...
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })

    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))
//...
  }

主要做了兩件事:1.遍歷_mutations,執(zhí)行handler。2.遍歷_subscribers,通知訂閱者。我們在看dispatch

  dispatch (_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)

    const action = { type, payload }
    const entry = this._actions[type]
//...

    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)

    return new Promise((resolve, reject) => {
      result.then(res => {
        try {
          this._actionSubscribers
            .filter(sub => sub.after)
            .forEach(sub => sub.after(action, this.state))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in after action subscribers: `)
            console.error(e)
          }
        }
        resolve(res)
      }, error => {
        try {
          this._actionSubscribers
            .filter(sub => sub.error)
            .forEach(sub => sub.error(action, this.state, error))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in error action subscribers: `)
            console.error(e)
          }
        }
        reject(error)
      })
    })
  }

dispatch使用Promise封裝了異步操作,遍歷_actions執(zhí)行handler操作,并遍歷_actionSubscribers通知訂閱者。

5.總結(jié)

Vuex的狀態(tài)管理方式store的使用和原理就介紹到這里。最后截取一張官網(wǎng)的圖為大家進(jìn)行一下總結(jié)。

截屏2023-04-03 17.54.19.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容