这篇文章主要收录了Vuex的基本使用和它的5大核心:state、getters、mutations、actions、modules
1 状态管理
vuex是什么?
状态管理工具
状态管理是什么?
可以简单看成多个组件需要共享的变量全部存储到一个对象里面
为什么官方还要专门出一个vuex插件?
能共享、响应式
管理什么状态呢?
- 用户的登陆状态
- 用户名称、头像、位置等等
- 购物车等等
1.1 基本使用
多个组件之间怎么共享状态的呢?
Vue Compents就是组件树;
需要进行共享和管理的状态放State对象里面;
当需要修改某个状态时,commit一个mutation(不要直接改!),就会执行Mutations中对应的函数,修改状态;
但Mutations中是不允许有异步请求的;
当修改某个状态需要发起异步请求时,就需要dispatch到Actions,然后由Actions去commit到Mutations;
创建store
vuex3.x的写法
安装
npm install vuex -s
store文件夹下的index.js;
使用Vuex.Store() 创建,state是个对象;
import vuex from 'vuex'
const store = new vuex.Store({
state: {
counter
}
})
export default store
vuex4.x写法
安装
npm install vuex@next -S
store文件夹下的index.js;
使用createStore() 创建,state是个函数,类似vue2.x中的data选项;
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0
}
}
})
export default store
main.js
vue2.x写法
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.use(store)
new Vue({
store,
render: h => h(App)
}).$mount('#app')
vue3.x写法
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
HelloWorld.vue
<h3>{{ $store.state.counter }}</h3>
提交修改
- 直接改devtools跟踪不到是哪个组件修改了状态
- 也可以从vue components直接到mutations,devtools只会跟踪同步操作
- 是要是有异步操作(如网络请求)就不能绕过actions,actions会跟踪异步操作
store目录下的index.js;
mutations里面的方法会默认传入state参数;
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0
}
},
mutations: {
increment(state) {
state.counter
}
}
})
export default store
vue2.x写法
HelloWorld.vue
<h2>{{ $store.state.counter }}</h2>
<button @click="add()">+</button>
methods: {
add () {
this.$store.commit('increment')
}
}
- 使用commit() 提交
- commit()的参数是在vuex实例中mutations的方法名
2 vuex核心
- state
- getters
- mutations
- action
- module
2.1 state
单一状态树
用一个对象包含全部应用的状态;
SSOT, Single Source Of Truth,单一数据源;
为了方便管理和维护、模块化,vuex提供了module;
辅助函数mapX()
vuex提供了一种辅助函数,方便获取状态,mapX(X可以是state、mutations等等);
如果想将某些状态放入计算属性,可以使用mapX;
mapX()返回的是一个对象;
有数组写法,如果想自定义名字可以使用对象写法;
store目录下的index.js
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0,
name: 'zsf'
}
}
})
export default store
HelloWorld.vue
<h2>{{ counter }}</h2>
<h2>{{ name }}</h2>
import { mapState } from 'vuex'
computed: {
// 其它计算属性省略
...mapState(['counter', 'name'])
}
2.2 getters
某些属性需要变化后才能使用,这个时候可以用getters;
类似计算属性computed;
getters里面的方法接收两个参数,第一个是state对象,第二个是其它getters对象;
store文件夹下的index.js
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0,
name: 'zsf'
}
},
getters: {
powerCounter(state) {
return state.counter * state.counter
}
}
})
export default store
传参
如果希望getters里的函数传参呢?
getters: {
moreAgeStu(age) {
return function (age) {
return state.student.filter((s) => s.age > age)
}
}
}
辅助函数
同样,getters也有对应的辅助函数mapGetters() ,用法与state的类似~;
2.3 mutations
传参
mutations里的函数的第二个参数payload,负载;
payload是通过 $store.commit() 的第二个参数传进来的;
Home.vue
<button @click="addCount(5)">+5</button>
methods: {
addCount (count) {
return this.$store.commit('increment', count)
}
}
store目录下的index.js
mutations: {
increment(state, payload) {
state.counter += payload
}
}
提交风格
一般风格
addCount (count) {
return this.$store.commit('increment', count)
}
另一种风格
addCount (count) {
return this.$store.commit({
type: 'increment',
count: count
})
}
常量
在mutations中使用常量,能减少一些commit() 和mutations中函数名不一致的错误;
以上面的increment为例
store文件夹下的mutations-types.js
export const INCREMENT = 'increment'
Home.vue
<h2>{{ $store.state.counter }}</h2>
<button @click="add()">+</button>
import {
INCREMENT
} from './store/mutations-types'
methods: {
add () {
this.$store.commit(INCREMENT)
}
}
store文件夹下的index.js
使用 [] 获取常量中的值
import {
INCREMENT
} from './mutations-types'
mutations: {
[INCREMENT] (state) {
state.counter++
}
}
辅助函数
当然,mutations也有对应的辅助函数mapMutations() ,用法与上面的辅助函数类似;
2.4 actions
action类似于mutation;
但action提交的是mutation,而不是直接变更状态;
action可以包含任意异步操作;
action中的函数的第一个参数context,是一个和store实例有相同方法和属性的context对象;
dispatch() 可以返回一个Promise对象;
基本使用
Home.vue
<h2>{{ $store.state.counter }}</h2>
<button @click="add()">+</button>
methods: {
add () {
this.$store.dispatch('increment')
}
}
store目录下的index.js
state() {
return {
counter: 0
}
},
mutations: {
increment(state) {
state.counter++
}
},
action: {
// 这里使用定时器模拟异步操作
incrementAction(context) {
setTimeout(() => {
context.commit('increment')
}, 1000)
}
}
当你点击+按钮,会过一秒counter才加1~
dispatch()
在组件中要是想知道某一次派发有没有完成,这就需要组件对请求成功之后提示,而不是全部交给action;
这时就可以使用dispatch() 返回Promise啦;
Home.vue
<h2>{{ $store.state.counter }}</h2>
<button @click="add()">+</button>
methods: {
add () {
this.$store.dispatch('increment').then((res) => {
console.log(res)
}, (err) => {
console.log(err)
})
}
}
store目录下的index.js
state() {
return {
counter: 0
}
},
mutations: {
increment(state) {
state.counter++
}
},
action: {
// 这里使用定时器模拟异步操作
incrementAction(context) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('increment')
resolve('提交成功!')
}, 1000)
}).catch((err) => {
reject(err)
})
}
}
increment这个mutation提交成功之后,执行resolve(),将提交成功的结果交给组件处理~
辅助函数
同样,action也有对应的辅助函数mapAction() ,用法与上面的辅助函数类似;
2.5 module
由于使用单一状态树,应用的所有状态都会集中到store对象中;
当应用变得复杂,store对象就会变得相当臃肿;
为了解决这一问题,Vuex允许将store对象分成模块(module) ;
每个模块有自己的state、mutation、action、getters等等;
在modules目录下的home.js
const home = {
state() {
return {
counter: 0
}
},
...
}
在modules目录下的user.js
const home = {
state() {
return {
counter: 1
}
},
...
}
store目录下的index.js
module: {
home,
user
}
Home.vue
<h2>{{ $store.state.home.counter }}</h2>
<h2>{{ $store.state.user.counter }}</h2>
最后屏幕显示0和1
命名空间
默认情况下,模块内部的 action 和 mutations 仍是注册在全局命名空间的;
这样使得多个模块能够对同一个 action 或 mutations 作出响应;
getters 同样也默认注册在全局命名空间,但是目前这并非出于功能上的目的;
如果希望使用或修改某个模块的状态时,不影响全局命名空间的其它状态;
这时就需要给模块带上命名空间了,模块内新增一个namespaced: true属性;
使用带有命名空间的模块里的状态,需要用类似路径的方式;
如果需要commit提交修改,类似~;
const home = {
namespaced: true
state() {
return {
counter: 1
}
},
...
}
store目录下的index.js
module: {
home
}
Home.vue
<h2>{{ $store.getters[home/counter] }}</h2>
最后显示1
辅助函数
同样,模块中也可以使用辅助函数;
但是注意:上面使用的辅助函数的前提是在全局命名空间中,局部命名空间使用辅助函数写法有所不同,详情看官网~;