likes
comments
collection
share

vue3全家桶 之 状态管理Vuex4的使用

作者站长头像
站长
· 阅读数 91

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)

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 不能直接改变 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

vue3全家桶 之 状态管理Vuex4的使用

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,
  },
});

本文github代码地址

vue3相关

转载自:https://juejin.cn/post/7036635003584774175
评论
请登录