Vuex
概述
1、是什么?
vuex是 一个vue提供的 状态管理工具插件,状态就是数据。用来管理vue通用的数据(多组件共享的数据)。
2、场景
某个状态在很多个组件中使用(例如:个人信息)多个组件 共同维护一份数据(例如:购物车)
3、优势
- 共同维护一份数据,
数据集中化管理 响应式变化- 操作简洁(
vuex提供了一些辅助函数)
创建仓库
目标:安装vuex插件,初始化一个空仓库

1、安装
npm install vuex@3
或者
yarn add vuex@3
2、新建 store/index.js 专门存放vuex
3、创建仓库
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters
})
export default store
4、在 main.js 中导入挂载到 vue实例上。
import store from './store'
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
通过
this.$store访问

核心
1、state 状态
目标:明确如何给仓库提供数据,如何使用仓库的数据
- 提供数据
State提供唯一的公共数据源,所有共享的数据都要统一放到 Store中的state中存储。
在state对象中可以添加我们要共享的数据
// 创建仓库
const store = new Vuex.Strore({
strict: true, // 通过 strict 可以开启严格模式
// state 状态,即数据,类似于 vue 组件中的 data
// 区别
// 1、data 是组件自己的数据
// 2、state 是所有组件共享的数据
state: {
title: '仓库大标题'
count: 1
}
})
- 使用数据
获取store
(1)`this.$store`
(2) `import` 导入 `store`
模板中使用: {{ $store.state.xxx }}
组件逻辑中: this.$store.state.xxx
js模块中: store.state.xxx
-
同构辅助函数(简化代码)
mapState是辅助函数,帮助我们把store中的数据自动映射到 组件的计算属性中
(1)导入 `mapState` import { mapState } from 'vuex' (2)数组方式引入`state` mapState(['count']) (3)展开运算符映射 computed: { ...mapState(['count']) }

2、mutations
目标:明确 vuex 同样遵循单向数据流,组件中不能直接修改仓库的数据。
掌握 mutations 的操作流程,来修改 state 数据。
state数据的修改只能通过mutations,mutations必须是同步的,便于监测数据变化,记录调试
(1) 定义 `mutations` 对象,对象中存放修改 `state` 的方法
const store = new Vuex.Store({
state: {
count: 0
},
// 定义mutations
mutations: {
// 第一个参数是当前 store 的 state 属性
addCount (state) {
state.count += 1
}
}
})
(2)组件中提交调用 `mutations`
this.$store.commit('addCount')
- 提供
mutations函数, 带参数
mutations: {
// 参数n 的位置 只能有一个,如果需要传多个参数,将参数包装成对象的形式传递
addCount (state, n) {
state.count += n;
}
}
- 页面中提交调用
mutations
this.$store.commit('addCount', 10)
- 辅助函数
mapMutations
说明:把位于 mutations中的方法提取出来,映射到组件methods中
mutations: {
subCount (state, n) {
state.count -= n;
}
}
// 使用 mapMutations
import { mapMutations } from 'vuex';
methods: {
...mapMutations(['subCount'])
}
// 不使用 mapMutations,就需要通过 commit触发
methods: {
subCount(n) {
this.$store.commit('subCount', n);
}
}
// 调用
this.subCount(10)
3、actions
目标:明确 actions 的基本语法,处理异步操作。
需求:一秒后,修改 state 的 count 为 666
mutations: {
changeCount(state, newCount) {
state.count = newCount;
}
},
// 提供 actions 方法
actions: {
setAsyncCount (context, num) {
// 一秒后,给一个数, 去修改 num
setTimeout(() => {
context.commit('changeCount', num)
}, 1000)
}
}
// 页面中 通过 dispatch 调用
this.$store.dispatch('setAsyncCount', 666)
- 辅助函数
mapActions
说明:mapActions 是把位于 actions中的方法 提取出来,映射到 组件methods 中
actions: {
setAsyncCount (context, num) {
// 一秒后,给一个数, 去修改 num
setTimeout(() => {
context.commit('changeCount', num)
}, 1000)
}
}
// 使用 mapActions
import { mapActions } from 'vuex'
methods: {
...mapActions(['setAsyncCount'])
}
// 不使用 mapActions,需要通过 dispatch 触发
methods: {
setAsyncCount(n) {
this.$store.dispatch('setAsyncCount', n)
}
}
// 调用
this.setAsyncCount(666)
getters
说明:除了state之外,有时我们需要从state中派生出一些状态,这些状态是依赖 state 的,此时会用到getters。(类似于计算属性)
例如:state 中定义了 list, 为 1-10 的数组,组件中,需要显示所有大于5的数据
state: {
list: [1,2,3,4,5,6,7,8,9,10]
},
// 1、定义getters
getters: {
// 注意:
// 1、getters 函数的第一个参数是 state
// 2、getters 函数必须有返回值
filterList (state) {
return state.list.filter(item => item > 5)
}
}
// 2、访问 getters
(1)通过 store 访问 getters
{{ $store.getters.filterList }}
(2)通过辅助函数 mapGetters 映射
computed: {
...mapGetters(['filterList'])
}
// 3、使用
{{ filterList }}
模块 module(进阶语法)
由于 vuex 使用 单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。(当项目变得越来越大的时候,vuex会变得越来越难以维护)。

- 模块拆分
const state = {
userInfo: {
name: '张三',
age: 18
}
}
// 模块拆分
// user 模块: store/modules/user.js
export default {
namespaced: true, // 开启命名空间
state,
mutations,
actions,
getters
}
// 导入模块
import user from './modules/user'
const store = new Vuex.Store({
modules: {
user
}
})
尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名。需要开启命名空间,才会挂载到子模块。
-
使用模块中的数据
-
直接通过模块名访问
$store.state.模块名.xxx -
通过
mapState映射- 默认根级别的映射
mapState(['xxx']) - 子模块的映射
mapState('模块名', ['xxx'])
注意: 子模块的映射 需要开启命名空间
namespaced: true - 默认根级别的映射
-
-
使用模块中
getters中的数据-
直接通过模块名访问
$store.getters['模块名/xxx'] -
通过
mapGetters映射- 默认根级别的映射
mapGetters(['xxx']) - 子模块的映射
mapGetters('模块名', ['xxx'])
注意: 子模块的映射 需要开启命名空间
namespaced: true - 默认根级别的映射
-
-
调用子模块中的
mutations-
直接通过
store调用$store.commit('模块名/xxx', 额外参数) -
通过
mapMutations映射- 默认根级别的映射
mapMutations(['xxx']) - 子模块的映射
mapMutations('模块名', ['xxx'])
注意: 子模块的映射 需要开启命名空间
namespaced: true - 默认根级别的映射
-
-
调用子模块中的
actions-
直接通过
store调用$store.dispatch('模块名/xxx', 额外参数) -
通过
mapActions映射- 默认根级别的映射
mapActions(['xxx']) - 子模块的映射
mapActions('模块名', ['xxx'])
注意: 子模块的映射 需要开启命名空间
namespaced: true - 默认根级别的映射
-
Pinia
什么是Pinia?
Pinia是vue的最新状态管理工具,是 vuex 的替代品。
- 提供更加简单的
API(去掉了mutations),action既支持同步,也支持异步 - 提供符合,组合式风格的
API(和 vue3 新语法统一) - 去掉了
modules的概念,每一个store都是一个独立的模块 - 配合
typescript更加友好,提供可靠的类型推断
创建引用
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import './assets/main.css';
// 1、导入createPinia
import { createPinia } from 'pinia';
// 2、执行方法得到实例
const pinia = createPinia();
// 3、把pinia实例加入到app应用中
createApp(App).use(pinia).mount('#app');
基础使用
Store
1、定义store
通过defineStore定义Store,它要求第一个参数是独一无二的名字,这个名字也被用作id,是必须传入的。defineStore的第二个参数可以接受两种类值:setup函数和Option对象
// stores/counter.js
// 定义 store (state + action)
import { defineStore } from 'pinia'
import { ref } from 'vue'
// 组合式写法
export const userCounterStore = difineStore('counter', () => {
// 数据(state)
const count = ref(0)
// 修改数据的方法 (action 支持同步+异步)
const increment = () => {
count.value++
}
// 以对象形式返回
return { count, increment }
})
2、组件中使用 Store
<script setup>
// 1、导入 userCounterStore 方法
import { userCounterStore } from '@/stores/counter'
// 2、执行方法 等到 counterStore 对象
const counterStore = useCounterStore()
</script>
<template>
<button @click="counterStore.increment">{{ counterStore.count }}</button>
</template>
getters实现
pinia中的getters直接使用computed函数 进行模拟。
// stores/counter.js
// 定义 store (state + action)
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const userCounterStore = difineStore('counter', () => {
// 数据(state)
const count = ref(0)
// 修改数据的方法 (action 支持同步+异步)
const increment = () => {
count.value++
}
// getter定义
const doubleCount = computed(() => count.value * 2)
// 以对象形式返回
return { count, increment, doubleCount }
})
action如何实现异步
action中实现异步和组件中定义数据和方法的风格完全一致。
// stores/counter.js
// 定义 store (state + action)
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import axios from 'axios';
export const userCounterStore = difineStore('counter', () => {
// 数据(state)
const count = ref(0)
// 修改数据的方法 (action 支持同步+异步)
const increment = () => {
count.value++
}
// getter定义
const doubleCount = computed(() => count.value * 2)
// 定义异步action
const list = ref([]);
const getList = async () => {
const res = await axios.get('请求api');
list.value = res.data.data;
}
// 以对象形式返回
return { count, increment, doubleCount, list, getList }
})
storeToRefs工具函数
使用storeToRefs函数可以辅助保持数据(state + getter)的响应式结构
// 响应式丢失,视图不再更新
const { count, doubleCount } = counterStore
// 保持数据响应式
import { storeToRefs } from 'pinia';
// 数据相关处理(保持响应式更新)
const { count, doubleCount } = storeToRefs(counterStore)
// 方法直接从原来的 counterStore 中解构赋值
const { increment } = counterStore;
$subscribe:订阅
/**** userStore.vue文件 ******/
import { defineStore } from "pinia";
export const useUserStore = defineStore('user', {
// 存储数据
state() {
return {
userName: '张三',
enName: 'zhangsan',
age: 18
}
},
// 方法: 用于响应组件中的 动作
actions: {
increment() {
// 修改数据 (this 是当前store)
this.age += 1;
}
},
getters: {
bigAge: state => state.age * 10,
upperName():string {
return this.enName.toUpperCase()
}
}
})
/**** index.vue文件 ******/
<template>
<div>首页</div>
全局用户信息:<br />
姓名:{{ userStore.userName }}, 年龄:{{ age }}, 放大十倍: {{ userStore.bigAge }}
英文:{{ userStore.upperName }}
<br />
<button @click="change">修改store</button>
<button @click="change1">修改store —— storeToRefs</button>
</template>
<script setup lang="ts" name="home">
import { useUserStore } from '@/store/user';
import { storeToRefs } from 'pinia'
// 读取 store 数据
const userStore = useUserStore();
// storeToRefs 只会关注store中的数据,不会对方法进行 ref包裹
let { age } = storeToRefs(userStore);
//
userStore.$subscribe((mutate, state) => {
console.log('userStore里面保存的数据发生变化了',mutate, state)
})
const change1 = () => {
age.value += 1
}
const change = () => {
// 第一种修改 store 的方法
// userStore.age += 1;
// 第二种修改store 的方法
// userStore.$patch({
// age: 20,
// userName: '李四'
// })
// 第三种 修改store 的方式: action
userStore.increment();
}
</script>
Pinia持久化插件
- 安装插件
pinia-plugin-persistedstate
npm i pinia-plugin-persistedstate
main.js使用
import persist from 'pinia-plugin-persistedstate'
app.use(createPinia().use(persist))
store仓库中,persist: true开启