Vuex是什么?
Vuex是一个专为 Vue.js应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信。
要点:
- vue官方搭配,专属使用 (类似于:vue-router),有专门的调试工具
- 集中式管理数据状态方案 (操作更简洁)
data() { return { 数据, 状态 }}
- 数据变化是可预测的 (响应式)
使用Vuex的好处:
1、数据的存取一步到位,不需要层层传递
2、数据的流动非常清晰
3、存储在Vuex中的数据都是响应式的
那么我们先来思考一个问题:什么样的数据适合存储到Vuex中?
答案是:需要共享的数据
Vuex的作用就是:频繁、大范围的数据共享
vue官方提供的独立于组件体系之外的,管理公共数据的工具:
vuex 五个核心概念:
state: 统一定义公共数据(类似于data(){return {a:1, b:2,xxxxxx}})
mutations : 使用它来修改数据(类似于methods)
getters: 类似于computed(计算属性,对现有的状态进行计算得到新的数据)
actions: 发起异步请求
modules: 模块拆分
Vuex-state定义公共数据并在组件中使用:
概念:State 本质上就是 Object 对象
state的作用是:保存公共数据(多组件中共用的数据)
state是响应式的: 如果修改了数据,相应的在视图上的值也会变化。
项目结构:
需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cinema.js # cinema模块
└── tabbar.js # tabbar模块
store基本创建:
vue2对应关系:2 ===>3 ===> 3
vue版本:^2.6.14
vue-router:“^3.5.2”
vuex版本:3.6.2
vue3对应关系:
vue3 ===> vue-router4 ===> vuex4
npm 指定版本号下载
npm install vuex@3.4.0 --save 安装并写入package.json的dependencies中
npm install vuex@3.4.0 -save-dev 安装并写入package.json的devDependencies中
npm uninstall xxx 删除 xxx 模块
npm uninstall -g xxx 删除全局模块 xxx
1)在store/index.js 中放置具体的代码,具体如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 定义共享状态
},
mutations: {
// 定义同步修改状态的方法
},
actions: {
// 定义异步修改状态的方法
},
getters: {
// 定义计算派生状态的方法
},
modules: {
模块名1: {
// namespaced为true,则在使用mutations时,就必须要加上模块名
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
},
模块名2: {
// namespaced不写,默认为false,则在使用mutations时,不需要加模块名
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}
}
})
export default store
访问数据和修改数据的调整
访问模块中的数据,要加上模块名
获取数据项: {{$store.state.模块名.数据项名}}
获取getters: {{$store.getters['模块名/getters名']}}
访问模块中的mutations/actions:
如果namespaced为true,则需要额外去补充模块名
如果namespaced为false,则不需要额外补充模块名
$store.commit('mutations名') // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true
使用了modules之后,在访问数据时就要额外添加modules的名字了。
建议: 在使用modules时,建议都给加上namespaced!
(1) state使用 辅助函数:mapState
// 方法一(直接使用)
this.$store.state.变量名
// 方法二(引入,之后可视为计算属性使用)
// 基于 Vuex 提供的 mapState 辅助函数,可以方便的把 Store 中指定的数据,映射为当前组件的计算属性
// 使用之前需要引入mapState,方法: import { mapState } from 'vuex';
computed:{
...mapState({在当前组件中使用的变量名: state => state.vuex仓库中的对应变量名}),
...mapState({'在当前组件中使用的变量名':'vuex仓库中的变量名'}), //一次只能声明一个变量
...mapState({在当前组件中使用的变量名:state = state.vuex仓库中的对应变量名})
...mapState(['变量名1','变量名2']),
}
(2) mutations使用 辅助函数:mapMutations
特点:想要修改State 中的数据,只能调用Mutation 方法,它是Vuex中用来修改公共数据的唯一入口。
好处:能够确保修改来源的唯一性,方便调试和后期维护。
在定义时:它的第一个参数是state,第二个参数是载荷
在调用时:用 this.$store.commit('mutation名', 载荷) 来调用
// 方法一(直接使用)
this.$store.commit('mutation名', 实参);
// 方法二(引入,之后可视为methods方法使用)
// 基于 Vuex 提供的 mapMutations 辅助函数,可以方便的把 Store 中指定的方法,映射为当前组件的 methods
// 使用之前需要引入mapMutations,方法: import { mapMutations } from 'vuex';
methods:{
...mapMutations(['mutation名1','mutation名2']),
...mapMutations({'在当前组件中使用的名字': 'mutation名'}),
使用方法fun() {
this.mutation名1();
}
}
Vuex-mutaions拓展理解
问:为啥是$store.commit('mutations的名字')而不是$store.mutations的名字()?
答:Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler) 。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
问:数据不可以该在组件内部直接修改吗?
答:不能。虽然语法上不报错,也有响应式的特点。但是不推荐。特别是在严格模式下会报错。若将vue创建 store 的时候传入 strict: true, 开启严格模式,那么任何修改state的操作,只要不经过 mutation的函数,vue就会报错。
(3) getter使用 辅助函数:mapGetters
// 方法一(直接使用)
this.$store.getters.getter的名字
// 方法二(引入,之后可视为计算属性使用)
// 基于 mapGetters 辅助函数,可以把 store 中的 getter 映射为当前组件的计算属性
// 使用之前需要引入mapGetters,方法: import { mapGetters } from 'vuex';
computed: {
...mapGetters(['getterName1','getterName2']),
...mapGetters({aliasName1: 'getterName1',aliasName2: 'getterName2'})
...mapGetters({'在当前组件中使用的名字': 'getter的名字'})
...mapGetters({aliasName1: state => state.moduleName.stateName1,})
}
methods:{
使用方法fun() {
this.getterName1();
}
}
(4)actions使用 辅助函数:mapActions
- actions是vuex的一个配置项
- 作用:发异步请求获取数据,调用mutations来保存数据,将整个ajax操作封装到Vuex的内部
- 要点:
-
- action 内部可以发异步请求操作
- action是间接修改state的:是通过调用 mutation来修改state
定义格式:
new Vuex.store({
// 省略其他...
actions: {
// context对象会自动传入,它与store实例具有相同的方法和属性
action的名字: function(context, 载荷) {
// 1. 发异步请求, 请求数据
// 2. commit调用mutation来修改数据
// context.commit('mutation名', 载荷)
}
}
})
action一般用来发异步请求,数据回来之后,在去调用mutations来保存数据
// 方法一(直接使用)
this.$store.dispatch('actions的名字', 参数)
// 方法二(引入,之后可视为methods方法使用)
// 基于 Vuex 提供的 mapActions 辅助函数,可以方便的把 Store 中指定的 Action,映射为当前组件的 methods
// 使用之前需要引入mapActions,方法: import { mapActions } from 'vuex';
methods: {
...mapActions(['actions的名字']),
...mapActions({'在当前组件中使用的名字': 'actions的名字'}),
使用方法fun() {
this.actions的名字();
}
}
应用场景二(用modules来拆分复杂业务):
export default new Vuex.Store({
// state: 用来保存所有的公共数据
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
模块名1: {
// namespaced为true,则在使用mutations时,就必须要加上模块名
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
},
模块名2: {
// namespaced不写,默认为false,则在使用mutations时,不需要加模块名
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}
}
})
在组件中使用:
// state使用
this.$store.state.模块名.变量名
...mapState('模块名',['变量名1','变量名2'])
// mutations使用
this.$store.commit('模块名/mutation名', 实参);
...mapMutaions('模块名',['方法名'])
// getters使用
this.$store.dispatch.模块名.变量名
...mapGetters('模块名',['变量名1','变量名2'])
// actions使用
this.$store.mapActions('模块名/actions名', 实参);
...mapActions('模块名',['方法名'])
vuex模块化:
当我们开发的项目比较大时,store中的数据就可能比较多,这时我们store中的数据就可能变得臃肿,为了解决这一问题,我们就需要将store模块化(module),即每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块
注意:被模块化的store须要开启命名空间。
模块A:a.js
export default {
namespaced: true,//开启命名空间
state: {
sum:0,
number:0
},
mutations: {
ADD_NUM(state,value){
state.sum+=value
}
},
actions: {
},
}
模块B:b.js
export default {
namespaced: true,//开启命名空间
state: {
name:'张三',
personList:[]
},
mutations: {
ADD_PERSON(state,value){
state.personList.unShift(value)//把数据添加到数组的首位
}
},
actions: {
addZhang(context,value){
//value参数是一个对象
if(value.name.indexOf('张')===0){
context.commit('ADD_PERSON')
}else{
alert('这个人不姓张!')
}
},
addServer(context,value){
axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(res=>{
context.commit('ADD_PERSON',{name:res.data})
},error=>{
alert(error.message)
})
}
},
getters: {
firstName(state,getters){
return state.personList[0].name+',你好!'
}
}
}
store/index.js:在modules中引入
import Vue from 'vue'
import Vuex from 'vuex'
//引入
import moduleA from './a.js'
import moduleB from './b.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {//模块化
moduleA,
moduleB
}
})
这时我们在项目中如何取用呢?
- 访问state数据:
第一种方式:this.$store.state.moduleA.sum
第二种方式:import {mapState} from 'vuex' computed:{ ...mapState('moduleA',['sum','number']) }
- 修改state数据:
第一种方式:this.$store.commit('moduleA/ADD_NUM',10)
第二种方式:import {mapMutations} from 'vuex' methods:{ ...mapMutaions('moduleA',['ADD_NUM']), }
vuex3和vuex4异同点:
1) 创建 Store 的方式
- Vuex 3:使用
new Vuex.Store()
创建 store 实例
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vue)
const store = Vuex.Store({
...配置项
})
export default store
- Vuex 4:使用
createStore
创建 store 实例
import {createStore} from 'vuex'
const store = createStore({
...配置项
})
export default store
Vuex 4 中使用 createStore
函数来创建 store 实例,而不是直接在 Vue 实例上挂载
2) 在组件中使用 Store
- Vuex 3:使用
this.$store
访问 store 实例,通过this.$store.state
访问状态,通过this.$store.commit()
进行提交 mutation,通过this.$store.dispatch()
进行分发 action。
export default {
name:"#app",
computed:{
count(){
return this.$store.state.count
}
},
methods:{
increment(){
this.$store.commit('increment')
},
incrementAysnc(){
this.$store.dispatch('incrementAysnc')
}
}
}
- Vuex 4:使用
useStore
函数来获取 store 实例,通过store.state
访问状态,通过store.commit()
进行提交 mutation,通过store.dispatch()
进行分发 action
import {useStore} from 'vuex'
export default {
setup(){
const store = useStore()
const count = computed(()=>store.state.count)
const increment = () =>{
store.commit('increment ')
}
const incrementAsync = () =>{
store.dispatch('incrementAsync')
}
return {
count,
increment,
incrementAsync
}
}
}
虽然 Vuex4 推荐使用更符合 Composition API 风格的 useStore()
来获取 store
实例。但是并没有移除 this.$store
,但是在 <template>
和 Vue2
选项式写法中还是支持使用 $store
的。
3)辅助函数
- Vuex 3:使用
mapState
、mapGetters
、mapMutations
和mapActions
辅助函数来进行映射,简化在组件中对 store 的访问。
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex';
export default {
name: 'App',
computed:{
...mapState['count'],
...mapGetters['doublecount'],
},
methods:{
...mapMutations['increment'],
...mapActions['incrementAsync'],
}
}
- Vuex 4:使用 Composition API 中的
computed
函数和普通的 JavaScript 函数来实现类似的功能。
import {computed,useStore} from 'vuex'
export default {
steup(){
const store = useStore()
const count = computed(()=>store.state.count)
const doublecount = computed(()=>store.getters.doublecount)
const increment = () => {
store.commit('store ')
}
const incrementAsync = () => {
store.dispatch('incrementAsync')
}
return {
count,
doublecount,
increment,
incrementAsync
}
}
}
Vuex4 支持选项式写法的辅助函数,在使用时和 Vuex3 一模一样的。但是需要注意辅助函数不能在组合式写法 setup
中使用。
4)Vuex4 支持多例模式
Vuex 3 是单例模式的,即整个应用只能有一个全局的 Vuex Store 实例。而在 Vuex 4 中,你可以通过 useStore
函数在不同组件中创建多个独立的 Vuex Store 实例,从而支持多例模式。
以下是一个示例展示了如何在 Vuex 4 中使用 useStore
辅助函数创建多个独立的 Vuex Store 实例:
<template>
<div>
<p></p>
<p></p>
<button @click="incrementcount1">Increment count 1</button>
<button @click="incrementcount2">Increment count 2</button>
</div>
</template>
<script>
import {useStore} from 'vuex'
export default {
setup(){
//使用useStore 创建 Vuex store实例
const store1 = useStore('store1')
const store2 = useStore('store2')
//通过store1.state.count1 获取第一个store实例状态
count count1 = store1.state.count1
//通过store2.state.count2 获取第二个store实例状态
count count2 = store2.state.count2
//通过store1.commit 提交mutation第一个store状态
const increment1 = () => {store1.commit('increment1 ')}
//通过store2.commit 提交mutation第二个store状态
const increment2 = () => {store2.commit('increment2 ')}
return {
count1,
count2,
increment1,
increment2
}
}
}
</script>
上述示例展示了如何在 Vue 组件中使用 useStore
辅助函数创建多个独立的 Vuex Store 实例,并通过这些实例分别访问和修改各自的状态和 mutations。这是 Vuex 4 相对于 Vuex 3 的一个重要的改进,使得 Vuex 在支持多例模式的场景下更加灵活和可扩展。