简介
Vuex是Vue.js应用程序的状态管理模式+库。它充当应用程序中所有组件的集中存储,其规则确保只能以可预测的方式更改状态。
简而言之就是提供一种状态管理的库,并且状态的改变是可预测的。
状态: 我个人理解为在你的组件中的任何一个变量都可以是状态。只要你想,都可以写进状态,但我相信你不会这样做的。
安装
- 已有项目可使用
vue add vuex
添加vuex。 - 创建项目时可以选择 Manually select features 自定义配置。
-
npm install vuex@next --save
,需要手创建store文件,建议使用前两种。
安装好后项目src文件下将创建一个store文件夹,其内部存在一个index文件
// index.js
import { createStore } from 'vuex'
export default createStore({
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
})
vuex4.x为了保持和vue3创建方式一致,所以建议使用createStore
创建
核心概念
state
存储数据
往state添加一个变量 count
,此时 count
将在项目下的任何组件内都可以访问。state内的数据称之为状态。在state中存储数据与data内存储数据规则相同。
state(){
count: 0
}
组件内访问count
- 模板内
$store.state.count
- 对象内
this.$store.state.count
由于Vuex商店是反应式的,因此从中
检索
状态的最简单方法是简单地从计算属性中返回一些商店状态:
computed(){
count(){
return this.$store.state.count;
}
}
此时就可以通过计算属性count访问store中的数据count。每当store中的count修改时,将导致重新计算计算属性,并触发关联的DOM更新。
如果需要引入的状态过多,当给每个状态绑定计算属性时,显然代码会变得重复和冗长。为了解决这个问题,导入 mapState
方法。
mapState可以帮助我们生成计算的state函数。
mapState
需要从 vuex 中导入 mapState 模块
import { mapState } from 'vuex'
使用mapState有两种方法
// 传入对象形式
computed: mapState({
// 第一种
count: state => state.count,
// 第二种:传递字符串 'count' 等同于 state => state.count
countAlias: 'count',
// 第三种:结合当前组件状态 data中定义localcount
countPlusLocalState(state){
return state.count + this.localcount;
}
})
mapState当映射的计算属性的名称与状态子树名称相同时,我们还可以传递字符串数组。
computed: mapState([ 'count' ])
访问时
this.count
store的computed 与 组件的computed结合
当前组件的计算属性(computed) 与store的计算属性(computed) 一起使用,就需要利用 对象扩展运算符
。
mapState返回一个对象
computed: {
localCountDecorate(){
return `本地组件状态${this.localcount}`
},
// 使用传递数组的方式,也可以使用传递对象的形式
...mapState([ 'count' ])
}
组件计算属性写在mapState内同样有效,但接受的参数不同,所以需要分开
组件仍可以具有本地状态
使用Vuex并不意味着您应该将所有状态都放入Vuex。尽管将更多状态添加到Vuex中可以使您的状态突变更明确和可调试,但有时它还可以使代码更冗长和间接。如果一个状态严格地属于单个组件,那么将其保留为本地状态就可以了。
getters
getters可以称之为装饰器,他可以将state中的数据做一层处理(装饰/筛选),返回给组件。
首先在state中创建一个状态(商品列表)
articleList: [
{id: '001', articleName: '* cotton', description: '*盛产优质长绒棉', stock: 1000000, price: 100},
{id: '002', articleName: 'shoe', description: 'LiNing 韦德之道4', stock: 10, price: 100},
{id: '003', articleName: 'Graphics card', description: '3080系列显卡', stock: 0, price: 100},
{id: '004', articleName: 'vaccine', description: '新型冠状病毒疫苗', stock: 100000000, price: 100},
{id: '005', articleName: 'xiaomi 11', description: 'xioami 第11代手机', stock: 100000, price: 100},
{id: '006', articleName: 'Kirin Chip', description: '华为 麒麟芯片', stock: 0, price: 100}
]
在getters中对其进行筛选
getters: {
Instock(state, getters){ // state作为第一个参数, getters 访问其他getters
console.log(getters);
return state.articleList.filter(item => item.stock != 0);
}
}
getters接收两个参数
- state: 访问当前store中的state
- getters: 访问当前store中的其他getters
现在就对state中的状态articleList经行一次筛选,把stock 为0的都去掉。
组件中访问
访问方式同state
computed: {
Instock(){ // 可不使用同名
return this.$store.getters.Instock;
}
}
同样可使用mapGetters映射到computed,减少代码量。
mapGetters
规则同mapState
computed: {
...mapState(['Instock'])
}
访问时
this.Instock()
获取指定数据
getters可返回一个函数以此接收 一个参数的传入,用于查找数据。
// getters
searchArticle: (state) => (id) => { // 返回函数传入id,使用id查找数据
return state.articleList.filter(item => item.id == id);
}
组件中
...mapGetters([ 'Instock', 'searchArticle' ])
此时可通过传递参数以使返回指定数据
this.searchArticle('001'); // 指定返回id为001的数据
getters只有对state中的状态进行装饰并返回的能力,并不能直接改变state。改变state需要使用mutations
。
mutations
state中的状态改变只能在mutations中。并且mutations中不能存在异步操作
mutations: {
addCount(state, payload){
// 接收组件内执行传递的参数累加
state.count += payload.count;
}
}
可接受两个参数
- state:访问当前store的state
- payload: 传递的参数。大多数情况下应为对象,以此传递更多的状态
组件中 methods
中声明。mutations通过commit
提交改变。
// template
<button @click="addCount">增加</button>
// js
methods: {
addCount(){
this.$store.commit('addCount', {count: 1}); // {count: 1} 传递的参数
}
}
简化:mapMutations
import { mapMutations } from 'vuex';
methods: {
// 传递数组
...mapMutations(['addCount']) // this.addCount() 执行
// 传递对象(赋新值)
...mapMutations({ localtotal: 'addCount' }) // this.localtotal() 执行
}
在有些情况下,会不可避免的在更改数据时进行请求或者其他异步操作,而mutations中不允许使用异步操作。此时将使用 actions
actions
动作类似于mutations(突变),不同之处在于:
- 动作不会改变状态,而是执行改变。
- 动作可以包含任意异步操作。
actions没有改变state的权利,但是可以commit mutations,所以说actions只是执行改变。
actions: {
asynCount(context, products){
setTimeout(() => {
context.commit('addCount', products);
}, 1000);
}
}
接收两个参数
- context:store上下文对象。可使用解构,包含commit、dispatch、getters、rootGetters、state、rootState
- products:传递的参数
组件中调用。actions通过dispatch
触发
// template
<button @click="asynAddCount">一秒后加一</button>
// js
methods: {
asynAddCount(){
this.$store.dispatch('asynCount', { count: 3 });
}
}
简化:mapActions
import { mapActions } from 'vuex';
methods: {
// 传递数组
...mapActions(['asynCount']) // this.asynCount() 执行
// 传递对象
...mapActions({ localAsyncTotal: 'asynCount' }) // this.localAsyncTotal() 执行
}
mudeles
随着我们应用程序规模的扩大,商店可能会变得臃肿。为了解决这个问题,Vuex允许我们将商店划分为模块。每个模块可以包含其自己的状态,变异,动作,获取器,甚至是嵌套模块-一直到整个过程都是分形的
项目越来越大的情况下,分模组是特别有必要的。
文件解构如下
store
└─ index.js
└─ moduleA.js
└─ moduleAInside.js
└─ moduleB.js
文件内代码
// index.js
import { createStore } from 'vuex'
import moduleA from './moduleA'
import moduleB from './moduleB'
export default createStore({
state: {
count: 0,
},
getters: {
},
mutations: {
addCount(state, payload){
state.count += payload.count;
}
},
actions: {
asynCount(context, products){
setTimeout(() => {
context.commit('addCount', products);
}, 1000);
}
},
modules: {
moduleA,
moduleB
}
})
// moduleA.js
import moduleAInside from './moduleAInside'
const moduleA = {
state: () => ({
moduleAstate: '未知'
}),
getters: { ... },
mutations: { ... },
actions: { ... },
modules: {
moduleAInside
}
}
export default moduleA;
// moduleAinside.js
const moduleAInside = {
state: () => ({
moduleAInsideState: '未知'
}),
getters: { ... },
mutations: { ... },
actions: { ... }
}
export default moduleAInside;
// moduleB.js
const moduleB = {
state: () => ({
moduleBstate: '未知'
}),
getters: { ... },
mutations: { ... },
actions: { ... }
}
export default moduleB;
模组内的用法和外部并无二样,同样模组内可继续嵌套模组。
关于模块内访问模块其他模块或者根模块。
- **getters: ** getters的第三、四个参数
rootState、rootGetters
可以访问根模块和其他模块 - **actions: ** actions的
context
包含rootState、rootGetters
,可用于访问其他模块与根模块。
高级
vue3推出了组合式api,在组合式api中可以使用 useStore
函数创建对 vuex
的引用,这等同于 this.$store
import { useStore } from 'vuex'
export default {
setup(){
const store = useStore();
}
}
访问state和getters
由于我访问的是comAPI模块内的状态,所以在访问state时需要先访问模块后才可访问具体的状态,getters则不用。
const comState = computed(() => store.state.comAPI.comAPIState);
const comGetters = computed(() => store.getters.comAPIGetters);
访问mutations和actions
访问mutations和actions不需要考虑模块作用域问题。
const changeState = () => { store.commit('changeState') }
const asynChangeState = () => { store.dispatch('asynChangeState') }
对比vuex4 & vuex3
创建方式
vuex4与vue3的创建方式保持一致
import { createStore } from 'vuex'
export const store = createStore({
...
})
安装到vue
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
const app = createApp(App)
app.use(store)
app.mount('#app')
新增组合式API useStore
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}