Vuex用法
前言:
vuex, 是vue 的一个插件,作用是 对vue应用中多个组件的共享状态进行集中式的管理(get/set)
用着用着 感觉回到了学Java的时候,写的 entity class,里面一堆的 getter setter
为解决多组件共享状态问题而生:
- 多个视图依赖于同一个状态
- 来自不同视图的行为需要变更同一个状态
以前的解决方法:
- 将数据以及操作数据的行为 全部集中定义在父组件中
- 将数据以及操作数据的行为传递给需要的子组件,(可能要逐层传递)
根据vue cli 3.0 搞法 在src 目录下新建 store文件夹
store文件夹下面 新建 index.js 文件
-
导入前置
import Vue from 'vue'
-
导入 vuex
import Vuex from 'vuex'
-
声明使用 vuex
Vue.user(Vuex)
-
实例化对象
const store = new Vuex.Store()
-
模块化编程的时候记得向外暴露对象
export default store
-
main.js 中引入
import store from './store/index.js'
-
在vue 实例中使用
new Vue({ store, //这里 render: h => h(App), /* * h 是方法, 是箭头匿名函数的 第一且唯一参数, 函数体返回 一个函数调用的结果 * h 的解释是 Hypescript 是一个生成 HTML结构 的script 脚本 , * * 体现在 vue 1.0 中为 * components: { * App * } * */ }).$mount('#app');
Vuex 中的各大模块
属性名 | 作用 |
---|---|
state | 用于数据的存储,是store中的唯一数据源 |
getters | 如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算 |
mutations | 类似函数,改变state数据的唯一途径,且不能用于处理异步事件 |
actions | 类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作 |
modules | 类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护 |
- 标准用法(简写)
export default new Vuex.Store({
state: {
// 任何地方都能 this.$store.state.xxx 了
},
getters: {
/* 不过get 提供了更好的获取方法,由于直接获取不能做一些 state 的操作,
而在 getters 中你可以写自己想要的 getter 逻辑
命名规则一般以 get 开头
*/
},
mutations:{
/*
一般是写 set 函数,改变state数据的唯一途径,不能写异步
命名规则一般以 set 开头
*/
},
actions:{
/*
用来提交 mutations ,在其中可以写与后台交互的 ajax 异步代码,
*/
}
modules:{
// 项目大的时候需要很多个状态,那么拆开作为各个模块来使用 状态管理
}
})
Vuex 结构图的理解
流程的解释:
初始化开始(初始化阶段)
vue components 向 vuex 中的 state 建立依赖( get )
还记得嘛?
computed
属性在 组件实例的初始化阶段就会默认的get 一次所以这个过程也是自动 get 一下vuex 中的依赖
更新开始(更新阶段)
在组件中的依赖的数据要发生改变时,通过
dispatch ()
分发
dispatch()
找到对应actions
中的方法名
actions
通过commit()
去往对应的mutations
mutations
直接更新state
中的数据(set)
了解完了几大组成,现在来说说 backend
和 devlools
backend:
适用于在actions 中写 ajax 代码与后端通信
devlools:
在 chrome 的 开发工具中,提供了监视 mutations 的一个视图,开发人员可以查看
Vuex 使用示例
export default new Vuex.Store({
state: {
count:1,
allStatus:false
},
getters: {
getCount(state){ //命名规范
return state.count; // 注意这里不能用this 此时的 this是 undefined,估计是使用了严格模式
},
getAllStatus(state){
return state.allStatus // 错误示例 :return this.state.allStatus
}
},
mutations:{
setCount(state,num){
state.count = num // 写其它操作
}
},
... // 此处展示最简单的用法
})
- 其它组件实例中可以这样获取
this.$store.state.count // 属于直接获取,无监听 this 代表当前所在的组件
this.$store.getters.getCount // 不用加() ,因为它默认是计算属性,计算属性对应的默认方法就是get
在使用了 vuex 的状态管理后,组件实例上就多了一个
$store
,方便组件的实例去获取依赖
- 其它地方可以这样设置
this.$store.commit('setCount',1) // 第一个参数为所去方法,第二个为 携带参数
actions 提交 mutations
// mutations 中定义的一系列方法
mutations: {
...
,
openAllstatus(state){
state.allStatus = true
},
closeAllstatus(state){
state.Allstatus = false
}
}
// actions 中对应的写上 提交mutations
actions: {
todoOpen(context){
context.commit('openAllstatus')
},
todoClose(context){
context.commit('closeAllstatus')
}
}
// 你还可能见到的写法为:
actions: {
todoOpen({commit}){
commit('openAllstatus')
},
todoClose({commit}){
commit('closeAllstatus')
}
}
/* 在进入函数后,参数接收到值,用{} ES6语法来将 context 解构,拿到其中的commit 对象*/
最显而易见的方式就是在 组件中定义 一个 计算属性 get 一下 对应的 state 中的值 来查看 提交的更新
computed: {
allStatus(){
return this.$store.getters.getAllstatus
// chrome vue development 中选择对应的组件可以看到,确确实实更新了
}
}
现在试着在任意组件中合法的位置 通过action 提交 mutation
mounted: {
this.$store.dispatch('todoOpen'); // 调试查看 计算属性中的值,已经发生改变 为 true
}
使用vuex中的 map映射
将代码优雅化
import {mapState,mapGetters,mapActions} from 'vuex' //解构赋值,名字不能瞎取
我们之前是这样写的:
this.$store.state.count // 属于直接获取,无监听 this 代表当前所在的组件
this.$store.getters.getCount // 不用加() ,因为它默认是计算属性,计算属性对应的默认方法就是get
改变下写法用新的API,两条代码分别相等,对应
...mapState(['count']) //相当于是state的语法糖,记得放在 computed里面,标识它是一个计算属性
...mapGetters(['getCount']) //相当于是getters的语法糖,记得放在 computed里面,标识它是一个计算属性
/*
三个点是 ES6 的拓展运算符,了解一下,在此处用作`对象展开`
我不反对你把 mapState,mapGetters放在 data 中(普通属性)
但它应该有自己的样子,请将mapState,mapGetters 移到 computed中(计算属性)
在 vuex 状态管理中找到对应的组件,可以看到值出现在 vuex bingdings里面
*/
mapState的返回值:
{
count(){
return this.$store.state['count'] //去 store 中的 state数据源找到 count 属性
}
}
mapGetters的返回值:
{
getCount(){
return this.$store.getters['getCount'] // 去getters 中的 getCount 方法
}
}
比如需要获取 count
,或者是其他值的时候
...mapState(['count','value1','value2'])
对象展开生成后:
{
count(){
return this.$store.state['count'] //去 store 中的 state数据源找到 count 属性
},
value1(){
return this.$store.state['value1']
},
value2(){
return this.$store.state['value2']
}
}
之前用来 分发提交 actions 的动作就变成:
this.$store.dispatch('todoSetxxx')
/* 变成下面这样 */
/* v v v v v */
...mapActions(['todoSetxxx']) // 放在 methods 里面
组件的实例中操作 actions
this.todoSetxxx()
this.todoSetxxx(data) //可携带参数,请与actions中相对应
dispatch 提交多参数时参数接收的解决
有时候需要传递多个参数给
actions
,然后再 提交mutations
来改变state
中的状态
像下面这样:
this.$store.dispatch('actionName',params1,params2)
//或者是 使用了 ...mapActions
...mapActions(['actionName']) //定义在了 methods 里
this.actionName(params1,params2) //与第一行相等
在
actions
中输出时你会发现,params2 的值为undefined
也就是说它的默认接参只支持两个,第一个为dispatch
的提交,第二个为用户指定参数
,mutations
中的方法也只接受两个参数
解决方法如下:(使用解构赋值)
传多个参数时:
this.$store.dispatch('actionName',{params1,params2})
actions
中解构出来
actionName({commit},{params1,params2}){
commit('mutationName',{params1,params2}) //传递的时候也要包装起来
}
对应的 mutations
中的方法,接受参数时也得 解构出来
mutationName(state,{params1,params2}){
}
需要注意的是使用解构赋值的时候,从头至尾的参数名称要一 一对应,不然解构不出来
Modules 将状态模块划分
一旦项目大起来,一个store > index.js 中可能塞满了各种各样的 state,维护将变得困难,modules
提供了管理办法 。
1. 建立如下方式的目录:
此时 index.js作为一个入口,在入口中引入拆分的状态,index.js中只留
modules
,mutation-types 是常量字符串映射值,可有可无。
2. 配置单个状态如下:
const state = {
// 定义状态
}
const getters = {
// get State
}
const mutations = {
// set State
}
const actions = {
// 提交 mutations
}
export default { //向外导出
namespaced: true, // 开启命名空间,将modules 里面配置的对象指定一个名字方便管理
state,
getters,
actions,
mutations
}
3. 在组件内部使用状态
这个时候因为状态被拆分了,且具有自己的名称,组件在创建后,vue 给组件加入了 $store
,也可使用引入mapState
,mapGetters
,mapMutations
,mapActions
来使用,但是得加上模块名称
假设我在 userInfo 状态模块里面管理了一个状态
const state = {
user:{
username:""
}
}
const getters = {
getUserName(state){
return state.user.username
}
}
const mutations = {
setUserName(state,val){
state.user.username = val
}
}
const actions = {
changeUserName(context,val){
context.commit('setUserName',val)
}
}
使用:state , mapState
this.$store.state['userInfo'].user.username
...mapState('userInfo',['user'])
this.user.username
使用:getters , mapGetters
this.$stroe.getters['userInfo'].getUserName
...mapGetters('userInfo',['getUserName'])
this.getUserName
使用:commit
this.$store.commit('userInfo/setUserName')
使用:dispatch,mapActions
this.$store.dispatch('userInfo/changeUserName')
...mapActions('userInfo',['changeUserName'])
4. 在组件外部使用
vue 不提供 $store
的注入,所以先引入 store
import store from '../store' // 默认下面如果是 index.js,那么可以省略不写
且无法使用 mapXXX
,那么使用原始的操作也一样
store.dispatch('userInfo/changeUserName') // 举一个例子,其它的语法照着写就是