Vuex笔记 - 一篇搞定Vuex
Vuex
1.Vuex简介
-
Vuex是专门在Vue中实现集中式数据管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理,也是一种组件间通信的方式,适用于任意组件间通信
-
使用Vuex的场合:
- 当多个组件依赖于同一数据
- 来自不同组件的行为需要变更同一状态
-
Vuex工作原理
-
store:store是一个容器,包含着应用中大部分的状态 (state),store同时管理着State,Mutations,Actions
-
State:提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data
-
Mutations:更改store中数据的唯一方法是提交 mutation。mutation 类似于事件,每个 mutation 都有一个字符串形式的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是实际进行数据更改的地方,并且它会接受 state 作为第一个参数
-
Actions:Action和Mutation相似,Mutation 不能进行异步操作,若要进行异步操作,就得使用Action
- 如果不需要与后台进行数据交互,一般会直接略过过Actions
2. 准备工作
-
搭建vuex的步骤
-
安装vuex
npm i vuex@[版本号]
Vue2使用3,Vue3使用4 -
创建一个文件夹(一般命名为store),在内部创建一个js文件(一般命名为index),这就是vuex存放公用数据的地方。index文件内部:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state:{}, mutation:{} })
-
在main.js中挂载store
import store from './store/index' new Vue({ render: h => h(App), store }).$mount('#app')
-
3. State
-
State提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data
-
由于Vuex 使用单一状态树,即一个对象就包含了全部的应用层级状态,所以每个应用只能包含一个 store 实例。State中的数据都只能通过Mutations来改变,除此之外任何直接修改State中数据的方式都将报错(v-model这种双向绑定的也会)
-
创建state
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { name:"张三", age:12, count:0 }, })
-
获取state中的数据
由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态
const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } }
4. Mutation
-
更改store中数据的唯一方法是提交 mutation。mutation 类似于事件,每个 mutation 都有一个字符串形式的事件类型 (type)和一个回调函数 (handler),这个回调函数就是实际进行数据更改的地方
-
mutation会接受 state 作为第一个参数,除了State之外额外附加的参数被称为载荷(payload)
-
mutation内的函数必须是同步的,任何异步操作都将导致devtools无法捕捉到数据变化。如果需要异步操作,使用actions
-
创建Muatation
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { name:"张三", age:12, count:0 }, mutations:{ addcount(state,num){ state.count = state.count + num } } })
-
调用Muatation
methods:{ btn(){ //这里的10就是载荷 this.$store.commit("addcount",10) } }
5. Action
-
Action 类似于 mutation,不同点在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作
-
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用
context.commit
提交一个 mutation -
创建Action
const store = createStore({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
-
调用Action
this.$store.dispatch('increment')
6. Getter
-
有时候需要从 store 中的 state 中派生出一些数据(过滤、数学计算等),此时就会用到Getter。Getter可以理解为store中的计算属性
-
创建Getter
const store = createStore({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { doneTodosCount (state) { return state.todos.filter(todo => todo.done) } } })
-
调用Getter
this.$store.getters.doneTodosCount
7. 辅助函数
7.1 mapState & mapGetter
-
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余
<!--这种写法很冗余--> <h1>当前求和为:{{ this.$store.state.sum }}</h1> <h2>当前求和放大10倍为 <h3>我在{{ this.$store.state.school }} 学习{{ this.$store.state.subject }}</h3> <!--改成这样也还是很冗余--> <h1>当前求和为:{{ sum }}</h1> <h3>我在{{ school }} 学习{{ subject }}</h3> <!--......--> computed:{ sum(){ return this.$store.state.sum }, school(){ return this.$store.state.school }, subject(){ return this.$store.state.subject } }
-
借助mapState生成计算属性,从state中读取数据
//mapState中的每一个项都会生成为一个函数,从而同时获取state中的多个数值 //mapState返回的是一个对象,所以这里使用对象展开符...将对象混入到外部函数中 ...mapState({sum:'sum',school:'school',subject:'subject'}) ...mapState({ count: state => state.count, countAlias: 'count', //和上一行等价 countPlusLocalState (state) { return state.count + this.localCount }) //特别的,当函数名与调用的 state 的属性名相同时,可以给 mapState 传一个字符串数组 sum(){return this.$store.state.sum}, school(){return this.$store.state.school} //↓ mapState({sum:'sum',school:'school'}) //↓ mapState(['sum','school'])
-
mapGetter同理
doneCount(){return this.$store.getters.doneTodosCount} ...mapGetters({ doneCount: 'doneTodosCount' })
7.2 mapMutations & mapActions
-
mapMutations和mapState类似,但是如果要提交载荷,那么就需要在调用时额外声明
<template> <div id="app"> <h1>当前求和为:{{ this.$store.state.sum }}</h1> <select name="addNumber" v-model="addNum"> <option :value="1">1</option> <option :value="2">2</option> <option :value="3">3</option> </select> <button @click="Add(n)">+</button> <button @click="Reduce(n)">-</button> <button @click="Odd">当前求和为奇数再加</button> </div> </template> <script> export default { methods: { ...mapMutations({ //将this.Add(this.n)映射为this.$store.commit('Add',this.n) Add:'Add' }), //将this.Reduce(this.n)映射为this.$store.commit('Reduce',this.n) //将this.Odd()映射为this.$store.commit('Odd') ...mapMutations(['Reduce','Odd']), } } //其中,Odd无法正常工作,因为它没有提交载荷,也就无法获取n。最后获取的数值也将是错误的 </script>
-
mapActions同理
...mapActions({add: 'increment'}), ...mapActions(['increment','incrementBy']),
8. 模块化
-
Vuex 允许我们将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter。模块化的目的是让代码更好维护,让多种数据分类更加明确
-
创建具有多个模块的store
const moduleA = { state: () => ({ ... }), mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: () => ({ ... }), mutations: { ... }, actions: { ... } } const store = createStore({ modules: { a: moduleA, b: moduleB } })
-
调用指定store内的数据
this.$store.a this.$store.b
-
单个模块中的Mutation,Action,Getter访问的都将是当前模块中的局部State数据
8.1 命名空间
-
默认情况下,模块内部的 action, mutation, getter 仍然是注册在全局命名空间的。这会导致在不同的、无命名空间的模块中定义两个相同的 action/mutation/getter 时出现错误
-
可以通过添加
namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名 -
注意:在开启模块化和命名空间之后,state中数据的调用方式将会发生变化
const moduleB = { namespaced: true, state: () => ({ ... }), mutations: { ... }, actions: { ... } }
-
开启命名空间后,组件中读取state数据
//方式一:直接读取 this.$store.state.personAbout.list //方式二:借助辅助函数读取,第一个参数为要读取的模块,第二个参数为要获取的值 ...mapState('countAbout',['sum','school','subject'])
-
开启命名空间后,组件中读取getters数据
//方式一 this.$store.getters['personAbout/firstPersonName'] //方式二 ...mapState('countAbout',['bigSum'])
-
开启命名空间后,组件中调用dispatch
//方式一 this.$store.dispatch('personAbout/addPersonWang',person) //方式二 ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
-
开启命名空间后,组件中调用commit
//方式一 this.$store.commit('personAbout/ADD_PERSON',person) //方式二 ...mapState('countAbout',{incrementOdd:'JIA',decrement:'JIAN'})