vue3全家桶 之 状态管理Vuex4的使用
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。随着vue3发布,Vuex也更新到了的4.x版本来与vue3的新特性做适配。本文基于Vuex4,主要写了在Vue3的
setup
中如何使用。
基础
起步
Vuex解决了什么问题?
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。 什么时候用到Vuex?
- 如果需要构建一个中大型单页应用,用Vuex可以很好的解决外部管理状态的问题。如果只是一个小型应用,使用Vuex可能是繁琐冗余的。
安装
npm install vuex@next --save
或
yarn add vuex@next --save
store
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state) 。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。
store目录结构
├── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── count.js # 模块1
└── cart.js # 模块2
安装Vuex后,创建一个store。创建文件 src/store/modules/count.js
const state = () => ({
num: 100,
});
const mutations = {
increment(state) {
state.num++;
},
};
export default {
namespaced: true,
state,
mutations,
};
创建文件 src/store/index.js
import { createStore } from 'vuex';
import count from './modules/count.js';
export const store = createStore({
modules: {
count,
},
});
main.js 中导入
import { store } from './store/index.js'
createApp(App).use(store).mount('#app')
一个简单的store创建完成。下面是怎么使用这个store,VuexPage.vue 中
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const num = computed(() => store.state.count.num);
function increment() {
store.commit('count/increment');
}
</script>
<button @click="increment">{{ num }}</button>
核心概念
- State
- Getter
- Mutation
- Action
- Module
1. State
在Vue组件中获取Vuex状态
Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:
<script setup>
const store = useStore();
const num = computed(() => store.state.count.num);
</script>
2. Getter
定义getters
getters
可以认为是store
的计算属性,src/store/modules/cart.js
const state = () => ({
cartList: [
{ name: '羽毛球', price: 5 },
{ name: '篮球', price: 120 },
{ name: '洗发水', price: 50 },
{ name: '运动鞋', price: 200 },
{ name: '纸巾', price: 20 },
],
});
const getters = {
getList: (state) => state.cartList,
getListLow: (state) => (num) => {
return state.cartList.filter((item) => item.price < num);
},
};
通过属性访问getters
src/views/vuex-page/VuexPage.vue
// 模板中可以直接使用 $store.getters['cart/getList']
const cartList = computed(() => store.getters['cart/getList']);
<li v-for="cartList in cartList">
{{ cartList.name }} - {{ cartList.price }}元
</li>
通过方法访问getters(可传参数)
src/views/vuex-page/VuexPage.vue
const cartListLow = ref('');
const lowNum = ref(1000);
function filterCartList() {
cartListLow.value = store.getters['cart/getListLow'](lowNum.value);
}
输入一个值lowNum
来筛选符合价格的商品,给src/store/modules/cart.js中getters的getListLow
传递这个值作为参数,这个getListLow
会返回符合价格的商品
<input type="number" v-model="lowNum" />
<button @click="filterCartList">筛选</button>
<ul class="cartul">
<li v-for="cartListLow in cartListLow">
{{ cartListLow.name }} - {{ cartListLow.price }}元
</li>
</ul>
3. Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler) 。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
const mutations = {
increment(state) {
state.num++;
},
};
使用commit
唤醒一个 mutation 处理函数,此时state.name会执行+1的操作
store.commit('increment');
提交载荷(Payload)
向 store.commit
传入额外的参数,即 mutation 的载荷(payload) 。在大多数情况下,载荷应该是一个对象。
const mutations = {
increment(state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
Mutation 必须是同步函数
在 Vuex 中,mutation 都是同步事务,要处理异步操作需要使用到Action。
4. Action
注册一个Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。(只有mutation才能正真改变stroe中的state)
- Action 可以包含任意异步操作。 src/store/modules/count.js
// ...
const actions = {
increment(context) {
context.commit('increment');
},
};
export default {
// ...
actions,
};
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。
实践中,可以通过es6参数解构简化代码:
const actions = {
increment({ commit }) {
commit('increment')
}
}
分发Action
store.dispatch('count/increment');
// 载荷方式带参数
store.dispatch('count/increment', {a: 123});
Action包含异步操作
const actions = {
increment(context) {
// context.commit('increment');
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('increment');
resolve();
}, 500);
});
},
};
处理异步结果
store.dispatch('count/increment').then(() => {
console.log('-----执行完action');
// 这里写一些逻辑
});
5. Module
Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter。
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
本文默认采用了模块的写法,每个子模块写在src/store/modules目录下,例如count.js
const state = () => ({
// ...
});
const mutations = {
// ...
};
const actions = {
// ...
};
export default {
namespaced: true,
state,
mutations,
actions,
};
子模块导出时添加namespaced: true
,给每个模块独立的命名空间。
src/store/index.js为出口,里面引入了module模块并导出:
import { createStore } from 'vuex';
import count from './modules/count.js';
import cart from './modules/cart.js';
export const store = createStore({
modules: {
count,
cart,
},
});
转载自:https://juejin.cn/post/7036635003584774175