Vue学习Day15 Vuex的getters、mutations、actions、modules用法、项目结构

想利用暑假时间好好学习一下vue,会记录每一天的学习内容。
今天是学习vue的第15天!

起起伏伏乃人生常态,继续加油~



1. Vuex核心概念

  • State
  • Getters
  • Mutations
  • Actions
  • Modules

关于State的部分昨天已经学完啦~今天从Getters开始

Getters

Vuex允许我们在store中定义getter,可以认为是store计算属性

Getters基本使用

有时候我们需要从store中获取一些state变化后的状态
比如下例:获取年龄大于20的学生

const store = new Vuex.Store({
  state: {
    students: [
      {id: 110, name: 'AIpoem1', age: 18},
      {id: 111, name: 'AIpoem2', age: 21},
      {id: 112, name: 'AIpoem3', age: 25},
      {id: 113, name: 'AIpoem4', age: 30},
    ]
  }
})

我们可以在store中定义getters

getters: {
  greaterAge(state) {
    return state.students.filter(s => s.age>20);
  }
},

在界面中调用:

<h2>年龄大于20的学生是:{{$store.getters.greaterAge}}</h2>

在这里插入图片描述

computed里定义方法也是可以的

computed: {
  greaterAge() {
    return this.$store.state.students.filter(s => s.age>20);
  }
},

Getters作为参数和传递参数

Getters作为参数:
如果此时要获取年龄大于20的学生的学生个数:

getters: {
  greaterAge(state) {
    return state.students.filter(s => s.age>20);
  },
  // 其实就是return state.students.filter(s => s.age>20).length;
  greaterAgeLength(state, getters) {
  	return getters.greaterAge.length;
  }
},

Getters传递参数:

getters默认是不能传递参数的,如果希望传递参数,只能让getters本身返回另一个函数
比如上例中,我们希望根据ID获取用户的信息

stuByID(state) {
  return function(id) {
    return state.students.find(s => s.id === id);
  }
}
<!--获取id为110的用户信息-->
 <p>{{$store.getters.stuByID(110)}}</p>

在这里插入图片描述


Mutations

Mutations状态更新

Mutation主要包括两部分:

  • 字符串的事件类型(type)
  • 一个回调函数(handler),该回调函数的第一个参数就是state

mutation的定义方式:

mutations: {
  increment(state) {
    state.counter++;
  }
}

通过mutation更新:

// 这里不一定方法名要交increment(),只是commit()里要是'increment'
methods: {
  increment() {
    this.$store.commit('increment');
  }
}

Mutations传递参数

在通过mutations更新数据的时候,有可能我们希望携带一些额外的参数

  • 参数被称为是mutations的载荷(Payload)

比如我们要定义一个方法,使counter能增加任意数目:

mutations中的代码:

mutations: {
  increment(state, n) {
    state.counter += n;
  }
}

界面中调用:

methods: {
  // increment()方法使counter+2
  increment() {
    this.$store.commit('increment', 2)
  }
}

如果我们有很多参数需要传递:

  • 通常我们会以对象的形式传递,也就是payload是一个对象
  • 再从对象中取出相关的信息

比如我们要添加一个student对象到state里的students数组中:
mutation中的代码:

mutations: {
  addStudent(state, newStudent) {
    state.students.push(newStudent);
  }
}

这个newStudent是需要我们传入的一个参数

methods: {
  addStudent() {
    const newStudent = {id: 115, name: 'new student', age: 20};
    this.$store.commit('addStudent', newStudent);
    // 直接传也可以
    this.$store.commit('addStudent', {id: 115, name: 'new student', age: 20});
  }
}

在这里插入图片描述


Mutations提交风格

上面例子通过commit进行提交是一种普通的方式
Vue还提供了另外一种风格,它是一个包含type属性的对象

this.$store.commit({
  type: 'changeCount',
  count: 100
})

mutations中的处理方式是将整个commit的对象作为payload使用

changeCount(state, payload){
  // payload是commit的对象,要从中取出count
  state.count = payload.count;
}

Mutation响应规则

Vuexstore中的state是响应式的,当state中的数据发生改变时,Vue组件会自动更新
这就要求我们必须遵守一些Vuex对应的规则:

  • 提前在store初始化好所需的属性
  • 当给state中的对象添加新属性时,使用下面的方式:
    • 方式一:使用Vue.set(obj, 'newProp', newValue);
    • 方式二:用新对象给旧对象重新赋值**

我们来看一个例子:

<template>
  <div id="app">
    <p>我的个人信息:{{$store.state.info}}</p>
    <button @click="updateInfo">更新信息</button>
  </div>
</template>

<script>
  export default {
    methods: {
      updateInfo() {
        this.$store.commit('undateInfo');
      }
    }
  }
</script>
const store = new Vuex.Store({
  state: {
    info: {
      name: 'AIpoem',
      age: 19
    }
  },
  mutations: {
    updateInfo(state) {
      state.info['address'] = '12345'
    }
  }
})

在这里插入图片描述
点击更新信息按钮,但界面未发生改变
在这里插入图片描述
Devtools是能成功跟踪到的,此时info对象中已经添加了address属性
说明这种添加属性的方法并不是响应式

如何才能让state中添加的属性是响应式的:

  • 方式一:使用Vue.set(obj, 'newProp', newValue);
mutations: {
  updateInfo(state) {
    Vue.set(state.info, 'address', '12345');
  }
}
  • 方式二:用新对象给旧对象重新赋值
mutations: {
  updateInfo(state) {
    state.info = {...state.info, 'address': '12345'}
  }
}

⚠️:如果是修改:已经在store中初始化好的state属性,是可以直接改的,因为它们已经被添加到vue的响应式系统里了
比如上面的info对象中的name属性或是age属性


使用常量代替Mutations事件类型

把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然
用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。

在store文件下新建一个mutation-types.js文件
在这里插入图片描述

将原本是'increment'的地方替换成常量INCREMENT,要记得导入mutation-types.js文件

// mutation-types.js
export const INCREMENT = 'increment'
// App.vue
import {INCREMENT} from './store/mutation-types'

methods: {
  increment() {
    this.$store.commit(INCREMENT)
  }
}
// index.js
import {INCREMENT} from './store/mutation-types'
 mutations: {
    [INCREMENT] (state) {
      //...
    }
  }

Actions

Mutations同步函数

通常情况下,Vuex要求我们Mutations中的方法必须是同步方法

  • 主要原因是当我们使用Devtools时,Devtools可以帮助我们捕捉mutations的快照
  • 但如果是异步操作,那么Devtools将不能很好的跟踪这个操作什么时候会被完成
  • 所以通常情况下,不要在mutations中进行异步操作

Actions 的基本定义

我们强调,不要在mutations中进行异步操作

  • 某些情况下,如果确实希望在Vuex中进行一些异步操作,比如网络请求
  • 这时候用actions来代替mutations来进行异步操作

举例:
先尝试在mutations中进行异步操作,看看结果

state: {
  info: {
	name: 'AIpoem',
	age: 19
  }
}
// 错误做法
mutations: {
  updateInfo(state) {
  	// setTimeout模拟异步操作
	setTimeout(() => {
	  state.info.name = '123'
    },1000)
}

点了更新信息后,界面已更改
在这里插入图片描述
Devtool中并未捕捉到
在这里插入图片描述
正确使用:
应该先由组件dispatchactions,再由actions commitmutations,从而修改state

先注册一个action

mutations: {
  updateInfo(state) {
    state.info.name = '123'
  }
},
actions: {
  aUpdateInfo(context) {
    setTimeout(() => {
      context.commit('updateInfo');
    },1000)
  }
},

context是一个和store实例具有相同方法和属性的对象,因此可以调用context.commit提交一个mutation,但context并不是store实例本身

分发action

methods: {
  updateInfo() {
    this.$store.dispatch('aUpdateInfo');
  }
}

这个时候再点击更新信息,界面发生更改
在这里插入图片描述
同时Devtool也能记录到
在这里插入图片描述


Actions传递参数

actions同样也能传递参数:

actions: {
  // payload参数
  aUpdateInfo(context, payload) {
    setTimeout(() => {
      context.commit('updateInfo');
    },1000)
  }
},
methods: {
  updateInfo() {
    this.$store.dispatch('aUpdateInfo', '我是payload');
  }
}

如果想传递多个函数,也和mutations一样,传一个对象payload,再从对象里取出来信息

我们还可以定义一个成功的回调,

methods: {
  updateInfo() {
    this.$store.dispatch('aUpdateInfo', {
      message: '一些携带的信息',
      success() {
        console.log('已经完成了');
      }
    };
  }
}
actions: {
  aUpdateInfo(context, payload) {
    setTimeout(() => {
      context.commit('updateInfo');
      // 执行到这里已经成功commit
      payload.success();
    },1000)
  }
},

一种更优雅的方式:
(携带信息和回调不放在一起)


在这里插入图片描述
遇到resolve()就会去执行then()里的内容,并且将整个promise对象返回给调用的方法
可以理解成this.$store.dispatch('aUpdateInfo', '我是payload') 被替换为 new Promise(...),然后继续执行then()


modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。

  • 为了解决以上问题,Vuex允许我们将 store 分割成模块
  • 每个模块拥有自己的 statemutationsactionsgetters、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
  state: {
    name: 'AIpoem'
  },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const store = new Vuex.Store({
  modules: {
    a: moduleA
  }
})
<h2>{{ $store.state.a.name }}</h2>

在这里插入图片描述
上面的代码中,我们已经有了整体的组织结构,下面我们来看看具体的局部模块中代码


mutations和getters

const moduleA = {
  state: {
    count: 0
  },
  mutations: {
    increment(state){
      state.count++;
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  }
}
<h2>{{ count }}</h2>
<button @click="increment"></button>
computed: {
  count() {
    return this.$store.getters.doubleCount;
  }
}
methods: {
  increments() {
    this.$store.commit('increment');
  }
}
  • mutationsgetters接收的第一个参数是局部状态对象

⚠️:虽然我们的 doubleCountincrement都是定义在对象内部的
但是在调用的时候,依然是通过this.$store来调用的


actions

接收一个context参数对象

  • 局部状态通过context.state暴露出来,根节点状态则为context.rootState
actions: {
    // state、commit、rootState是context对象的属性,这里是对象解构写法
	incrementIfOddOnRootSum ({ state, commit, rootState }) {
		if ((state.count + rootState.count) % 2 === 1) {
        	commit('increment')
        }
    }
}

⚠️:如果getters也需要使用全局的状态,也可以接收rootState参数


2. 项目结构

Vuex并不限制你的代码结构。
但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutations 是更改状态的唯一方法,并且这个过程是同步的
  3. 异步逻辑都应该封装到 actions 里面。

只要你遵守以上规则,如何组织代码随你便。

如果你的 store 文件太大,只需将 actionsmutationsgetters 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

就是将actionmutationsgetters 都抽出去,
state不用抽
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值