认识vuex

vuex是一个vue状态管理工具(状态就是数据)
vuex 是一个插件,可以帮我们管理 vue 通用的数据(多组件共享的数据)
vuex是单一组件树,只有一个store,但是可以分模块
状态管理:在开发中要处理各种各样的数据,这些数据需要保存在一个位置,对这些数据的管理就称为状态管理
之前在组件中进行数据管理,但是实际开发中项目很复杂,数据变多,还那样做项目会变得臃肿
vuex的核心是store仓库,本质上是一个容器,包含应用中大部分的状态state

  • 理解:
    比如一个购物车案例,你买了两个西瓜,那么这个2个西瓜和对应价钱这些数据不光会用在西瓜这个物品展示的组件上,还会用到结算金额的组件上,多组件共享数据的情景使用vuex可以帮我们管理
  • 使用场景:
    某个状态在很多个组件来使用(个人信息)
    多个组件共同维护一份数据(购物车)
    关键词:通用,多组件共享
  • vuex优势:
    共同维护一份数据,数据集中化管理
    响应式变化(任何一个组件修改数据,所有用到这个数据的地方也都会对应修改)
    操作简洁(vuex提供了一些辅助函数)

创建一个空仓库:

理解:既然要多组件共享数据,那么肯定要有个地方存放这些数据。安装vuex插件,初始化一个空仓库,将来的组件想要用数据在这里拿即可
步骤:安装 vuex;新建vuex模块文件(创建文件夹store,在下面创建index.js文件定义vuex,在main.js中挂载使用);创建仓库(new Vuex.Store());main.js导入挂载(在main.js中导入挂载到vue实例上)

安装vuex,创建空仓库

装包(后续如果在创建项目的时候把vuex勾选上那么其实不需要装包的,会自动帮你搭好):在终端输入npm add vuex@3 –force ,如果报错npm add vuex@3 –legacy-peer-deps(他人经验有备无患)
我用的第一个命令强制安装了,但是弹出了很多warn
要验证是否成功安装了vuex,在任意组件中使用一下,在组件中访问仓库,控制台打印一下 this.&store,如果成功那么能看到这个store,如果没成功那么现实undefind

index.js代码

1
2
3
4
5
6
7
8
9
//这里面存放的就是 vuex 相关的核心代码
import Vue from 'vue'
import Vuex from 'vuex'
// 插件安装
Vue.use(Vuex)
//创建仓库
const store = new Vuex.Store()
//导出给main.js使用
export default store

main.js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/index' //新增,导入挂载

Vue.config.productionTip = false

new Vue({
router,
store, //新增
render: h => h(App)
}).$mount('#app')

核心概念-state状态

理解:已经有了空仓库,那么要给仓库提供数据,还要能使用仓库的数据
state:状态即数据(类似vue组件中的data)
data是组件自己的数据
state是所有组件共享的数据

  1. 提供数据:
    state提供唯一的公共数据源,所有共享的数据都要统一放到store中的state中存储,在stste对象中可以添加要共享的数据
    const store=new Vuex.Store({
    store:{
    //要添加的数据写在这里
    数据名1:’早上好’,
    数据名2:123
    }
    })

  2. 获取使用数据
    方法1:
    通过store直接访问找到仓库,然后再找到仓库的state,再拿到数据
    先获取store两种方法(1)this.$store(2)import导入store
    在模板中
    在组件逻辑中:this.$store.state.数据名
    在js模块中:store.sttate.数据名
    方法2:
    通过辅助函数简化代码,mapState辅助函数帮助我们把store中的数据自动映射到组件的计算属性中,mapState辅助函数会自动生成数据映射
    步骤:
    (1)导入mapState,
    在.vue中引入import{mapState}from ‘vuex’
    (2)用数组方式引入state,mapState([‘数据名’])多个之间用逗号分开
    (3)展开运算符映射,computed:{…mapState([‘数据名’])}多个之间用逗号分开
    这样做以后.vue中使用数据不需要再,直接使用即可

核心概念-mutations

理解:vuex同样遵循单项数据流,意味着组件中不能能直接修改仓库的数据,如果任意组件都能修改仓库的数据,那么很难维护,所以要修改数据需要用到mutations
在store声明中增加代码strict:true开启严格模式,这样在组件中修改仓库数据会及时报错,但是上线后要关闭这行代码,因为监测消耗运行成本

通过mutations修改数据

理解:本质上是组件想要修改数据,仓库提供一个修改数据的党法,组件把修改请求提交给仓库,由仓库来进行修改操作
使用步骤:

  1. 在store声明的地方定义mutations对象,在对象中存放修改state的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    mutatios:{
    state:{
    count:0
    },
    mutations:{
    addCount(state){
    state.count +=1
    }
    }}
  2. 在组件中提交调用mytations
    在组件中的相关函数中
    this.$store.commit(‘addCount’)

mutation函数传参语法

  1. 在mutations中对应函数提供参数
    mutations:{
    addCount(state,n){
    state.count +=n
    }
    }
  2. 在页面中提交调用mutation
    this.$store.commit(‘addCount’,10)

辅助函数-mapMutations(简化方法代码)

mapMutations和mapState很像,它是把位于mutations中的方法提取了出来,映射到methods中
比如有函数

1
2
3
4
5
mutations:{
addCount(state,n){
state.count +=n
}
}

你要在组件中用,可以

1
2
3
4
5
6
7
8
9
10
import{mapMutations} from 'vuex'
methods:{
...mapMutations(['subCount'])
}
等价于
methods:{
sunCount(n){
this.$store.commit('addCount',n)
}
}

调用的时候需要this.subCount(100)

核心概念-actions

理解:处理异步操作,如一秒钟之后修改…
区别:mutations必须是同步的(便于检测数据变化,记录调试),而actions是处理异步操作的
比如下面的代码,context代表上下文,也可以理解为仓库

  1. 提供action方法
    1
    2
    3
    4
    5
    6
    7
    actions:{
    setAsyncCount(context,num){
    //一秒后,给一个数,去修改 num
    setTimeout(()=>{
    context.commit('changeCount',num)},1000)
    }
    }
  2. 页面中dispatch调用
    this.$store.dispatch('changeCount',200)
    注意,改数据都是mutations,但如果是异步,就在外面套一层dispatch

辅助函数-mapActions

mapActions 是把位于 actions中的方法提取了出来,映射到组件methods中,化简methods
类似前面的辅助函数,在vue中只需要:

1
2
3
4
import {mapActions }from'vuex
methods:f
...mapActions(['changecountAction'])
this.changecountAction(666)//进行调用

核心概念-getters

类似组件中的计算属性,除了state之外还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters
理解:比如state中定义了一个list为1-20的数组,但是组件中要展示大于10的数据,那么此时纪要定义一个getters来找到大于10的数据
state:{list:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]}

  1. 定义getters
    注意:getters函数的第一个参数(形参)是state,而且必须要有返回值
    1
    2
    3
    4
    5
    getter:{
    filterList(state){
    return state.list.filter(item=>tiem>5)
    }
    }
  2. 使用:访问getters
    (1)通过store访问getters
    {{ $store.getters.filterList }}
    (2)通过辅助函数mapGetters映射
    1
    2
    3
    4
    computed:{
    ...mapGetters(['filterList'])
    }
    {{filterList}}

核心概念-模块module

由于 vuex 使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时
store 对象就有可能变得相当臃肿。(当项目变得越来越大的时候,Vuex会变得越来越难以维护)
理解:
vuex中和数据相关的所有东西都放在一起,那么当项目变大后和js文件的东西会变得越来越大难以维护,那么希望能够把这些东西才拆分成一个个小的模块,每一个小的模块里面装各自的state,mtation,action,getter

模块拆分:

在store文件夹下面新建一个modules文件夹,里面放xxx1.js,xxx2.js...(一个模块一个xxx.js)每一个js文件就是一个模块
在每个.js文件中写各自的内容,在最后进行导出
如user.js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const state ={
userInfo:{
name:'zs‘
age: 18
}
}
const mutations={}
const actions={}
const getters={}
export default{
state,
mutations,
actions,
getters
}

使用的时候在index.js中导入挂载代码

1
2
3
4
5
6
7
import user from './modules/user'
const store = new Vuex.store({
//在这里面最后面加下面的代码
modules:{
user
}
})

导出导入后,要验证是否配置成功,在调试工具看Root下面是否有子模块user

子模块中的state访问

尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的 state 中,属性名就是模块名。本质还是单一模块树,但是拆分子模块后可维护性高了很多
方法1;直接通过模块名访问
$store.state.模块名.属性名
方法2:通过mapState映射
默认根级别的映射mapState(['属性名'])
子模块的映射mapState('模块名',['属性名'])
注意,需要开启命名空间:在子模块导出的地方第一行加上namespaced:true,可以理解为给一个一个模块起了不同的名字划分了不同空间

子模块中的getters访问

方法1;直接通过模块名访问
$store.getters.['模块名/属性名']
方法2:通过mapGetters映射
默认根级别的映射mapGetters(['属性名'])
子模块的映射mapGetters('模块名',['属性名'])
注意,需要开启命名空间:在子模块导出的地方第一行加上namespaced:true,可以理解为给一个一个模块起了不同的名字划分了不同空间

子模块中的mutation的调用和语法

默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块
调用子模块中的mutation:
方法1;直接通过store访问
$store.commit.['模块名/属性名',额外参数]
方法2:通过mapMutations映射
默认根级别的映射mapMutations(['属性名'])
子模块的映射mapMutations('模块名',['属性名'])
注意,需要开启命名空间

子模块中的action的调用和语法

默认模块中的mutation和actions会被挂载到全局,需要开启命名空间,才会挂载到子模块
调用子模块中的action:
方法1;直接通过stores访问
$store.commit.['模块名/属性名',额外参数]
方法2:通过mapActions映射
默认根级别的映射mapActions(['属性名'])
子模块的映射mapActions('模块名',['属性名'])
注意,需要开启命名空间