一、什么是Vuex
Vuex是vue应用开发的状态管理模式,采用集中式存储管理
注意:
vue2中使用vuex3 npm i vuex@3
vue3中使用vuex4
1、状态管理
如果组件之间的层级过多,组件之间的数据传递变得非常困难,所以需要数据共享
实际上就是将多个组件共享的变量存储在一个对象里边,将对象放在顶层的vue实例之中,实现共享,而且数据发生改变时所有共享数据的页面可以实现响应式的刷新
虽然可以将共享数据添加到vue原型中,也可以实现共享,但是无法实现响应式
2、使用场景
多个组件依赖于同一状态 | 来自不同组件的行为需要变更同一状态
并不是所有的共享数据都需要Vuex,例如父子组件数据共享,通过父子传值已经足够
- 登陆状态,用户名、头像、地理位置
- 商品的收藏,购物车中的信息
对于以上的状态,就可以使用Vuex进行保存和管理,实现响应式
二、Vuex的使用
// 安装vuex
npm install vuex --save
// 创建store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 将插件挂载到vue实例,必须在store实例化之前挂载
Vue.use(Vuex)
// 创建对象
const store = new Vuex.Store({
// 保存共享的状态
state: { num:1 },
mutations: {
// 定义修改state的方法,这里默认会把state参数传递过来
add(state){
state.num++
}
},
actions: {},
getters: {},
modules: {}
})
// 导出store
export default store
// 最后将store挂载到vue实例中,出现$store属性
import store from './store'
new Vue({
el: '#app',
store,
render: h=>(App)
})
工作原理分析
注意:
- 不建议直接修改state中的共享数据@click="$store.state.num++",通过Mutation修改才能使用devtools进行修改的记录监听,但是devtools工具只能跟踪同步操作,所以异步操作要在action中进行
-
修改步骤:store.dispatch('action中的方法名', 数据)
store.commit('mutations中的方法名', 数据)
如果没有其他业务逻辑,可以直接越过action,直接进行commit
三、state
state单一状态树概念:将所有需要管理的信息全部放到一个store之中进行管理,使用一个状态树管理应用层级的全部状态,能够直接地访问到某个状态的片段。
而放到多个store之中,维护会变得非常困难
state: {
num:1,
age: [19,44,12,7,34],
info: {
name: 'zhou',
age: 20
}
}
// 使用
<p>{{$store.state.num}}</p>
>将state中的内容导入到组件计算属性之中使用:
1、可以将数据配置在计算属性之中,模板中可以直接使用num
computed:{
num(){
this.store.state.num
}
}
2、使用mapState自动生成 {计算属性名: state中的值}
import {mapState} from 'vuex'
computed:{
// 返回结果是一个计算属性组成的对象(计算属性名和state属性名可以不一样)
...mapState({sum:'sum'})
}
3、使用数组写法,意味着生成的计算属性名和state中的属性名一致
computed:{
...mapState(['sum'])
}
// mapState实际上是将数据赋值给了vue对象一份
// 可以通过this.store.state.num修改数据
// 不能通过this.num修改数据
四、getters
类似计算属性,对数据进行处理,收到一个参数state
getters: {
newNum(state){
return state.num+=1
},
// 可以传入getters对象为第二个参数
newNum2(state,getters){
return state.num * getters.newNum
}
// 可以返回一个函数,可以由外部自定义参数进行处理
age(){
return function (age){
return state.age.filter(s => s>age)
}
}
}
// 使用(直接按照属性的方式使用,不需要括号)
<p>{{store.getters.newNum}}</p>
// 外部传入参数,显示大于8岁的年龄
<p>{{store.getters.age(8)}}</p>
>将getters中的内容导入到组件计算属性之中使用:
// getters
export default {
cartLength(state) {
return state.cartList.length
},
cartList(state) {
return state.cartList
}
}
// 要使用getters的组件
import {mapGetters} from 'vuex'
computed:{
...mapGetters(['cartLength','cartList'])
// 可以添加一层映射,直接使用属性名进行使用
...mapGetters({
length: 'cartLength',
list: 'cartList'
})
}
// 使用
{{cartLength}}或者{{length}}
五、mutation
1、store状态更新
store状态更新唯一的方式,提交mutation,默认会把state参数传递过来
// 定义
mutations: {
// 定义修改state的方法,这里默认会把state参数传递过来
add(state){
state.num++
}
}
// 使用
methods: {
change(){
// 使用commit('方法名')对state进行修改
this.$store.commit('add')
}
}
>在methods中调用commit方法:
import {mapMutations} from 'vuex'
// {方法名: mutation属性} | [方法名(mutation属性)]
methods: {
...mapMutations({add:'add'})
// 或者
...mapMutations(['add'])
}
// 在methods中添加了方法,如果有参数,需要在调用时候进行传递
2、使用时传参
使用mutation更新数据时侯,有时希望携带参数,也称作mutation的载荷(Payload)
——使用时传入参数:
<p @click="change(5)"></p>
methds: {
change(count){
this.$store.commit('add',count)
}
}
----------
// 增加多少由使用者决定
mutations: {
// 这里默认会把state参数传递过来,同时也会加上count参数
add(state,count){
// 每次加5
state.num+=count
}
}
如果参数不止一个,Payload也可以是一个对象
3、Mutation的提交风格
Vue除了上面的普通风格外,还提供了另一种风格,它是一个包含type属性的对象
<p @click="change(5)"></p>
methds: {
change(count){
// 将数值提交,这里提交的是一个对象,包括type和count属性
this.$store.commit({
type: 'add',
count: count
})
}
}
----------
mutations: {
// 这时候接收到的不是数字,而是一个对象包括type和count属性
add(state,payload){
state.num+=payload.count
}
}
4、Mutation响应规则
- 提前在store中初始化所需属性
- 使用以下方式添加属性
- Vue.set(要修改的对象,'属性',修改的值),此方法也可以进行修改
- 用新对象给旧对象重新赋值
state: {
// 定义时添加属性,属性会添加到响应式系统之中,监听属性的变化,实时更新界面
info: {
name: 'zhou',
age: 20
}
}
----------
// 修改属性
updateInfo(state){
// 可以实现响应式
state.info.name="zhouyabo"
}
// 增加属性
updateInfo(state){
// info中可以添加sex属性,但是界面不会响应刷新
state.info.['sex']="男"
// 解决方法,实现响应式添加
Vue.set(state.info,'sex','男')
}
// 删除属性
delInfo(state){
// 能删除info中的属性,但是不能实现响应式
delete state.info.age
// 解决方法
Vue.delete('state.info','age')
}
5、mutation常量类型
在mutations之中,定义了许多事件类型(方法名称),多个文件切换可能会出现方法名错的情况
// 将方法名单独抽离成一个文件motations_type.js
export const UPDATE = 'updateinfo'
---------
import {UPDATE} from './motations_type.js'
mutations: {
[UPDATE](state){}
}
----------
import {UPDATE} from './motations_type.js'
methods: {
this.$store.commit(UPDATE)
}
6、mutation同步函数
通常情况下,Mutation中的方法必须是同步方法,如果是异步方法,devtools将不能很好的跟踪操作是否完成
// 异步修改属性:错误的写法
updateInfo(state){
setTimeout(()=>{
state.info.name="zhouyabo"
},1000)
}
// 页面可以更改,info也可以实现更改,但是devtool不会更改,没有记录新信息
// 将异步操作放到action中执行
updateInfo(state){
state.info.name="zhouyabo"
}
解决办法:action是用来处理异步操作的
六、action
1、基本使用
类似于mutation,是用来代替mutation处理异步操作的,对异步操作进行处理
actions: {
// action中的方法也有默认属性,但是不是state,而是context(上下文:store)
actionUpdate(context){
setTimeout(()=>{
// 不能直接在这里修改state,修改state唯一的途径就是mutation
context.commit('updateInfo')
},1000)
},
}
>在methods中调用dispatch方法:
methods: {
changeInfo(){
// 这里不能使用commit,commit是提交mutation的
thsi.$store.dispatch('actionUpdate')
}
}
--------------------------
同样可以使用mapActions进行操作
import {mapActions} from 'vuex'
methods: {
...mapActions({changeInfo:'changeInfo'})
// 或者
...mapAction(['changeInfo'])
}
若有参数,需要在调用时候传递
2、执行回调
- 方法一,函数回调
methods: {
changeInfo(){
// 可以传递一个对象
thsi.$store.dispatch('actionUpdate',{
message:'传递的信息',
success:()=>{console.log('提示信息')}
})
}
}
----------
actions: {
actionUpdate(context,payload){
setTimeout(()=>{
context.commit('updateInfo')
// 执行传递进来对象的成功函数进行提示
payload.success()
},1000)
}
}
- 方法二,Promise 回调
actions: {
actionUpdate(context,payload){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('updateInfo')
resolve('ok')
},1000)
})
}
----------
methods: {
changeInfo(){
thsi.$store.dispatch('actionUpdate','传递的信息').then(res=>{
console.log(res) // ok
}
}
}
七、modules
state单一状态树使得代码越来越臃肿,所以可以使用module划分模块进行保存
export default new Vuex.Store({
modules: {
a: {
// 开启命名空间,默认为false
namespaced: true,
state: {
name:'Tom'
},
mutations: {
getData(){}
},
actions: {},
getters: {}
},
b: {
namespaced: true,
state: {},
mutations: {},
actions: {},
getters: {}
}
}
})
目的:让代码更好地维护,数据分类更加明确
开启命名空间后,读取state的数据
方法一:直接读取
this.$store.state.a.name
方法二:借助mapState读取
.mapState('a', ['name'])
开启命名空间后,读取getters数据
方法一:直接读取
this.$store.getters['a/要读取的属性']
方法二:使用mapGetters读取
.mapGetters('a',['要读取的属性'])
开启命名空间后,组件中调用dispatch
方法一:直接调用
this.$store.dispatch('a/方法名', 参数)
方法二:使用mapAction
...mapAction('a', {methods中方法名, actions中的方法名})
开启命名空间后,组件中调用commit
方法一:直接commit
this.$store.commit('a/方法名', 参数)
方法二:使用mapMutations
...mapMutations('a', {methods中的方法名, mutations中的方法名})
八、store文件夹的目录组织
=store
=modules
————moduleA.js
———index.js
———mutations.js
———actions.js
———getters.js
文件通过export default 导出,通过impot导入
文章评论