vuex的安装和使用,vuex的核心概念state,mutations,actions,getters和modules,以及相关的辅助函数mapState,mapMutations等

一.基础内容

1.vuex概述

Vuex是vue的状态管理工具,状态就是数据
通俗来说,Vuex是一个插件,帮我们管理vue通用的数据(多组件共享的数据)
vuex的优势:

  • 共同维护一份数据,集中化管理
  • 响应式变化
  • 操作简洁,vuex的辅助函数立大功
    在这里插入图片描述
2.构建vuex环境并创建一个空仓库
  • 创建一个脚手架项目:
vue create vuex-demo
勾选:css,linter,2.x,
components下新建两个组件:son1和son2

要求:App.vue,Son1.vue和Son2.vue三个组件共享一份数据,任一组件都能修改并同步数据
在这里插入图片描述

  • 创建一个空仓库
    仓库:就是通用数据的存放位置
    需要安装vuex插件后,初始化空插件
vuex安装>>yarn add vuex@3
新建vuex模块文件>>新建store/index.js
创建仓库>>
    import Vue from "vue"
    import Vuex from "vuex"
    Vue.use(Vuex)
    //创建仓库    
    const store=new Vuex.Store()
    //导出
    export default store;
在main.js导入挂载:import store from "@/store/index.js"

二.Vuex的核心概念:state,mutations,actions,getters

1.state状态:给仓库提供数据并使用仓库中的数据

state提供唯一公共数据源,所有的共享数据都放在Store中的state里面存储

  • 提供数据:在state对象中添加共享数据
const store = new Vuex.Store({
    state:{
        count:1001
    }
})
  • 使用数据
//方法一:通过store直接访问
在模板中:{{$store.state.count}}
在组件逻辑中:this.$store.state.count
在JS模块中:store.state.count<===前提:import导入

//方法二:通过辅助函数mapState
//App.vue
step1:import {mapState} from 'vuex'
step2:mapState(['count'])
step3:
computed:{
    ...mapState(['count','title'])
}

//等价于:
computed:{
   count(){return this.$store.state.count},
   title(){return this.$store.state.title}
}
//调用:{{count}}{{title}}
2.mutations对象:存放同步函数的集合

state中的数据是不能直接修改的

//不能直接改:
this.$store.state.count++

它的修改只能通过mutations,在mutations对象里面可以存放修改state的方法

2.1.mutations的使用
  • step1:定义mutations对象,对象中存放修改state的方法
state:{
    count:1001
},
mutations:{
	addCount(state,n){*
  	  state.count+=n
	}
}
  • step2:在页面中提交调用mutations
//App.vue
//html
{{count}}
<button @click="handleClick(1)">点击自增1<.button>
<button @click="handleClick(5)">点击自增5<.button>
<button @click="handleClick(10)">点击自增10<.button>
//js
methods:{
	handleClick(n){
		this.$store.commit("addCount",n);
	}
}

//store/index.js
state:{
	count:10
},
mutations:{
	addCount(state,n){
		return state.count+=n
	}
}

*不支持传递多个参数,但可以把这个唯一参数写成数组或对象的形式达到相同目的

//store/index.js
addCount(state,obj){
  state.count+=obj.count;
}
 //App.vue
handleClick(n){
 this.$store.commit("addCount",{
   count:n,
   title:"nihao"
 });
}
2.2.使用mutations实现输入框和子组件的实时更新

难点:不能用v-model对vuex中的数据做双向绑定
vuex中的数据要遵循单向数据流,任何数组的修改都要通过mutations来操作

步骤:(把v-model拆分为value属性和input方法)
  • 1.输入框内容渲染===>用:value渲染
  • 2.监听获取输入框的内容==>用@input触发
  • 3.封装mutations处理函数==>传参
  • 4.调用传参
代码
//父组件App.vue
//1.输入框内容渲染
<input type="text" :value="count" @click="handleInput">
computed:{
	...mapState(['count']),
}
methods:{
	//2.监听获取输入框的内容
	handleInput(e){
		var num=+e.target.value;//字符串==>数字
		this.$store.commit("changeCount",num);//4.调用传参
	}
}

//子组件:Son.vue
{{count}}

//$store/index.js
mutations:{
	//3.封装mutations处理函数
	changeCount(state,newCount){
		state.count=newCount
	}
}
2.3.辅助函数mapMutations

和mapState类似,它是把mutations中的方法提取出来,映射到methods中

//store/index.js
mutations:{
    subCount(state,n){
        state.count-=n
    }
}
xxx.vue子组件
methods:{
    ...mapMutations(["subCount"])
}
//此时相当于组件中的methods多出了一个方法,像把压缩包解压到此处一样
methods:{
    subCount(n){
        this.$store.commit("subCount",n)
    }    
}  
3.actions对象:存放异步方法的集合

mutations必须是同步的,以便检测数据变化,记录调试,因此涉及到异步操作的使用场景(尤其是向后端请求数据)时,需要一个专门的方法来实现项目需求

3.1.使用actions处理异步操作

示例:实现1s后修改state中的count值变为666

//store/index.js
state:{
	return{
		count:100
	}
},
mutations:{
	changeCount(state,newCount){//让mutations方法来改变state中的数据
		state.count=newCount
	}
},
actions:{//不能直接操作state,而是调用mutations来间接操作
	setAsyncCount(context,num){//没有子模块module时,context(上下文)可理解为store
		setTimeOut(()=>{//模拟异步,代替发送请求这种异步场景
			context.commit("changeCount",num);//提交调用mutations方法
		},1000)
	}
}

//页面:App.vue
//html
<span>我是count的值:{{count}}</span><button @click="handleClick">点击改变</button>
//js
  computed:{
    ...mapState(['count'])
  },
  methods:{
    handleClick(){
      this.$store.dispatch("setAsyncCount",666)
    }
  }

3.2.辅助函数mapActions

mapActions把actions中的方法提取出来并映射到组件的methods中
上例可以修改如下:

//App.vue
	//html
	//此处不用另起一个点击事件名了
    <span>我是count的值:{{count}}</span><button @click="setAsyncCount(888)">点击改变</button>
	//js
	import {mapState,mapActions} from "vuex"
	methods:{
   		 ...mapActions(['setAsyncCount'])
  	 }
4.getters计算属性:派生state数据

类似于computed中的数据是基于data中的数据派生出的来的,getters就是从state中派生出的一些依赖属性
getters中的方法有以下要求:

  • 必须要有state作为形参
  • 必须要有返回值(必须要return)
4.1.使用getters选择派生出符合条件的新数据

示例:state中的长度为10的自然数数组,要求在组件中显示大于5的数据

//store/index.js
  getters:{
    filterList(state){
      return state.arr.filter(item=>item>5)
    }
  }
//组件中调用getters数据
<h3>我是数组数据:{{arr}}</h3>
//方法一:直接通过store访问;方法二:mapGetters
<h3>我是数组派生数据:{{this.$store.getters.filterList}}</h3>
4.2.辅助函数mapGetters

mapGetters把写在getters中的方法映射在组件页面的computed中

//App.vue
	//html
   <h3>我是数组数据:{{arr}}</h3>
   <h3>我是数组派生数据:{{filterList}}</h3>
   //js
  import {mapState,mapActions,mapGetters} from "vuex"
  computed:{
    ...mapState(['count','arr']),
    ...mapGetters(['filterList'])
  }

三.Vuex的核心概念:modules

0.介绍
为什么要创建modules模块?

由于vuex使用单一状态树,应用的所有状态会集中到state中,当应用变得复杂时,state对象将会变得十分臃肿,Vuex也将会变得难以维护

Vuex的单一状态树(Single State Tree)是指整个应用的所有组件共享同一个状态存储对象(即store实例),形成树形结构的状态管理系统3。其核心特征包括:
* 唯一性:整个应用只有一个store实例
* 集中式管理:所有组件状态统一存储在state对象中
* 可追溯性:状态变化通过明确的mutation记录

那么,按照功能来拆分成模块,是提高维护性的做法

如何创建modules模块?
  • step1:新建store/modules/user.js
const state={
    userInfo:{
		name:'zhangsan',
		age:18
    }
},
const mutations={},
...
export dafaule {//导出一个对象
    state,
    mutations,
    ...
}
  • step2:在store/index.js中挂载
import user from "@/modules/user.js"
modules:{
    user
}

验证是否挂载成功:控制台>vue>root:user

1.访问模块中state的语法

尽管已经分了模块,但是子模块的状态还是会挂载到跟级别的state上,属性名就是模块名

  • 直接访问:$store.state.模块名.xxx
{{$store.state.user.userInfo.name}}//张三
  • mapState映射:(需开启命名空间[必要])
//默认根级别的映射:mapState(['xxx'])
//而子模块的映射:mapState('模块名',['xxx']),

export default{
    namespaced:true,//开启命名空间
    state,
    mutations,
    ...
}

{{userInfo.name}}
computed:{
    ...mapState("user",['userInfo','score'])
}
2.访问模块中getters的语法
直接访问:$store.getters['模块名/xxx']
通过mapGetters:
    对比根级别的映射:mapGetters(['xxx']),
    子模块的映射:mapGetters('模块名',['xxx']),需开启命名空间

const getters={
    UpperCaseName:(state){//注意此处的state是子模块的状态
        return state.userInfo.name.toUpperCase()
    }
}

1.直接访问:
    div $store.getters['user/UpperCaseName']//Zhangsan
为啥是这个格式?
    created打印this.$store.getters是对象
等价于对象中多出了一个属性,属性名为"user/UpperCaseName"(:"模块名/getters中方法名"),
由于对象属性是特殊字符,所以不能用"obj.属性名"而应该使用"obj[属性名]"

2.通过mapGetters访问:
    div {{UpperCaseName}}
    ...mapGetters('user',['UpperCaseName'])
3.调用模块中的mutations的方法

调用方法:

  • 方法一:直接通过$store调用:[直接访问也需开启命名空间]
  $store.commit("模块名/xxx",额外参数)
  • 方法二:通过mapMutations映射:[开启命名空间]
const mutations={
    changeName:(state,newName){
        state.userInfo.name=newName
    }
};
export default{
    namespaced:true,
    mutations,
}

示例:

//直接访问
{{$store.state.user.userInfo.name}}//张三
<buttom @click="updateUserName">改名字</button>
methods:[
    undateUserName(){
        //this.$store.commit('模块名/mutations中方法名',额外参数')
        this.$store.commit('user/changeName','李四')
    }
]

//mapMutations访问
{{userInfo.name}}
<buttom @click="changeName('李四')">改名字</button>
methods:{
    ...mapMutations('user',['changeName'])
}
4.调用模块中actions的语法

与mutations完全一样,略

5.完整代码
store/modules/user.js
const state={
    userInfo:{
      name:"zhangsan",
      age:18
    }
};
const getters={
 	 UpperCaseName:(state)=>{//注意此处的state是子模块的状态
   		 return state.userInfo.name.toUpperCase()
	}
};
const mutations={
  changeName:(state,newName)=>{
    state.userInfo.name=newName
  }
};
const actions={};

export default{
  namespaced:true,
  state,
  getters,
  mutations,
  actions
}
store.index.js(引入和挂载user.js)
import user from "@/modules/user"
...
  modules:{
    user
  }
App.vue(随便一个页面)
<template>
  <div id="app">
    <h2>1.访问模块中的state</h2>
    <p>直接访问:{{$store.state.user.userInfo.name}}</p>
    <p>mapState访问:{{userInfo.name}}</p>

    <h2>2.访问模块中的getters</h2>
    <p>直接访问:{{$store.getters['user/UpperCaseName']}}</p>
    <p>mapState访问:{{UpperCaseName}}</p>

    <h2>3.访问模块中的mutations</h2>
    <p>直接访问:{{$store.getters['user/UpperCaseName']}}</p>
    <p>mapState访问:{{UpperCaseName}}</p>
    {{$store.state.user.userInfo.name}}
    <button @click="updateUserName">改名字1</button>
    <button @click="changeName('lisi')">改名字2</button>
    
    <h2>4.访问模块中的actions:略</h2>
  </div>
</template>

<script>
import {mapState,mapGetters,mapMutations} from "vuex"
export default {
  name: 'App',
  component:{},
computed:{
    ...mapState("user",['userInfo']),
    ...mapGetters('user',['UpperCaseName'])
},
  methods:{
    updateUserName(){
        //this.$store.commit('模块名/mutations中方法名',额外参数')
        this.$store.commit('user/changeName','wangwu')
    },
    ...mapMutations('user',['changeName'])
  }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端OnTheRun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值