vuex的使用

257 阅读4分钟

一:什么是Vuex?

Vuex 是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

二:vuex的核心

  • Store:仓库,Store 是使用 Vuex应用程序的核心,每个应用仅有一个Store,它是一个容器,包含着应用中的大部分状态,当然我们不能直接改变Store中的状态,我们要通过提交Mutations的方式改变状态。
  • State:状态,保存在Store中,因为Store是唯一的,所以State也是唯一的,称为单一状态树,这里的状态是响应式的。
  • Getter:相当于Vuex中的计算属性,方便从一个属性派生出其他的值,它内部可以对计算的结果进行缓存,只有当依赖的状态发生改变的时候,才会重新计算。
  • Mutation:状态的变化必须要通过提交Mutation来完成。
  • Actions:与Mutation类似,不同的是可以进行异步的操作,内部改变状态的时候都需要改变Mutation
  • Module:模块,由于使用的单一状态树让所有的状态都会集中到一个比较大的对象中,应用变得很复杂的时候,Store对象就会变得相当臃肿,为了解决这些问题Vuex允许我们将Store分割成模块,每个模块拥有自己的StateMutationActionsGetter,甚至是嵌套的子模块。

三:vuex的基本结构

// store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head' // 引入的module模块
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    state:{
        name:'helloVueX'
    },
    mutations: {

    },
    actions: {

    },
    modules: {
        ModuleHead
    }
})

export default store
// main.js

import Vue from 'vue'
import App from './App.vue'
import 'style/comment.css'
import router from 'route/index' 

import store from 'store/index' // 引入store文件

// ElementUI的引入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Vue.config.productionTip = false

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

四:vuex的使用

1)state的使用
// demo.vue
<template>
  <div>
      <!-- 基础设施C -->
     <span>$store.state获取vuex的值:{{$store.state.name}}</span>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

每次使用变量都要前面写$store.state很是麻烦,所以这里使用Vuex内部提供的mapState的函数,会帮我们生成状态对应的计算属性

<template>
  <div>
      <!-- 基础设施C -->
      <span>mapState获取vuex的值:{{name}}</span>
  </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
  computed:{
    ...mapState(['name'])
  }
}
</script>

<style>

</style>

当然啦,我们也可以给获取的state的值取别名的,和上面方法差不多,不同的是,mapState参数是对象,键是别名,值是内容。

<template>
  <div>
      <!-- 基础设施C -->
      <span>mapState获取vuex的值并设置别名:{{my_name}}</span>
  </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
  computed:{
    ...mapState({my_name:'name'}) // 参数是对象,键是别名,值是state对象里的值
  }
}
</script>

<style>

</style>
2)Getter的使用

Vuex中的getter就相当于组件中的计算属性,如果想要对state的数据进行简单的处理在展示,可以使用getter

// store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head'
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    state:{
        name:'helloVueX',
        my_name: '老哥'
    },
    getters:{ 
        mapData({my_name}) { // 参数是state对象,这里把my_name解构出来了
            return `我是经过改造的${my_name}`
        }
    },
    mutations: {

    },
    actions: {

    },
    modules: {
        ModuleHead
    }
})

export default store
<template>
  <div>
      <!-- 基础设施C -->
      <span>{{$store.getters.mapData}}</span>
  </div>
</template>

<script>
export default {
  computed:{
  }
}
</script>

<style>

</style>

同样那样引用过于麻烦,那么和mapState一样,使用内部的mapGetters,也是将其映射到组件的计算属性,其用法和mapState一样,也可以为了避免冲突使用对象设置别名

<template>
  <div>
      <!-- 基础设施C -->
      <span>mapGetters取值:{{mapData}}</span>
      <br>
      <span>mapGetters取值并取别名:{{another_name}}</span>
  </div>
</template>

<script>
import {mapGetters} from 'vuex'
export default {
  computed:{
    ...mapGetters(['mapData']),
    ...mapGetters({ another_name:'map_data'})
  }
}
</script>

<style>

</style>
3)Mutation的使用

状态的修改必须提交MutationMutation必须是同步执行的。

// store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head'
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    state:{
        name:'helloVueX',
        my_name: '老哥',
        age:0
    },
    getters:{
        mapData({my_name}) {
            return `我是经过改造的${my_name}`
        },
        map_data({my_name}) {
            return `${my_name}有别名的`
        }
    },
    mutations: {
        // 第一个参数是store,
        //此处需要对store对象的值进行修改,不可以解构里面的变量,要写全
        // 不能写成addStoreAge({age},payload)的形式
        // 第二个参数是提交的参数
        addStoreAge(store,payload) {
            store.age += payload
        },
        desStoreAge(store,payload) {
            if (store.age > 0) {
                store.age -= payload
            }
        }
    },
    actions: {

    },
    modules: {
        ModuleHead
    }
})

export default store
// demo.vue

<template>
  <div>
      <!-- 基础设施C -->
      <button @click="$store.commit('desStoreAge',2)">减2</button>
      <input v-model="$store.state.age"/>
      <button @click="$store.commit('addStoreAge',2)">加2</button>
      
  </div>
</template>

<script>
export default {
  computed:{
  }
}
</script>

<style>

</style>

同样那样引用过于麻烦,那么和mapState一样,使用内部的mapMutations ,使用map方法将当前的mutation映射到methods中,其依旧会返回一个对象,这个对象中存储的是mutation中映射的方法

<template>
  <div>
      <!-- 基础设施C -->
      <button @click="des(2)">减2</button>
      <input v-model="age"/>
      <button @click="add(2)">加2</button>
      
  </div>
</template>

<script>
import {mapMutations,mapState} from 'vuex'
export default {
  computed:{
    ...mapState(['age'])
  },
  methods:{
    // ...mapMutations(['desStoreAge','addStoreAge'])
    ...mapMutations({des:'desStoreAge',add:'addStoreAge'}) // 起别名
  }
}
</script>

<style>

</style>
4)Actions的使用

如果有异步的修改,需要使用actions,在actions中可以执行异步操作,当异步操作结束后,如果需要更改状态,还需要提交Mutation

// store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head'
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    state:{
        name:'helloVueX',
        my_name: '老哥',
        age:0
    },
    getters:{
        mapData({my_name}) {
            return `我是经过改造的${my_name}`
        },
        map_data({my_name}) {
            return `${my_name}有别名的`
        }
    },
    mutations: {
        // 第一个参数是store,
        //此处需要对store对象的值进行修改,不可以解构里面的变量,要写全
        // 不能写成addStoreAge({age},payload)的形式
        // 第二个参数是提交的参数
        addStoreAge(store,payload) {
            store.age += payload
        },
        desStoreAge(store,payload) {
            if (store.age > 0) {
                store.age -= payload
            }
        }
    },
    actions: {
        // 第一个参数是context上下文,这个对象中有state,commit,getters等成员
        // 第二个参数是payLoad
        addStoreAgeAsync({commit},payload) {
          setTimeout(() => { // 延迟两秒,模拟异步操作
            commit('addStoreAge',payload)
          }, 2000);
        },
        desStoreAgeAsync({commit},payload) {
            setTimeout(()=> {
                commit('desStoreAge',payload)
            },2000)
        }
    },
    modules: {
        ModuleHead
    }
})

export default store
<template>
  <div>
      <!-- 基础设施C -->
      <button @click="$store.dispatch('desStoreAgeAsync',2)">减2</button>
      <input v-model="age"/>
      <button @click="$store.dispatch('addStoreAgeAsync',2)">加2</button>
      
  </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
  computed:{
    ...mapState(['age'])
  },
  methods:{
}
</script>

<style>

</style>

结果是:点击按钮2秒后,值才会被修改

同样的,actions也有对应的mapActions,不需要使用如上面的方法dispatch

<template>
  <div>
      <!-- 基础设施C -->
      <button @click="des(2)">减2</button>
      <input v-model="age"/>
      <button @click="add(2)">加2</button>
      
  </div>
</template>

<script>
import {mapState,mapActions} from 'vuex'
export default {
  computed:{
    ...mapState(['age'])
  },
  methods:{
    // ...mapActions(['desStoreAgeAsync','addStoreAgeAsync'])
    ...mapActions({des:'desStoreAgeAsync',add:'addStoreAgeAsync'})
  }
}
</script>

<style>

</style>
5)Modules的使用

模块可以让我们把单一状态树拆分成多个模块,每个模块都可以拥有自己的statemutationactiongetter甚至嵌套子模块。

模块定义

store文件夹中,创建一个modules文件夹,里面每一个js文件就是一个模块,下面是每一个模块的定义格式

// head.js,这里定义值

let headData = [
    {
        lable: '基础设施1',
        value: 'infrastructureA',
        path: 'tab_a'
    },
    {
        lable: '基础设施2',
        value: 'infrastructureB',
        path: 'tab_b'
    },
    {
        lable: '基础设施3',
        value: 'infrastructureC',
        path: 'tab_c'
    },
    {
        lable: '基础设施4',
        value: 'infrastructureD',
        path: 'tab_d'
    },
    {
        lable: '基础设施5',
        value: 'infrastructureE',
        path: 'tab_e'
    }
]
export  {
    headData
}

注意:子模块需要使用命名空间namespaced: true,否则是找不到的

// modules/head/index.js // 子模块

import {headData} from './head'
let head_store = {
    // 命名空间
    namespaced: true,

    state:{
        headData,
        name: null,
        userInfo: null
    },

    mutations: {

        login(state,payload) {
            state.name= payload.my_name
        },

        saveInfo(state,payload) {
            console.log('异步修改state:',state)
            console.log('异步修改payload:',payload)
            state.userInfo= payload
        }
    },
    actions: {

        //异步修改
        asyncSaveInfo({commit},data) {
            commit('saveInfo',data)
        }

    },
    modules: {
        
    }
}
export default head_store
// store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head'
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    state:{
        name:'helloVueX',
        my_name: '老哥',
        age:0
    },
    getters:{
        mapData({my_name}) {
            return `我是经过改造的${my_name}`
        },
        map_data({my_name}) {
            return `${my_name}有别名的`
        }
    },
    mutations: {
        // 第一个参数是store,
        //此处需要对store对象的值进行修改,不可以解构里面的变量,要写全
        // 不能写成addStoreAge({age},payload)的形式
        // 第二个参数是提交的参数
        addStoreAge(store,payload) {
            store.age += payload
        },
        desStoreAge(store,payload) {
            if (store.age > 0) {
                store.age -= payload
            }
        }
    },
    actions: {
        // 第一个参数是context上下文,这个对象中有state,commit,getters等成员
        // 第二个参数是payLoad
        addStoreAgeAsync({commit},payload) {
          setTimeout(() => { // 延迟两秒,模拟异步操作
            commit('addStoreAge',payload)
          }, 2000);
        },
        desStoreAgeAsync({commit},payload) {
            setTimeout(()=> {
                commit('desStoreAge',payload)
            },2000)
        }
    },
    modules: {
        ModuleHead // 引入子模块
    }
})

export default store

使用子模块headData的数据

<template>
  <div>
      <!-- 基础设施C -->
      <p v-for="(item,index) in $store.state.ModuleHead.headData" :key="index">
        {{item.lable}}
      </p>
      
  </div>
</template>

<script>
export default {
  computed:{
  },
  methods:{
  }
}
</script>

<style>

</style>

同样的,我们也可以使用mapStatemapMutations

<template>
  <div>
      <!-- 基础设施C -->
      <p v-for="(item,index) in headData" :key="index">
        {{item.lable}}
      </p>
      
  </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
  computed:{
    ...mapState('ModuleHead',['headData'])
  },
  methods:{
  }
}
</script>

<style>

</style>

五:Vuex严格模式

所有的状态变更必须提交mutation,但是如果在组件中获取到$store.state.msg进行修改,语法层面没有问题,却破坏了Vuex的约定,且devTools也无法跟踪到状态的修改,开启严格模式之后,如果在组件中直接修改state,会报错。

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head'
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    strict: true, // 开启严格模式
    state:{
        name:'helloVueX',
        my_name: '老哥',
        age:2
    },
    getters:{
        mapData({my_name}) {
            return `我是经过改造的${my_name}`
        },
        map_data({my_name}) {
            return `${my_name}有别名的`
        }
    },
    mutations: {
        // 第一个参数是store,
        //此处需要对store对象的值进行修改,不可以解构里面的变量,要写全
        // 不能写成addStoreAge({age},payload)的形式
        // 第二个参数是提交的参数
        addStoreAge(store,payload) {
            store.age += payload
        },
        desStoreAge(store,payload) {
            if (store.age > 0) {
                store.age -= payload
            }
        }
    },
    actions: {
    },
    modules: {
        ModuleHead
    }
})

export default store
<template>
  <div>
      <!-- 基础设施C -->
      <button @click="$store.state.age -= 2">des</button>
      <input v-model="age"/>
      <button @click="$store.state.age += 2">add</button>
      
  </div>
</template>

<script>
import {mapState} from 'vuex'
export default {
  computed:{
    ...mapState(['age'])
  },
  methods:{
  }
}
</script>

<style>

</style>

注意:

  • strict:false是非严格模式,默认是false,可以直接通过$store.state.age来进行修改
  • 不要在生产环境开启严格模式,因为严格模式会深度检测状态树,会影响性能。在开发模式中开启严格模式,在生产环境中关闭严格模式
export default new Vuex.Store({
 strict: process.env.NODE_ENV !== 'production',
 state: {
  ...
}

vuex插件

会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数

代码如下:

// plugin.js

export function my_plugin(store) {
    store.subscribe((mutation,state) => {
        console.log('mutation==',mutation.type)  
        console.log('state==',state) 
    })
}
// store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import ModuleHead from './modules/head'
import {my_plugin} from './Plugin'
//挂载Vuex
Vue.use(Vuex)

//创建VueX对象
const store = new Vuex.Store({
    strict: true,
    plugins:[my_plugin],
    state:{
        name:'helloVueX',
        my_name: '老哥',
        age:2
    },
    getters:{
        mapData({my_name}) {
            return `我是经过改造的${my_name}`
        },
        map_data({my_name}) {
            return `${my_name}有别名的`
        }
    },
    mutations: {
        // 第一个参数是store,
        //此处需要对store对象的值进行修改,不可以解构里面的变量,要写全
        // 不能写成addStoreAge({age},payload)的形式
        // 第二个参数是提交的参数
        addStoreAge(store,payload) {
            console.log('触发addStoreAge')
            store.age += payload
        },
        desStoreAge(store,payload) {
            console.log('触发desStoreAge')
            if (store.age > 0) {
                store.age -= payload
            }
        }
    },
    actions: {
    },
    modules: {
        ModuleHead
    }
})

export default store