Vue 复学 之 仓库 Vuex

Vuex是vue中的一种状态管理模式,就是一个 状态仓库,仓库做什么?存储状态、管理状态(数据)的变化、提供状态获取窗口。

本文中一些测试用例基于@vue/composition-api@1.7.1,vuex@3.6.2, vue@2.6.10 做的验证。Vue3和 vuex@4.x用法有所不同。

用来干什么,能做什么用?

可用于全局数据存储跨层级组件通讯动态路由

在通讯的关键是,一方通知后,另一方怎么知晓。在vue2中利用 mapMutations, mapActions 解析出来,则可直接使用解析后的方法进行通知,另外一方 根据 mapGetters, mapState 解析出来的 属性、方法做接收,达到互通目的。

大体剖析

大致理解如下图:

1.Vuex的 install 对Vue提供use 接入, 利用Vuex.Store 产生实例化对象,具体实例化的规则通过配置的 state, getters, mutations, actions,namespaced 确定。

2.mutations对应及时同步更新仓库数据,对应 store实例的.commit方法

3.actions对应异步延时更新数据,对应store实例 的.dispatch方法。

4.commit和 dispatch 函数所带参数,可以是指定type属性的对象,也可以利用参数1直接type类型。 ({type: "mutations中的项目", payload}) 或者 ("actions中的项目")

5. 关于响应式,vue2直接用 mapState, mapGetter。 compositon-api 中可以直接使用 store示例对象下的 { 变量 } =  toRefs(store.state)

6.针对有命名空间模块,采用 “命名空间名(modules下挂接的属性名)/ ” 进行使用。

6.支持常量类型。为了让大家都清楚,提供了哪些mutation,action, 独立一个 _types ,在types中定义好类型名字,做store 的配置时,直接使用 [_types.XXX]

7.针对state\ 非函数式getter,数据会进行缓存。

针对Vue3组合式,对应Vuex4.x 版本, 其使用方式方法有所不同, 产生 store 用CreateStore, 示例使用 app.use(store),不是属性挂接,使用 useStore。

以前觉得vuex的使用必须按常规挂到主 Vue示例上,其实是着相了。 不管是 vuex 还是其他库、框架,本质都是基于某种语言实现的。vuex一样,我们常常使用 Vue.use(Vuex), 在 主Vue实例上挂接 {store: new Vuex.Store({state, getters, matutions, actions})}, 利用 $store 去访问。

那么既然都是js,是不是可以直接使用 store实例就好,答案是可以的,但在某些地方使用时会很不方便,比如vue2直接使用 mapXXX解析,为什么?因为Vuex中提供的映射 mapXXXX 函数内部采用的就是 this.$store 对象进行相关操作。

使用compostion-api时,直接引入 利用  store.js 中生成的 store 示例,不绑定到vue上,也可直接使用。

PS: Vue.use(Vuex)不能省略,否则出现错误 must call Vue.use(Vuex) before creating a store instance.

具体用法vue2 + vuex + compostion-api

针对不采用compositon-api的,利用 mapXXX在对应属性上映射即可,方便快捷。

安装背负(Vue.use(Vuex)) -> 指腹为婚(new Vue({store: new Vuex.Store({}) })) -> 开支散叶 (...mapXXX)

直接访问比如

export default {

    computed: {

        ...mapState({

//箭头函数可使代码更简练

            count: state => state.count,

//传字符串参数 'count' 等同于 `state => state.count`

            countAlias: 'count',

//为了能够使用 `this` 获取局部状态,必须使用常规函数

            countPlusLocalState (state) {

              return state.count + this.localCount

            }

        }),

        ...mapGetters([

          'doneTodosCount',

          'anotherGetter',

        ])

    },

    methods: {

        ...mapMutations([

'increment', //将 `this.increment()` 映射为 `this.$store.commit('increment')`

// `mapMutations`也支持载荷:

'incrementBy' //将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`

        ]),

        ...mapMutations({

add: 'increment' //将 `this.add()` 映射为 `this.$store.commit('increment')`

        }),

        ...mapActions({

add: 'add' //将 `this.add()` 映射为 `this.$store.dispatch('add')`

        })

     }

 }

不做映射使用this.$store访问, 而composition-api 比较类似这种做法。

vue2 + vuex + compostion-api

基础使用过程:利用Vue.use(Vuex)将vuex安装到vue上,背上vuex,开始vuex之旅。利用 new Vuex.Store() 创建 store 示例, 无需挂载到 Vue主示例的 store上。需要触发状态值维护的地方,直接 store.commit , store.dispatch 即可,需要 接收状态值的地方,使用 toRefs(store.state.模块) 的方式接收即可。

注意,如果要使用getter,取根级getter相对方便, store.getters.。 启用了命名空间的getter, 相对麻烦,可使用 store._wrappedGetters 获取,且都是函数式的。

代码如下:

store.js store生成文件

import Vue from "vue"

import Vuex from "vuex"

import app from "./modules/app"

import calc from "./modules/calc"

Vue.use(Vuex)  //必须在 new Vuex.Store 之前

const store = new Vuex.Store({

    modules: {

        app,

        calc

    },

//这里启用了模块,注意:启用了模块管理于根root上配置不冲突

state(){  //用函数可以避免 污染

        return {}            

    },

    getters: {},

    mutations: {},

    actions: {}

})

export default store

app模块配置.js

//为方便 mutation  actions 的 外部使用,可使用 动态属性名

const app = {

// namespaced: true, //启用命名空间后, getter 直接通过 store 实例对象是无法直接通过 store.getter获取到的

state(){ //状态

        return {

            count: 1

        }

    },

    getters: {

        getMoreUseAttr(state, getters){

            return state.count + 10

        },

getMoreUseMethod: state => more => { //函数式getter, 在使用时 函数调用

            return state.count + more

        }

    },

mutations: { //加工、维护。只能同步。 为方便

//外部使用 .commit(mutationName, playload) 或者对象方式 {type, ...payload}

        increment (state, payload) {

            state.count += payload?.count

        }

    },

actions: { //行为。 异步维护。

//第一个参数是store模块上下文 conctext,外部使用使用.dispatch(actionName, payload)或者({type: actionName, ...payload}) payload一般建议使用对象,可常量。

        addAsync({commit}, payload){

            return new Promise((resolve, reject) => {

                setTimeout(() => {

                    commit({type: "increment", ...payload})

                    resolve(payload)

                }, 2000)

            })

        }

    }

}

export default app

启用命名空间后,getter直接通过 store 实例对象是无法直接通过 store.getter获取到的

calc模块配置

const calc = {

    namespaced: true,

    state(){

        return {

            count: 2

        }

    },

    mutations: {

increment(state, payload){ //注意,这是与app配置中重名的。

            state.count += payload.count

        }

    },

    actions:{

        addAsync:{

root: true, //注意,这是与app配置中重名的。并提升到root根级

            handler(context, payload){

                new Promise((resolve, reject) => {

                    setTimeout(() => {

                        context.commit("increment", payload)

                    }, 1000)

                })

            }

        }

    }

}

export default calc

在启用命名空间的情况,mutation或 action 也可使用 root: true 的方式提升到根级root上。

当多个模块有重名mutation或 actions 时, 如果启用命名空间,则互不关系,访问时指定命名空间,如果未启用命名空间或提升到了根级,在访问时,所有同名的都会被调用。

入口文件main.js

//入口文件 main.js

import Vue from 'vue'

import App from './App.vue'

Vue.config.productionTip = false

import store from "./components/store/index.js"

new Vue({

  render: function (h) { return h(App) },

// store不挂接

}).$mount('#app')

状态值维护组件:/components/vuex/index.vue

<template>

    <div>

        BusIndex

改变数据</button>

异步改变</button>

        <div>

获取更大值</button>

更大值{{more}}

        </div>

    </div>

</template>

<script>

import {onMounted, ref, computed, getCurrentInstance} from "@vue/composition-api"

import store from "./store/store.js"

export default {

    name: "VuexIndex",

    setup(props, ctx){

        const changeData = () => {

            store.commit("increment", {count: 2})

            store.commit("calc/increment", {count: 5})

        }

        const asynChangeData = () => {

            store.dispatch("addAsync", {count: 10}, { root: true })

        }

//访问根级的getMoreUseAttr

        const more = computed(() => store.getters.getMoreUseAttr)

        return {

            changeData, asynChangeData, asynChangePowerData, more

        }

    }

}

</script>

状态值接收组件/components/vuex/comp1.vue

<template>

    <div>

状态值app.count: {{count}} calc.count: {{count1}}

    </div>

</template>

<script>

import {onMounted, ref, toRefs} from "@vue/composition-api"

import store from "./store/store.js"

export default {

    name: "BusComp1",

    setup(props, ctx){

        const st = store

        const {count} = toRefs(st.state.app)

        const {count: count1} = toRefs(st.state.calc)

        return {

            count,

            count1

        }

    }

}

</script>

主容器组件App.vue

<template>

  <div id="app">

    <div>

vuex测试

      <vuexIndex></vuexIndex>

      <vuexcomp1></vuexcomp1>

    </div>

  </div>

</template>

<script>

import vuexIndex from "./components/vuex/index.vue"

import vuexcomp1 from  "./components/vuex/comp1.vue"

export default {

  name: 'App',

  components: {

    vuexIndex, vuexcomp1

  }

}

</script>

官网地址:https://vuex.vuejs.org/zh/

关键词回顾:

 vuex, store, state, mutations, actions, getters;

mapState, mapMutations, mapActions, mapGetters;

modules, namespaced,registerModule;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359

推荐阅读更多精彩内容

  • Vuex 的学习记录 资料参考网址Vuex中文官网Vuex项目结构示例 -- 购物车Vuex 通俗版教程Nuxt....
    流云012阅读 1,462评论 0 7
  • 安装 npm npm install vuex --save 在一个模块化的打包系统中,您必须显式地通过Vue.u...
    萧玄辞阅读 2,945评论 0 7
  • 1. Vuex简介 Vuex是专门用来管理vue.js应用程序中状态的一个插件。他的作用是将应用中的所有状态都放在...
    黄黄黄大帅阅读 433评论 0 0
  • 1. vuex简介 vuex是专门用来管理vue.js应用程序中状态的一个vue插件。他的作用是将应用中的所有状态...
    一只章鱼哥阅读 295评论 0 0
  • Vuex是什么? Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件...
    萧玄辞阅读 3,122评论 0 6