状态管理
vuex
是一个专门为Vue.js应用程序开发的
状态管理模式
。
采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vue为这些被多个组件频繁使用的值提供了一个统一管理的工具——VueX。
在具有VueX的Vue项目中,我们只需要把这些值定义在VueX中,即可在整个Vue项目的组件中使用。
- 小型项目(不适用于vuex)
- 中小型项目(有store模式)
- 中大型项目(vuex 可以分模块)
状态管理模式
状态管理模式包含以下部分:
- state 驱动应用的数据源
- view 以声明的方式将state映射到视图
- actions 响应在view上的用户输入导致的状态变化
我们把组件的共享状态抽取出来,以一个全局单例模式管理
在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。
使用vuex的情况
Vuex 可以帮助管理共享状态。
中小型项目,不是特别庞大,使用vuex比较冗余,可以使用store模式。
需要构建一个中大型的单页应用
,可以使用vuex进行组件外部的状态管理。
简单的store模式
utils文件夹中新建store.js 做为临时仓库
export default{
debug:true,
state:{
msg:"is store",
name:'zhangsan'
},
actions:{
getname(){
console.log('zhangsan');
}
}
}
在组件中获取store仓库里的数据和方法
Home.vue
<template>
<!-- {{state.msg}} -->
<!-- <button @click="getname">点击</button> -->
</template>
<script>
import store from '../utils/store'
export default {
data(){
return{
state:store.state
}
},
methods:{
...store.actions
}
}
</script>
单一状态树
vuex使用单一状态树,一个对象就包含了全部的应用层级状态。
每一应用只包含一个store实例。
单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
vuex安装与使用
安装vuex
npm i vuex -s
初始化
store 文件夹下 新增文件 index.js
import { createStore } from 'vuex'
export default createStore({
state: {
msg:'is store',
},
mutations: {
},
actions: {
},
modules: {
}
})
将store挂载到当前项目的vue实例中
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
在组件中使用vuex
State
存放数据
state: {
msg:'is store',
...
},
组件中使用
Home组件
方法一:
<template>
{{$store.state.msg}} //一般不这样使用
</template>
方法二:计算属性方法
<template>
{{msg}}; //官方推荐使用方式
</template>
<script>
export default{
computed:{
msg(){
return this.$ store.state.msg; //官方推荐使用
}
}
}
</script>
方法三:辅助函数(项目中常用方法)
<template>
{{msg}};
</template>
<script>
import {{mapState}} from 'vuex' //辅助函数
export default{
computed:{
...mapState(['msg']),
},
}
</script>
getters
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。
就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
定义
state: {
books:['js01','js02','js03'],
},
getters:{
GET_BOOK_NUM:(state,getters)=>{
return state.books.length
}
},
使用
方法一:不推荐使用
<template>
{{$store.getters.GET_BOOK_NUM}}
</template>
方法二:计算属性方法
<template>
{{GET_BOOK_NUM}}
</template>
computed:{
GET_BOOK_NUM(){
return this.$store.getters.GET_BOOK_NUM
}
}
方法三:辅助函数(项目中常用方式)
<template>
{{GET_BOOK_NUM}}
</template>
import {mapGetters} from 'vuex'
computed:{
...mapGetters(['GET_BOOK_NUM'])
}
mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数.
- 无传参
定义
state: {
num:0
},
mutations: {
add(state){
state.num++;
}
},
使用
方法一:传统用法
<template>
{{num}}
<button @click="add">+</button>
</template>
methods:{
add(){
this.$store.commit('add')
}
}
方法二:辅助函数
<template>
{{num}}
<button @click="add">+</button>
</template>
import {mapMutations} from 'vuex' //辅助函数
methods:{
...mapMutations(['add'])
}
- 传参
定义
state: {
num:0
},
mutations: {
add(state,n){
state.num+=n;
}
},
使用
方法一:
<template>
{{num}}
<button @click="add(2)">+2</button>
</template>
add(){
this.$store.commit('add',2)
},
方法二:
<template>
{{num}}
<button @click="add(2)">+2</button>
</template>
//传递参数时,可以采用更易读的方式传递
store.commit({type: 'add',num: 2})
在 Vuex 中,mutation 都是同步事务
Action
类似于mutation,不同之处在于
- Action 提交的是mutation,而不是直接变更状态
- Action可以包含任意
异步操作
定义
actions: {
async_add(context,n){
setTimeout(() => {
context.commit('add',2) //这里我们可以使用仓库中的mutation中的方法并且可以是异步的!
}, 2000); //Action中使用异步事件
}
},
使用
方法一:传统用法
<template>
{{num}}
<button @click="async_add(2)">+2</button>
</template>
methods:{
async_add(n){
this.$store.dispatch('async_add',n)
} //传统写法
}
方法二:辅助函数
<template>
{{num}}
<button @click="async_add(2)">+2</button> //异步。两秒钟之后加2
</template>
import {mapActions} from 'vuex'
methods:{
...mapActions(['async_add']) //辅助函数
}
module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
在store文件夹中新建module子文件夹作为子模块,在module模块中新建home.js作为子仓库home.
命名空间
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
通过添加 namespaced: true 的方式使子模块成为带命名空间的模块。
当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
当分模块后,取值方式可以适当改变,以方便我们取值:
子仓库 store/module/home.js
export default{
namespaced:true,//开启命名空间
state(){
return{
name:'home'
}
}
}
主仓库 store/index.js
import { createStore } from 'vuex'
import home from './modules/home' //将子仓库引入总仓库
export default createStore({
state: {
name:app,
msg:'is store',
num:0
},
})
组件中使用 Home.vue
<template>
{{msg}}
{{num}}
{{name}}
</template>
<script>
import {mapState} from 'vuex'
export default{
computed:{
...mapState('home',['name']), //home仓库里的name,显示为home
...mapState(['name']);默认为主仓库的name值,为app ;下面的会 覆盖上面的,此时显示在页面中的name值为app
...mapState(['msg','num']), //主要主仓库中有msg和num,直接显示
...mapState({
home_name: (state)=>{state.home.name}, //读取的是home仓库中的name值,为home
name:(state)=>{state.name} //读取的是主仓库中的name值,为app
//将不同仓库里的name进行重命名,使name名不同
}),
}