11 Vuex状态管理
11.1 Vuex介绍
Vuex
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,是一个插件
Vuex核心store
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
Vuex管理的状态包括(多个组件间共享):
- 如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
- 比如用户的登录状态、用户名称、头像、地理位置信息等等。
- 比如商品的收藏、购物车中的物品等等。
- 这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的
Vuex实现了组件共享和响应式
页面状态管理
单页面的状态管理
- State:不用多说,就是我们的状态。确定应用的数据源(你姑且可以当做就是data中的属性)
- View:视图层,可以针对State的变化,显示不同的信息。
- Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变。
多页面状态管理
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
Vuex基本思想
- Vuex背后的基本思想:通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
- 即:将共享的状态抽取出来,交给我们的大管家,统一进行管理。按照我规定好的规定,进行访问和修改等操作
Vue官方调试工具vue devtools
Vue 的官方调试工具 devtools extension的安装
- 在浏览器的设置中添加扩展程序devtools
- 在运行的程序中可以看到vue,点击进行程序的调试
11.2 Vuex的基本使用
11.2.1 基本使用
使用步骤:(Vuex是一个插件,所以在使用中必须通过Vue.use(Vuex)来安装插件才能使用)
- 下载:用npm进行安装(开发时依赖)
1
npm install vuex --save
- 导入,并安装插件Vue.use(Vuex)
- 创建实例对象store,之后可以通过$store来获取实例对象store(单独创建文件夹store->index.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import Vue from 'vue'
// 导入
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex);
// 2 创建对象
const state = {
count: 1000
}
// mutations = {}
// actions = {}
// getters = {}
//构造器Vuex.Store
const store = new Vuex.Store({
state,
// mutations,
// actions,
// getters,
})
// 之后可以直接通过$store来获取状态管理对象store
export default store- Vuex 通过
store
选项,提供了一种机制将状态从根组件“注入”到每一个子组件中,在入口文件中引入store->index.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13import Vue from 'vue'
import App from './App'
import router from './router'
引入store
import store from './store'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
//添加store,Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中
store,
render: h => h(App)
})- 这里store和router一样,可以通过$store来获取Vue对象中的状态管理实例store
- 在script标签的组件内部通过
this.$store
获取到实例对象,并通过.store属性名
获取属性值 - 在.vue文件组件的模板中通过
$store
获取,这里获取的是全局store对象
- 在script标签的组件内部通过
Vuex 依赖 Promise。如果你支持的浏览器并没有实现 Promise (比如 IE),那么你可以使用一个 polyfill 的库,例如 es6-promise。
11.2.2 modules涉及知识点
Vuex中数据的响应式原理
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新
Mutation响应规则(可以发生响应的前提)
- 提前在store中初始化好所需的属性(响应式的前提条件)
- 原理:在state中初始化好属性后,该属性会被直接添加到响应式的系统里,Vue中的响应式系统会实时监控watch(dev->watches)这些属性的变化并刷新显示在页面中
- 注意后添加的属性如果直接进行添加是做不到响应式的,并没有添加到响应式的系统中,常见的不能响应式的数据有
- 通过数组的下标修改数组中的值就不能做到响应式的
arr[index]=0
,解决方案通过splice() - 通过state直接添加/删除属性
state.info[未初始化属性]=''``delete state.info.name
name是初始化过的属性
- 通过数组的下标修改数组中的值就不能做到响应式的
- 当给state中的对象添加新属性时, 解决方案:使用下面的方式
- 方式一: 使用Vue.set(obj, ‘newProp’, 123)
- 方式二: 用新对象给旧对象重新赋值(借助扩展运算符)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21updateInfo(state) {
// 1.初始化之前定义的数据是可以做到数据响应式的
state.info.name = 'respark'
// 2.为初始化属性添加属性值,并不会响应
state.info['address'] = '哈尔滨'
/*
3.解决未初始化属性的响应,
- 通过Vue.set()
- 给对象赋一个新的值
*/
// 方式1
Vue.set(state.info, 'age', 45)//可响应
// 方式2:这里通过扩展运算符将对象转为用逗号分隔的参数序列,在添加新的属性
state.info = { ...state.info, 'height': 1.88 }
// 4. 删除初始化属性(不能做到响应式)
delete state.info['age']
// delete state.info.age
// 5.删除属性解决
Vue.delete(state.info, 'age')
}- 提前在store中初始化好所需的属性(响应式的前提条件)
Vue.set()/ Vue.delete()
二者都是全局api,通过Vue的全局api操作的属性会添加到响应式系统里,可以做到实时的数据响应
Vue.set( target, propertyName/index, value )
参数:
{Object | Array} target
要修改的目标对象{string | number} propertyName/index
目标对象中的key(对象关键字)/index(数组索引号){any} value
要修改的值
返回值:设置的值。
用法:
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如
this.myObject.newProperty = 'hi'
)注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
Vue.delete( target, propertyName/index )
参数:
{Object | Array} target
{string | number} propertyName/index
用法:
删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制,但是你应该很少会使用它
目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。
仅在 2.2.0+ 中同样支持在数组上工作。
Mutation常量类型
ES6中导入方式:
- 通过
export
导出的变量和函数,导入时通过对象接收对应的变量和函数import {变量名,函数名} from ''
- 通过
export default
函数/变量 ,默认导出,导入时可通过重新命名default导出的名称,只能有一个默认导出,import 重命名 from ''
- 可以通过
as
进行重名import * as 重命名 from ''
- 通过
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。
- 这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:
具体操作:
- 创建文件mutation.types.js,p并且在其中定义我们的常量.
- 在store->index.js和vue文件中分别导入
- 定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.格式:
[常量名](){}
11.2.3 Vuex详细使用
Vuex.Store
- Vuex.Store()是一个构造器,通过new来创建store的实例对象
1 | import Vuex from 'vuex' |
Vuex.Store 构造器选项
构造器中传入的参数一个对象,对象中包含options属性,主要包括:
state
类型:
Object | Function
Vuex store 实例的根 state 对象
store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:
1
2
3
4
5
6
7
8const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}如果你传入返回一个对象的函数,其返回的对象会被用作根 state。这在你想要重用 state 对象,尤其是对于重用 module 来说非常有用模块重用
单一状态树:Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
mutations
类型:
{ [type: string]: Function }
在 store 上注册 mutation,处理函数总是接受
state
作为第一个参数(如果定义在模块中,则为模块的局部状态),payload
作为第二个参数(可选)。更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
提交载荷(Payload):就是传递参数,参数可以是字符串/对象
你可以向
store.commit
传入额外的参数,即 mutation 的 载荷(payload):在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
提交mutation的两种方式:
字符串形式:你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为
increment
的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:对象风格的提交:直接使用包含
type
属性的对象:
mutation必须是同步函数,不是同步函数devtools无法追踪状态的改变,
- 当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.
- 通常不要再mutation中进行异步的操作,若要执行异步操作action进行传递
小结:修改状态的两种方式
- 没有异步操作直接提交mutation
- 存在异步操作先分发action->再提交mutation
actions
类型:
{ [type: string]: Function }
在 store 上注册 action。处理函数总是接受
context
作为第一个参数(默认会传入),payload
作为第二个参数(可选)。context
对象包含以下属性:1
2
3
4
5
6
7
8{
state, // 等同于 `store.state`,若在模块中则为局部状态
rootState, // 等同于 `store.state`,只存在于模块中
commit, // 等同于 `store.commit`
dispatch, // 等同于 `store.dispatch`
getters, // 等同于 `store.getters`
rootGetters // 等同于 `store.getters`,只存在于模块中
}同时如果有第二个参数
payload
的话也能够接收。注册action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit
很多次的时候):
1 | actions: { |
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
action的分发:
- action中通过
store.dispatch
()分发
1
store.dispatch('increment')
- store.dispatch()支持同样的载荷方式(参数的传递)和对象方式进行分发
1
2
3
4
5
6
7
8
9
10// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})- 在组件中分发action一定要先在根节点注入store
- action中通过
组合action
异步操作执行中在执行成功或失败后进行相应操作和获取返回值问题的解决方案:
- 1.promise解决,
- 2.异步函数解决
action通常是异步操作,涉及到异步操作涉及到返回值的获取问题,异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolve或reject,以此来判断action执行的时期,并在异步不同的时期进行其他操作,通过组合action来实现更加复杂的异步流程
store.dispatch
可以处理被触发的 action 的处理函数返回的 Promise,并且store.dispatch
仍旧返回 Promise:- action中返回promise对象,将异步操作包裹在promise对象中
store.dispatch('actionA')
分发了action中的事件类型,该事件函数就会执行,执行之后就会返回一个promise对象(也就是actionA回调函数中返回的promise对象),在该promise对象后面通过then()和catch()方法就就可以得到异步操作执行的执行结果
1
2
3
4
5
6
7
8
9
10
11actions: {
actionA中的事件类型和回调函数,只有在分发dispatch的时候才会执行并返回事件类型对应的事件函数执行后的promise对象,
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14分发之后返回actionA事件函数的promise对象,通过then()和catch()方法就就可以得到异步操作执行结果
store.dispatch('actionA').then(() => {
// ...
})
也可以在另外的action中直接调用
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}- 通过promise可以解决异步操作返回值问题同样可以通过异步函数(async/await)解决组合的action乳腺
- 异步函数中添加async,await只能在async函数中出现
- await操作一直会等到该异步操作执行完毕拿到返回值才会执行下一步操作
1
2
3
4
5
6
7
8
9
10
11// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}一个
store.dispatch
在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。- 注意action中可以通过参数结构的方式来获取context中传递过来的属性值
{commit,state}=context
测试代码
1 | index.js |
getters
- 类型:
{ [key: string]: Function }
在 store 上注册 getter,getter 方法接受以下参数:
1 | state, // 如果在模块中定义则为模块的局部状态 |
注册的 getter 暴露为 store.getters
。
应用场景:从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:如果有多个组件需要用到此属性在每个组件的computed中都通过$store.state来计算不是很理想,
- 解决方案:通过store中的getters(类似于组件中的计算属性computed)就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
- 调用的使用直接$store.getters.属性名称
- 注意:这里和计算属性一样getters中存储的都是属性,不是方法
getter(state,getters)
- 参数1:默认传入state,会自动传入
- 参数2:可选,接受其他的getter作为第二参数
- 可以在任何组件中直接使用它
1
2
3
4
5
6
7
8
9
10getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
使用
store.getters.doneTodosCount /访问方式
通过属性进行访问
- Getter 会暴露为
store.getters
对象,你可以以属性的形式访问这些值:$store.getters.属性名称**
- 可以很容易的在任何组件中使用
1
2
3
4
5computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}- Getter 会暴露为
通过方法访问(实现传参)
- 通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16getters: {
// ...
getTodoById: (state) => {
//箭头函数
(id) => {
return state.todos.find(todo => todo.id === id)
}
}
}
//完整写法:
getMoreAge: function (state) {
// 箭头函数
return function (age) {
return state.students.filter(s => s.age > age)
}
}1
store.getters.getTodoById(2)访问
modules
- 类型:
Object
- 包含了子模块的对象,会被合并到 store
1 | { |
与根模块的选项一样,每个模块也包含 state
和 mutations
选项。模块的状态使用 key 关联到 store 的根状态。模块的 mutation 和 getter 只会接收 module 的局部状态作为第一个参数,而不是根状态,并且模块 action 的 context.state
同样指向局部状态。
- 使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。Vuex 允许我们将 store 分割成模块(module)。
- 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
1 | const moduleA = { |
子模块中定义的所有的state、mutation、action、getter中的属性都会合并到根store中对应的state、mutation、action、getter中访问都需要到根store中访问
- 访问子模块的state:
store.state.子模块.属性名
.在根store中在访问子模块中的state中的状态 - 访问子模块的gettters:
store.getters.属性名
子模块中getters合并 - 访问子模块中的motations和action:直接通过根store调用对应的 方法即可
- 访问子模块的state:
模块的局部状态
- 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
- 对于模块内部的 action,局部状态通过
context.state
暴露出来,根节点状态则为context.rootState
:context包括的内容如下
1
2
3
4
5
6
7
8{
state, // 等同于 `store.state`,若在模块中则为局部状态
rootState, // 等同于 `store.state`,只存在于模块中
commit, // 等同于 `store.commit`
dispatch, // 等同于 `store.dispatch`
getters, // 等同于 `store.getters`
rootGetters // 等同于 `store.getters`,只存在于模块中
}- 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
321 const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
2 actions: {
//参数为context
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
3 getters: {
//根节点作为第三个参数暴露出来,第二个参数为子模块汇总对应的getter
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}命名空间
- 默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
- 可以通过添加
namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。详细见官方文档
Vuex.Store 实例属性
state
类型:
Object
根状态,只读
getters
类型:
Object
暴露出注册的 getter,只读
Vuex.Store 实例方法
commit
store.commit(type: string, payload?: any, options?: Object)
- 参数1:type的String名称
- 参数2:可选,payload可以是变量/对象
store.commit(mutation: Object, options?: Object)
- 参数1:mutation
dispatch
store.dispatch(type: string, payload?: any, options?: Object): Promise
store.dispatch(action: Object, options?: Object): Promise
通常支持参数的传递和对象风格的提交和commit一样
11.2.4 Vuex模块化管理
补充promise的使用
promise介绍
- promise是异步编程的一种解决方案,可以获取异步函数中的返回值(对异步事件封装的方式)
- 处理异步事件的场景
- ajax发送网络请求(ajax本来执行本身就是异步任务)
- 文件的读取,数据库的获取等异步函数执行
- 定时器异步操作
- 问题,当网络请求嵌套调用就会出现回调地狱的问题,
- 代码难看且不容易维护
- 通过promise的方式来解决
1 | $.ajax({ |
定时器是异步事件,这里通过定时器模拟解决回调地狱的问题
小结:
- 插件都需要导入在安装Vue.use(插件)才能使用
- router和vuex都是插件
promise的基本使用
- 格式:
1 | new Promise( function(resolve, reject) {...} /* executor */ ); |
有异步操作时, 就可以给异步操作包装一个Promise,promise有三种状态
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。会自动调用resolve回调函数,并调用then()方法
- rejected: 意味着操作失败。会自动调用reject回调函数,并通过catch()方法获取结果
pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的
then
方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。状态发生变化会自动执行相应的回调函数,
通过回调函数中传递参数的方式获取执行结果
promise是一个构造函数,通过new创建,new -> 构造函数(1.保存了一些状态信息 2.执行传入的函数)
promise属性
Promise.length:length属性,其值总是为 1 (构造器参数的数目).
-
表示
Promise
构造器的原型.
promise方法
Promise.all(iterable)
:这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功; 败的promise对象的错误信息作为它的失败错误信息。Promise.race(iterable)
:当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
异步函数执行解析
1
2
3
4
5
6
7
8new Promise很明显是创建一个Promise对象
小括号中((resolve, reject) => {})也很明显就是一个函数,而且我们这里用的是之前刚刚学习过的箭头函数。
但是resolve, reject它们是什么呢?
我们先知道一个事实:在创建Promise时,传入的这个箭头函数是固定的(一般我们都会这样写)
resolve和reject它们两个也是函数,通常情况下,我们会根据请求数据的成功和失败来决定调用哪一个。
成功还是失败?
如果是成功的,那么通常我们会调用resolve(messsage),这个时候,我们后续的then会被回调。
如果是失败的,那么通常我们会调用reject(error),这个时候,我们后续的catch会被回调代码
- 方式1:
- then(()=>{}):参数是箭头函数,resolve执行调用
- catch(()=>{}):reject执行调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 2.promise一般使用在异步操作,对异步操作进行封装
new Promise((resolve, reject) => {
// 包裹异步执行函数
setTimeout(() => {
// 成功的时候执行,通过给回调函数传递参数的方式获取执行结果
resolve('hello world');
// 失败的时候执行
reject('err message')
})
}).then((result) => {
// 通过形参接收resolve回调函数通过实参传过来的执行结果
console.log(result);
}).catch((err) => {
console.log(err);
})- 方式2:
- then(()=>{},()=>{})参数是两个箭头函数,第一个参数表示传入成功时调用,第二个参数传入失败时调用
1
2
3
4
5
6
7
8
9
10new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello World');
reject('err message')
}, 1000)
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})- 方式1:
- promise解决回调地狱问题
1 | // 3.回调地狱问题的解决 |
链式编程
链式编程:因为
Promise.prototype.then
和Promise.prototype.catch
方法返回promise 对象, 所以它们可以被链式调用。promise中resolve的三种书写方式,注意前面有return 关键字
- new promise中resolve()
- new Promise只有resolve参数且没有异步执行操作时====Promise.resolve(参数)
- Promise.resolve()===直接写参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58// 1.链式编程完整写法
new Promise((resolve) => {
// 异步执行
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then((data) => {
console.log(data, '第1层执行的结果');
// 第二次发送请求
return new Promise((resolve) => {
resolve(data + '111')
})
}).then((data) => {
console.log(data, '第2层执行的结果')
// 第三层发送请求
return new Promise((resolve) => {
resolve(data + '222')
})
}).then((data) => {
console.log(data, '第三次执行结果')
})
// 2.new Promise中只有resolve一个参数,并且在第二层开始就没有异步执行操作,Promise.resolve()代替
// new Promise(resolve => resolve(结果))简写
new Promise((resolve) => {
// 异步执行
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then((data) => {
console.log(data, '第1层执行的结果');
// 第二次发送请求
return Promise.resolve(data + '111')
}).then((data) => {
console.log(data, '第2层执行的结果')
// 第三层发送请求
return Promise.resolve(data + '222')
}).then((data) => {
console.log(data, '第3次执行结果')
})
// 3.省略掉Promise.resolve,内部会处理成promise对象返回
new Promise((resolve) => {
// 异步执行
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then((data) => {
console.log(data, '第1层执行的结果');
// 第二次发送请求
return data + '111'
}).then((data) => {
console.log(data, '第2层执行的结果')
// 第三层发送请求
return data + '222'
}).then((data) => {
console.log(data, '第3次执行结果')
})抛出异常的两种方式,注意抛出异常后面都需要链式编程调用catch()方法
- reject()抛出异常,
- reject
- return Promise.reject()
- throw ‘异常信息’:手动抛出异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39// 4.出现catch的情况, reject抛出
new Promise((resolve) => {
// 异步执行
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then((data) => {
console.log(data, '第1层执行的结果');
// 第二次发送请求,抛出异常
return Promise.reject('err message')
}).then((data) => {
console.log(data, '第2层执行的结果')
// 第三层发送请求
return data + '222'
}).then((data) => {
console.log(data, '第3次执行结果')
}).catch((err) => {
console.log(err);
})
// 5.出现catch的情况,throw手动抛出
new Promise((resolve) => {
// 异步执行
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then((data) => {
console.log(data, '第1层执行的结果');
// 第二次发送请求,注意这里没有return 关键字
throw 'err message throw'
}).then((data) => {
console.log(data, '第2层执行的结果')
// 第三层发送请求
return data + '222'
}).then((data) => {
console.log(data, '第3次执行结果')
}).catch((err) => {
console.log(err);
})- reject()抛出异常,
Promise.all()
Promise.all()
和Promise.race()
是并行运行异步操作的两个组合式工具。Promise.all(iterable)
方法子promise返回值
- 该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;
- 一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。
1 | // 1.成功情况:子promise中的返回值(对象/字符串)保存在.then方法接收的数组参数中, |
本文链接: https://sparkparis.github.io/2020/05/13/Vue%E7%AC%94%E8%AE%B08/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!