vuex-核心概念 | 青训营笔记


theme: condensed-night-purple

highlight: a11y-dark

这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天

Vuex有几个比较核心的概念:

  • State: 保存共享状态,存放状态相关信息
  • Getters: 类似计算属性
  • Mutation: 状态更新
  • Action: 进行异步操作
  • Module: 划分模块,针对不同模块进行相关数据的保存

State 单一状态树

STATE属性里面有个mapState的辅助函数

State: 保存共享状态,存放状态相关信息

在一个项目里只建一个store

Vuex提出使用单一状态树, 什么是单一状态树呢? 英文名称是Single Source of Truth,也可以翻译成单一数据源

如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。 所以Vuex也使用了单一状态树来管理应用层级的全部状态。

单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。

Getters 类似计算属性 需要从store中获取一些state变异后的状态时使用

和mutations不同的应该是,他是拿来用,不改原来的样子

在mutation里定义的操作会改变state里面的属性值,但getter里定义的方法只是对数据进行了一番处理,并没有改变state里面的属性值。

getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数 ```js

{ { $store.getters.powerCounter }}

{ { $store.getters.more20stu }}

{ { $store.getters.more20stuLength }}

{ { $store.getters.moreAgeStu(18) }}

getters: { powerCounter (state) { return state.counter * state.counter }, more20stu(state) { return state.students.filter( s => s.age >= 20) }, // getters也可以作为参数转入 // 但getters里的方法只能接收两个参数:state和getters more20stuLength(state, getters) { return getters.more20stu.length }, // 想要自己给定比较的age,可以返回一个函数,将参数传入 moreAgeStu(state) { return age => { return state.students.filter( s => s.age > age) } } }, ```

Mutation 状态更新

Vuex的store状态的更新唯一方式:提交Mutation

Mutation主要包括两部分:

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

```js // mutation的定义方式: mutations: { // 方法 默认参数state 自动传入state increment(state) { state.counter++ } },

// 通过mutation更新 // 在app里 methods: { addition() { this.$store.commit("increment") // 使用commit方法,传入方法名 // 跟踪每一次的提交的状态 } }, ```

Mutation传递参数

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

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

但是如果参数不是一个呢? 比如我们有很多参数需要传递. 这个时候, 我们通常会以对象的形式传递, 也就是payload是一个对象. 这个时候可以再从对象中取出相关的信息. ```js // Mutation中的代码: mutations: { incrementCounter(state, count) { // mutation的使用与事件处理函数非常相似,都具有类型和回调函数 // (类似methods,不过获取state中的变量不是this.变量名,而是state.变量名)。 state.counter += count }, addStudent(state, stu) { state.students.push(stu) } },

// 在app里的代码:

methods: { addCount(count) { this.$store.commit("incrementCounter", count) }, addStudent() { // 有多个参数,通过对象同一传递 // payload:负载 参数被称为是mutation的载荷(Payload) const stu = { id : 104, name : "alban", age : 26} this.$store.commit("addStudent", stu) } } ```

Mutation提交风格

上面的通过commit进行提交是一种普通的方式。 Vue还提供了另外一种风格, 它是一个包含type属性的对象 ```js addCount(count) { // 1. 普通的提交风格 // 提交的只是值 // this.$store.commit("incrementCounter", count)

// 2. 特殊的提交风格
  // 将整个对象提交过去
  this.$store.commit({
    type : "incrementCounter",  // type就是mutations里的方法名
    count   // counter : counter  ES6
  })
}

```

Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下 ```js incrementCounter(state, payload) { // mutation的使用与事件处理函数非常相似,都具有类型和回调函数 // (类似methods,不过获取state中的变量不是this.变量名,而是state.变量名)。

// 普通提交
  // console.log(count);
  // state.counter += count

  // 特殊提交
  // 此时count可以写成payload
  console.log(payload);
  state.counter += payload.count
}

```

Mutation响应规则

前排提醒,本节讲述的方法在Vue3已经废弃,Vue3可以直接在mutations正常赋值

Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.

这就要求我们必须遵守一些Vuex对应的规则: 提前在store中初始化好所需的属性.

当给state中的对象添加新属性时, 使用下面的方式: - 方式一: 使用Vue.set(obj, 'newProp', 123) - 方式二: 用新对象给旧对象重新赋值

```js

------------App组件--Mutations使用--info对象的内容是否响应式-------------

{ { $store.state.info }}

updataInfo() { this.$store.commit("updataInfo") }
mutations代码   
    updataInfo(state) {
  // state.info.name = "abc"

  // 要添加属性
  // 这种方法不是响应式的
  // state.info['address'] = '多伦多'
  // 
  Vue.set(state.info, 'address', '多伦多')

  // 删除属性
  // delete state.info.age
  Vue.delete(state.info, 'age')
}

```

state里的每个对象的初始化属性都有一个watcher,一开始就定义的属性会被加入vue的响应式系统, 之后就会通过 DEP->[watcher]观察属性的变化, (每个属性对应一个dep,dep对应很多watcher,dep存放wather的集合 内部响应式系统:dep监听属性的变化-观察者模式,数据变化后看那些地方需要根据数据变化进行刷新界面 dep有个数组,里面放着很多watcher,通知对应watcher进行页面变化)

state里一开始就定义的属性都会被加入到响应式系统中,而响应式系统会监听属性的变化,当属性发生变化时,会通知所有界面中用到该属性的地方,让界面发生刷新

每个属性都有自己的一个depency数组,里面装着不同的watcher,watcher则是对应视图中每一个有调用这个属性的地方

后增加的属性没有加入响应式系统,所以无法响应,界面没有刷新 要想实现响应式,需要使用Vue.set(要增加的属性的对象,"属性名“,属性值) 删除用Vue.delete(要删除的属性的对象,"属性名")

Mutation常量类型

概念

问题: 在mutation中, 我们定义了很多事件类型(也就是其中的方法名称). 当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多. 方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.

如何避免? 一种很常见的方案就是使用常量替代Mutation事件的类型. 将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.

具体怎么做? 创建一个文件: mutation-types.js, 并且在其中定义我们的常量. 定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.

代码

```JS -----mutation-types.js export const INCREMENT = 'increment' 这样导出时要注意,导入时要用对象(普通导出)

-----index.js // '函数名字'{} //在对象中可以这样定义方法 // 在ES6中,把属性名用[ ]括起来,则括号中就可以引用提前定义的变量。 INCREMENT { state.counter ++ },

-----app.vue import HelloWorld from './components/HelloWorld.vue'; // 这种方式只能是export default导出 import { INCREMENT, }from './store/mutations-types' // 普通导出要这样导入 this.$store.commit(INCREMENT) ```

Mutation同步函数

Vuex要求我们Mutation中的方法必须是同步方法

在mutations里进行异步操作,看起来是修改了state, 但是devtools无法跟踪该步操作,使得记录的state数据是操作之前的,导致出现错误

Action

总结:state(存放数据),getter(处理数据), mutations(修改数据), actions(接收异步数据)

概念

Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.

异步操作要在actions里进行,然后提交commit到mutataions,在mutations里对state进行修改 在app里,需要dispatch到actions, actions可以传递参数payload,它的第一个参数为context,可以理解为store

context是和store对象具有相同方法和属性的对象,可以通过context去进行commit相关的操作, 也可以获取context.state等。但是注意, 这里它们并不是同一个对象, 为什么呢? 我们后面学习Modules的时候, 再具体说

基本使用

```js app
// 进行异步操作

this.$store.dispatch('aUpdateInfo', '我是payload')

actions: { aUpdateInfo(context, payload) { // context: 上下文 可以可理解为store 可以传递参数 setTimeout(() => { // context.state.info.age = 35 不能这样,因为只能在mutations里修改state context.commit('updateInfo',payload) console.log(payload); }, 1000) } }, ``` 这里打开devtools后,添加信息按钮报错,解决方法: devtools导致报错的,把插件里Plugin settings的legacy action开启就好了

如果app想要知道异步操作是否完成,要怎么做?

一般认为 commit成功了就是异步操作完成

  1. 可以把回调函数放到payload中传递 不够优雅 js // app.js this.$store.dispatch('aUpdateInfo', { messege : '我是携带的信息', success () { console.log('异步操作成功'); } })

  2. 更加优雅的方式 dispatch执行发送.then执行回调,

在Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolve或reject.

Promise经过dispatch中转,在app里调用then方法 js // Promise经过dispatch中转,在app里调用then方法 // app组件 this.$store .dispatch('aUpdateInfo2', 'payload携带的信息') .then( res => { console.log(res); console.log('异步操作成功'); }) // index.js aUpdateInfo2(context, payload) { // 2. actions返回Promise,经过dispatch,在组件中使用then方法 return new Promise((resolve, reject) => { setTimeout(() => { context.commit('updateInfo') console.log(payload); resolve('返回Promise方法') }, 1000) }) }

Module 模块 (套娃)

概念

为什么在Vuex中我们要使用模块呢? Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理. 当应用变得非常复杂时,store对象就有可能变得相当臃肿. 为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutations、actions、getters等

我们按照什么样的方式来组织模块呢?

代码:

```js // 组件

{ { this.$store.state.a.messege }}

// modules里的a会被放入到state里,所以使用时:this.$store.a.messege updateMessege(){ this.$store.commit('updateMessege', '书籍是人类进步的阶梯') // 一般模块里的mutations方法的名字不要和store里重复 直接commit,提交统一commit } // index.js const moduleA = { state : { messege : '大大怪将军' }, mutations: { updateMessege(state, payload) { state.messege = payload } }, actions : { aUpdateMessege(context) { // 只commit模块内部的mutations,这里的context相当于自己模块 setTimeout(() => { context.commit('updateMessege', '小心超人') }, 1000) } }, getters : { fullmessege(state){ return state.messege + '1111' }, fullmessege2(state, getters) { return getters.fullmessege + '2222' } // 如果想要获取store的state // 在模块内可以有第三个参数rootState fullmessege3(state, getters, rootState) { return getters.fullmessege2 + ' ' + rootState.counter } } } actions还有另一种写法 因为这里的context多了两个属性:rootState,rootGetters 所以如果actions需要store的数据,可以使用 ES6中有个**对象解构**的知识 js // 在actions中 actions : { isEqual({state, commit, rootState}) { if(state.counter == rootState.counter) commit('equal') } }

// 对象解构 const obj = { name : 'yuli', age : '20', address : '洛杉矶' } const {name, age} = obj ``` 解构赋值 解构对象的时候,名称一定要与对象里面的名称一样 顺序可以替换,也可以少分配 数组就得按顺序了,对象是按key来的

// 完整写法是name:name,左边name是匹配模式,右边是变量名,简写就是直接name,
// 如果想改变量名就name:na,一定记住结构时冒号左边是匹配模式,右边是要赋值的变量,
// 具体可以看阮一峰es6

注意:

  1. modules里的a会被放入到state里,所以组件使用时:this.$store.a.messege
  2. 一般模块里的mutations方法的名字不要和store里重复 直接commit,提交统一commit
  3. 如果模块内想要获取store的state,在模块内getters可以有第三个参数rootState,还有rootGetters
  4. actions的参数context相当于自己模块,只能commit模块内部的mutations
  5. dispatch时,模块的actions名字也不要重复
  6. 使用模块的mutations,actions,getters 和store一样,this.$store.方法名

猜你喜欢

转载自blog.csdn.net/weixin_50945128/article/details/129377820