文章目录
- 2018年12月30日 - 上午
- 第141个视频 - tabbar文件路径引用的问题
- 第142个视频 - Promise的介绍和基本使用
- 第142个视频 - Promise的三种状态和另外处理方式
- 第143个视频 - Promise的链式调用
- 第144个视频 - Promise的all方法的使用
- 第145个视频 - vuex概念和作用解析
- 第146个视频 - 单界面到多界面状态管理切换
- 2018年12月30日 - 下午
- 第147个视频 - devtools和mutations
- 第148个视频 - state单一状态树的理解
- 第149个视频 - getters的使用详解
- 第150个视频 - mutations的携带参数
- 第151个视频 - mutation的提交风格
- 第152个视频 - 数据的响应式原理
- 第153个视频 - mutations的类型常量
- 第154个视频 - vuex-actions的使用详解
- 第156个视频 - vuex-modules的使用详解
- 第157个视频 - vuex-store文件夹的目录组织
2018年12月30日 - 上午
第141个视频 - tabbar文件路径引用的问题
1 - 上节课路径的问题
上次的代码当中路径没有修改,跑不起来。
2 - 路径起别名
路径层级太深,很麻烦。
在开发当中一般就是要避免这种情况的。
就是要给某些文件夹启别名。
这个也是属于webpack的配置。
以前我们说过,webpack.base.conf.js当中的resolve就是为了解决和路径相关的问题的。
extensions配置的意思就是可以省略某些文件的后缀的。
alias配置就是别名的意思。
现在已经有了一个别名,就是src文件夹的别名是@
。
如果我们把所有的路径都替换成为@
开头的,就可以了。
一般情况下,在项目开发当中,还会搞其他的文件夹的别名。
我们一般就是像下面一样起别名:
3 - 别名需要注意的点
如果你已经起了别名。在vue文件当中的script标签内,import的情况下,使用路径别名是没有问题。
但是在template当中,不是通过import这种方式的,那些资源路径,写上别名还是找不到。
这些路径虽然已经起了别名,但是还是找不到的。
必须都加上波浪符号。
4 - 小问题
我们通过上面的修改之后,重跑了项目,但是出错了。
这个原因是因为这里:
在脚手架2当中,已经配置了@表示src的情况下,配置其他路径的时候,不能够使用@。
在脚手架3当中,是可以的。
我们修改成为下面的配置:
运行效果是正常的:
第142个视频 - Promise的介绍和基本使用
1 - ppt
2 - 同步编程过程
先执行某一段代码,再执行某一段代码。
3 - 异步编程过程
比如网络请求,同步不适合。
用户发送网络请求,网络请求耗时,会阻塞。阻塞用户体验不好。
所以,网络请求,一般都是异步编程过程。
一个网络请求,开启异步任务。程序继续执行。
网络请求数据回来,会通过网络请求的回调函数,拿到网络请求的数据。
- 网络请求都会开启异步任务。
这是简单的网络请求的过程。
如果网络请求非常复杂,会出现回调地狱。
4 - 回调地狱
回调函数里面又需要回调,多层嵌套回调,这个就是回调地狱。
为了希望使用更加优雅的方式,来进行异步操作,就是使用Promise。
Promise就是对异步操作进行优雅封装。
Promise是一个类。
5 - 为了了解Promise,我们模拟异步操作
Promise是一个类。
我们需要new Promise()来创建对象。
这个时候是需要传入参数的。
我们可以看源码:
创建对象的时候,就是要执行上面的操作。
-
对象这个参数本身,是一个函数。
-
这个函数本身包含两个参数,一个叫做resolve,一个叫做reject。
-
resolve和reject本身也是函数。
new Promise((resolve, reject) => {
});
我们以后所有的异步操作,都放到这个箭头函数的代码块当中。
现在使用Promise之后,逻辑变得很清晰了。即使有十次,代码也很清晰的。
一种是嵌套结构,一种是链式结构。
6 - Promise到底是怎么运行的呢?
什么情况下,会用到Promise呢?
当我们进行异步操作的时候。
一旦进行异步操作,就塞到Promise对象当中,调用resolve()和then()就可以了。
使用Promise对异步操作进行封装。
我们看看Promise的源码:
在接口当中有new的方法。
在我们通过new创建Promise对象的时候,就是执行上面的代码。
当我们创建完成Promise对象的时候,就会回调executor函数。传入两个参数,resolve和reject,这两个也是函数。
在我们通过new创建Promise对象的时候,会执行构造函数。
- 保存了一些状态信息。
- 执行传入的函数。
- 执行函数的时候,传入两个参数,resolve和reject。
- 之后,我们就可以做异步操作了。
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('冀蕊')
reject('错误信息')
},1000)
}).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
})
我们在上面的代码当中setTimeout模拟的就是网络请求。
- 如果网络请求成功,通过resolve来传递请求成功的数据,通过then函数来处理。
- 如果网络请求失败,通过reject来传递失败的信息,通过catch函数来捕获。
第142个视频 - Promise的三种状态和另外处理方式
1 - PPT
2 - promise的过程
-
async operation是异步操作的意思
-
wrapped into是包裹的意思
-
意思就是异步操作包裹在Promise当中
- Pending是等待的状态
- Fulfilled是满足的意思,意思就是成功了。满足状态就是调用resolve(val)。执行then。
- rejected就是拒绝失败的意思,一般会有错误信息,失败状态就是调用reject(error),执行catch。
3 - promise的另外的处理方式
我们查看then函数的代码
发现then函数这里可以传入两个参数。
这个参数都是函数。
第一个参数会在fulfilled的时候执行,也就是成功的时候。
第二个参数会在rejected的时候执行,也就是失败的时候。
因此,产生了promise的另外的写法,另外的处理方式:
第143个视频 - Promise的链式调用
1 - ppt
then当中,我们处理完成之后,可以继续返回promise对象。
这样,就可以实现链式调用了。
2 - 链式调用的实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log('姜烨烨');
console.log('姜烨烨');
console.log('姜烨烨');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
}).then(() => {
console.log('陈汝莹');
console.log('陈汝莹');
console.log('陈汝莹');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
}).then(() => {
console.log('陈菁华');
console.log('陈菁华');
console.log('陈菁华');
});
</script>
</body>
</html>
3 - 需求 - 不进行异步操作,但是想链式编程
- 第一次网络请求,结果是aaa,自己处理(10行)
- 处理:给结果aaa拼接111,再自己处理(10行)
- 处理:给结果aaa111拼接222,自己处理
这个需求的意思,就是我不想进行异步操作,但是我就是想链式编程的。
我们先实现上面的需求,上面的需求,实现的代码,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa');
}, 1000);
}).then((res) => {
console.log(res, '第1层的10行处理代码');
return new Promise(resolve => {
resolve(res + '111');
});
}).then(res => {
console.log(res, '第2层的10行处理代码');
return new Promise(resolve => {
resolve(res + '222');
});
}).then(res => {
console.log(res + '第3层的10行处理代码');
});
</script>
</body>
</html>
上面就是,我为了实现一个复杂的需求,我通过使用promise来进行了分布处理。
第一次省略 - 省略了new Promise((resolve) => {resolve()})
如果是这种需求场景下,我们可以简写的。简写的原来如下所示:
上面的写法就是:把第一层的处理结果,通过return Promise.resolve传递给第二层进行处理。
第二次省略 - 省略了Promise.resolve()
但是我觉得这种写法,还是不够简洁。
这样写的时候,内部会自动进行Promise的包装,并且会自动调用resolve()的。
如果某一层出现错误怎么简写
第一次简写:Promise.reject()
第二次简写:throw
第144个视频 - Promise的all方法的使用
1 - 自己实现Promise
自己来实现一个最简单的Promise。
这个王红元没有讲。
2 - 需求场景
我们有多个网络请求,网络请求1和网络请求2。
我们普通情况下,两个网络请求是分别请求的。
但是有一种情况,我们有一个需求,这个需求是需要两个网络请求,只有两个网络请求的数据都拿到的时候,需求才可以实现。
如果我们是使用ajax来发送请求,我们就需要自己手动判断一下,两个网络请求都拿到结果了,然后进行结果处理。
可能的处理方式,是下面的这个样子。
let isResult1 = false
let isResult2 = false
$ajax({
url: '',
success: function () {
console.log('第1个网络请求的结果');
isResult1 = true
handlerResult()
}
})
$ajax({
url: '',
success: function () {
console.log('第1个网络请求的结果');
isResult1 = true
handlerResult()
}
})
function handlerResult(){
if (isResult1 && isResult2){
//处理两次网络请求的结果,进行下一步操作
}
}
3 - promise包装两个异步请求
Promise.all()当中是可以传入一个数组,就是一个可迭代对象。
在all()方法内部,会自动判断,两个网络请求都获取到结果的时候,再去then()当中去执行代码。
<script>
//Promise.all()当中传入iterator可迭代对象
Promise.all([
new Promise((resolve, reject) => {
$ajax({
url: 'url1',
success: function () {
resolve(data)
}
})
}),
new Promise((resolve, reject) => {
$ajax({
url: 'url2',
success: function () {
resolve(data)
}
})
})
]).then(results => {
console.log(results[0]);// url1
console.log(results[1]);// url2
})
</script>
第145个视频 - vuex概念和作用解析
1 - ppt
- vuex就是状态管理工具
- 什么是状态管理呢?
- 多个组件有一个变量,需要共享。这个变量放到哪里呢?
- 这些组件之间,不是父子关系,很难联系上。
- 我们搞个大管家,保存所有需要共享的变量,所有组件都可以朝它要。
- 这就是所谓的集中式状态管理。
- 使用vuex管理状态,这个还是响应式的。
- 响应式就是数据改变,页面改变。
- 这就是状态管理。
- 我们可以把这个大管家,想象成为单例对象。
2 - 我们自己实现状态管理
如果我们自己来实现这个状态管理器,可以实现吗?
我们可以通过Vue的原型来实现,类似我们在vue-router时候使用 r o u t e r 和 router和 router和route这种东西。
但是我们上面创建的shareObj并没有放到vue的响应式系统当中。
是无法做到响应式的。
如果你想要封装响应式,又过于麻烦了。
所以vue给你提供了一个vuex。
3 - 开发中什么东西放到vuex中?
只有多个组件需要共享的,放到vuex中。
父组件有个状态,子组件想要用,还是用父子组件通信,不要放到vuex中。
- 用户的登录状态
- 你去服务器请求数据,有一些数据是只针对登录用户的。
- 这个时候请求服务器数据,请求参数必须携带token。
- 多个界面请求数据的时候,都需要携带token。
- 这个token来自于用户登录成功后,服务器返回给用户的。
- 我们可以将token放在vuex当中。
- 用户名称
- 用户头像
- 用户地理位置信息
- 商品的收藏
- 购物车中的物品
第146个视频 - 单界面到多界面状态管理切换
1 - PPT
2 - 讲一下上面的图
state:状态的意思。状态是用变量保存的。在vue中,变量一般保存在data中。state相当于data中的变量。
state是在view中显示的。
view当中可以产生actions,比如用户发生了点击。
actions反过来会修改state,例如点击的事件函数当中修改data当中的变量。
写代码的话,就是这样的:
3 - 多页面管理
我们现在是要进行多页面状态管理的。
我们新建一个组件叫做HelloVue.vue。
在这个页面当中也展示计数器。
然后注册到我们的App.vue当中。
跑动项目报错:
就是因为我们HelloVue当中没有counter。
但是HelloVue是App的子组件。
我们可以在HelloVue当中定义一个props用来接收父组件传过来的属性。
然后我们在父组件App当中给HelloVue标签 上面绑定属性:
如果两个组件没有直接的父子组件的关系呢?
不希望父子组件通信的方式呢,就需要用vuex。
vuex就是相当于大管家。所有组件的状态都交给vuex。
现在我们有一个counter的状态,想要共享的。
这个可以放到vuex当中。
vuex是一个插件。
我们装的vue原生当中是不包含vuex的。
第一步,安装插件。这个插件是运行时依赖。我们要通过npm安装。
npm install vuex@3.0.1 --save
第二步,我们要为vuex单独创建一个文件夹。我们最好不要在main.js当中写vuex的代码。
之前我们为vue-router创建的文件夹,叫做router。
我们为vuex创建的文件夹,我们一般叫做store。
官网上也是叫做store的。
第三步,在store当中创建index.js。
导入vue和vuex。
- 安装插件,使用Vue.use(Vuex),执行这个Vue.use,内部会执行插件的install方法。
- 创建store对象,在vuex当中有一个store属性,这个store也是一个类,所以,我们是创建store对象。
- 导出store对象,export default store。
- 在main.js当中导入store对象,然后挂载到vue实例上面。放到vue实例当中,各个组件才会拿到store对象,才会给我们的vue.prototype设置 s t o r e 属 性 , 然 后 每 个 组 件 都 可 以 通 过 store属性,然后每个组件都可以通过 store属性,然后每个组件都可以通过store来获取保存的状态了。
4 - store对象当中放置什么东西呢?
第一个是state,state是一个对象。
第二个是mutations,是一个对象。
第三个就是actions,是一个对象。
第四个就是getters,是一个对象。
第五个就是modules,是一个对象。
5 - state对象
state就是用来保存状态的。我们可以在state当中定义一个状态叫做counter。
我们现在是想要在HelloVue当中使用counter状态。
一旦 store对象在vue实例挂载之后,所有组件都有了$store了。
在App.vue当中,我们在组建内部就不需要维护一个counter变量了,也可以直接使用store对象当中的state当中的counter对象。
现在我们希望点击的时候,counter能够发生改变,但是现在我们点击的时候,直接报错了:
报错的原因,如下图所示:
我们现在点击的时候,要改变的是store当中的state当中的counter。
我们怎么修改呢?
第一种方法,我们直接修改:
得到的效果,如下图所示:
这个效果是正常的。
但是这种修改状态的方法,不好。
为什么不好。
官方不建议这样来进行修改。
官方是建议按照固定的格式来进行修改的。
如果你不按照官方的规定来修改,好像也是可以,但是代码调试的时候,会出现问题的。
6 - vuex状态管理图例
我们看上面的图,虚线部分是属于vuex的。
vue components就是vue的组件。
组件当中能够引用vuex当中的state。
现在如果想要修改state的时候,不要从vue components直接修改。
- 要先调用一个dispatch,发布一个actions
- 在actions当中提交comit到mutations
- 然后通过mutations,mutate修改state
7 - 为什么官方要求这样修改state呢?
上面的图中有一个devtools,这是vue官方提供的浏览器的插件。
这是为vue开发提供的一个插件。
这个插件,可以帮你记录,每次你修改state的状态。
为什么devtools能够记录呢?
我们现在vuex当中管理的状态都是多个页面共享的状态。
修改状态的时候,可能是多个页面都可能修改状态的。
你很难跟踪到到底是哪个页面修改的状态。
不能跟踪。
这样你查错的时候,非常难查。
vue官方提供的devtools就可以记录每一次修改state的是哪一个组件。
这样如果出错,就能够定位到组件。
但是,如果你绕过了mutation的环节,你直接从组件修改state,这样devtools就跟踪不到了。
只要是修改state状态,一定要经过mutations,这样devtools才能够跟踪记录,这样调试才容易。
所以结论是:可以从组件当中直接修改state,但是不要这样做,不要这样做。
8 - 图中修改state经过mutations理解,为什么要经过actions呢?
组件修改state,可以隔开actions,直接通过mutations,来修改state,这个是可以的。这个是允许的。
那这个actions是用来干啥的呢?
当你是修改mutations的时候,你有异步操作,不要在mutations当中做。mutations当中推荐的操作就是同步操作。
如果你在mutations当中做了异步操作,这个devtools就跟踪不到了。
如果你有异步操作,你就在actions当中做,等你异步操作完了,你再提交到mutations当中去修改state。
所以,actions是用来做异步操作的。
什么情况下会进行异步操作呢?
就是发送网络请求的时候,所以在官方的图当中,actions后面是连接backend api。
- 后端:backend
- 前端:frontend
2018年12月30日 - 下午
第147个视频 - devtools和mutations
1 - 安装devtools
如上图的位置,出现了vue,就表示安装成功了。
点击vue,就是下面的界面:
这个东西,就是做调试用的。
也可以在vue当中看到当前项目的结构的:
这个插件是可以用于调试整个程序的。
当我们的程序比较复杂,有个商品列表,看看商品列表当中的数据,对不对,就可以使用devtools。
之后讲项目的时候,可以使用这个工具。
2 - 切换devtools的vuex
这里会显示你定义的所有数据。
如果你通过mutations修改state,就会在上方显示跟踪。
像是我们直接修改的时候,这个工具就不会生效。
3 - mutations怎么用
我们可以在store文件夹的index.js当中的store对象中的mutations对象中,定义很多方法。
定义的方法,都是修改state的方法。
这些方法都是默认有一个参数的,就是state的。不需要你自己通过this什么去拿state对象中的数据。
我们直接使用mutations方法参数当中的state,就可以了。
我们这样写mutations方法来修改state当中的counter:
在组件当中,我们怎么调用mutations当中的方法呢?
通过下面图中的步骤,在组件当中提交mutations方法:
我们看一下效果:
效果就是上面图的样子。
上面图的右边,我们可以点击filter mutations这个跟踪路径当中每个元素,查看每一次修改的状态。
4 - 这个就是PPT中对我们的简单案例的描述
第148个视频 - state单一状态树的理解
前面我们讲了vuex的基本使用,我们现在讲讲vuex的核心概念
1 - 核心概念
第一个比较核心的概念就是state,这里会引出来state的单一状态树,后面说。
第二个就是getter,类似我们的computed,计算属性。
第三个就是mutations。
第四个就是actions
第五个就是modules,就是划分模块,根据模块进行相关数据保存。
vuex除了基本使用,还有五个核心概念。我们一一介绍。
2 - state概念
state就是用来放置状态信息的。非常简单。
但是state当中,vuex讲state的时候,讲到了一个概念,就是单一状态树。
这个名字高大上,什么意思呢?
我们直接翻译过来,单一状态树,就是单一数据源。
我们国家中,每个人的个人信息都是记录下来的。你的档案、社保记录、公积金、结婚、户口、医疗、房产都需要记录。
目前我们国家都是分类在不同部门不同系统进行记录的。
如果很多信息分布不同系统,好处就是每个信息划分比较清晰,还有一个就是比较数据安全。
如果某一天系统被入侵了,信息暴露了,只有这一个系统,其他系统相对安全。这个是优势。
划分清晰,可以提供比较多的就业岗位。
但是这种信息管理,有很多缺点,比如说你要办理某个业务,比如说你要入户,你会发现,你要提交很多信息,你要去不同部门打印不同信息。
然后每个地方都需要盖章,然后你最终把所有信息提交某个地方,你就可以入户了。
信息的分布的坏处,办理业务的时候,非常麻烦,而且低效。
在我们国家肯定有一个系统,可以查到一个人所有的信息的。
比如公积金,你杭州工作,然后深圳,然后去北京,公积金账户不是同一个。你就要到处跑。非常麻烦。
我们国家现在正在不断完善这些系统。比如公积金系统。
举这个例子告诉大家,跟我们开发比较类似。
vue在设计vuex的时候,提出来单一状态树的概念。
我们当前创建了一个store对象。我们能够创建多个吗?创建多个store对象然后分类存储信息。
vue的单一状态树的意思就是,别这样,你最好是使用一个store对象。
你的所有的状态信息,统一放在一个store对象,就可以了,这个就是单一状态树的概念内涵。
不推荐你在项目当中搞很多的store。
单一状态树,你能够非常直接找到你的状态信息,方便维护和调试。
在一个项目当中,不要建多个store,永远用一个store,对应每个组件当中的$store。
第149个视频 - getters的使用详解
vuex有五个核心:state、getters、mutations、actions、modules。
getters在开发当中,也是用得比较多,类似于组件当中的计算属性computed。
计算属性是在什么条件下使用呢?
当我们某个数据必须经过一系列变化,再让界面使用的时候,就可以通过计算属性了。
getters也是一样。
比如说,我们想要从state当中获取状态counter的时候,我们希望对counter经过某种变化之后再进行获取。
这个时候,就可以使用getters。
1 - 演示一下getters
我们在getters当中也是写方法,方法也是有个默认的参数,就是state对象的。
在store对象当中,我们就像上面的写法,写getters,在组件当中,我们这样使用:
效果如下:
这个就是getters最基本的使用。
2 - ppt中演示的getters基本使用
我们在代码当中演练一下ppt当中的案例:
定义了这些state,某个组件当中想要获取年龄大于20岁的学生。
我们可以怎么做呢?
第一种做法。
我们在组件当中搞一个计算属性。
filter函数的作用是:针对students数组,会拿到数组当中的每个对象,就是s,返回值是一个表达式,如果表达式是false的时候,就会忽略掉,如果表示是true的时候,就会将这个对象,添加到新的数组当中进行返回。
我们可以再优化一下,使用箭头函数的简写 形式:
效果如下:
这个就是一种实现方法。
但是假如说,其他组件当中也想要获取年龄大于20岁的学生呢?就也需要在组件当中写计算属性。
所以,这个时候,我们最好的做法,是写到getters当中。
第二种实现方法,就是使用getters。
我们组件当中直接使用就可以了:
上面的就是ppt当中讲解的getters基本使用的案例,我们又用代码写了一遍。
3 - getter的传参
需求场景是:不仅仅是想要获取年龄大于20岁的学生,还想要获取年龄大于20岁学生的个数。
这个时候怎么办呢?
我们可以直接在组件当中进行获取。
但是我们的需求要求,把获取个数的这个方式,定义成为一个store的getter。
我们怎么做呢?
我们可以通过上面的方式,直接实现。
但是这种写法,有点啰嗦。
我们可以通过在getters当中进行传参,传入一个getters对象。
4 - 更复杂的东西
需求场景:想要获取年龄大于age的学生,age是别人用getters的时候,传进来的。
这个时候,我们怎么写getters呢?
不要再getters的参数上想办法了。
我们的getters可以返回一个函数给调用方。
这个调用处就是一个函数,就可以进行传参了。
如果你的getters想要别人传参的情况下,你的getters就要返回一个函数。
当然了上面的代码, 你也可以用箭头函数来进行优化得更加简洁:
第150个视频 - mutations的携带参数
1 - PPT
mutations当中定义的函数,可以看成两部分。一个叫做字符串的事件类型,一个叫做回调函数。
知道这个东西有什么用呢?待会就会用到的。
函数当中的第一个参数就是state对象。
通过mutation更新就是根据$store来commit事件类型了。
2 - commit mutations的时候传入参数
我们希望点击按钮,给counter这个状态加5和加10。
我们在组件当中定义一个方法:
这个方法是可以传入参数的。
所以,我们在我们的store对象当中,去mutations当中去定义一个方法。
效果是正常的:
这个也就是说明,我们在组件当中通过$store对象进行commit的时候,我们就可以通过这种方式来实现就可以了。
3 - commit mutations的时候传入多个参数
我们在组件当中添加一个按钮,如果点击按钮的时候,就往store的state当中增加一个学生对象。
这种情况,就不能够传入一个参数了,就是传入一个对象。
1、组件内的按钮:
2、组件内的方法:
3、store对象当中的mutations中的方法:
这个如果有多个参数需要传递的时候,我们就放到一个对象当中进行传递就可以了。
在store的mutations当中,我们传入的参数,这个参数叫做payload,叫做载荷。
4 - ppt当中的讲解
第151个视频 - mutation的提交风格
之前的提交风格,就是通过$store的commit方法,就可以进行提交了。
vuex还提供了另外的提交风格,就是可以把所有提交的东西,都放置在一个对象当中。
1 - 普通的提交风格
2 - 特殊的提交风格
我们使用这种风格去提交的时候,在store的mutations的方法中,就不像是普通提交风格中,直接拿到参数了。
而是拿到一个对象。
在mutations当中的方法中,我们要执行的操作,就需要通过对象来实现:
3 - ppt中对mutation提交风格的表述
第152个视频 - 数据的响应式原理
1 - ppt
store对象的state响应式,是有一定的要求的。
第一个规则:提前在store当中要初始化的数据,初始化好。
在store/index.js当中store中定义一个info:
在App.vue当中,template当中展示store当中的state信息:
在App.vue当中,写一个监听点击的函数:
在store/index.js中mutations中写一个方法:
效果:
我们创建state的时候,初始化了一个info,初始化了之后,就会加入到vue的响应式系统当中,vue就会通过一个watcher的东西,就可以观察到这些数据的变化,然后就可以响应式渲染了。
每一个info的属性都有一个Dep,通过Dep对应好多watcher。
我们在info当中有3个属性,每个属性都对应一个Dep对象,Dep是一个观察者模式,可以监听属性的数据变化,会看一下哪些地方是需要根据数据变化发生更新的。Dep里面有一个数据,每个数组都有一个watcher,每个watcher对应页面上的内容。
记住:这些属性一开始定义到state当中,初始化好了,就会被加入到响应式系统当中。
反例
假如,我们在上面,通过按钮,修改我们的state,给info对象,新增一个address属性和值,这个不会加入到响应式系统当中。
需求
在开发当中,我们给state新增一个属性。我们确实是想要它加入到响应式系统当中的。
我们怎么办呢?
有没有办法解决呢?
之前我们讲数组的时候,讲到了哪些方法是响应式的,讲了push方法,pop方法,shift方法,unshift方法,splice方法,sort方法。
看上面的图,我们通过Vue.set,就可以加入到响应式中。
在state当中,也是一样的。
通过Vue.set追加state的时候,就可以加入到响应式系统中了。
除了vue.set还有其他吗?
我们不要给state当中的info对象追加属性。
我们想要删除state当中info对象的属性。
效果如下:
我们通过devtools可以看到age被删除了,但是页面,并没有响应式。
所以,使用delete删除对象属性,也做不到响应式。
对应的,有一个Vue.delete方法。
这样写了之后,就可以使删除操作变成响应式了。
总结
1 - 原来定义的属性,可以响应式
2 - 追加对象属性,使用vue.set
3 - 删除对象属性,使用vue.delete
第153个视频 - mutations的类型常量
1 - 讲解
我们当前的mutations中已经定义了很多个方法。
我们之前在mutations中定义了一个方法,然后在组件的methods中通过this.$store.commit的时候,就是拷贝mutations中方法的名字。
如上面的图所示,但是有的人不喜欢拷贝,有的人就喜欢手写。
vuex也考虑了这个问题。
我们使用常量来解决这些问题。
第一,搞一个文件mutations-types.js,然后在文件中,这样写:
然后,我们需要在组件中,进行mutations提交的时候,我们导入mutations-types.js当中的常量。
我们在上面是使用export const来导出了一个常量。
那么我们在组件中,应该用什么方式导入呢?
import INCREMENT from ./store/mutations-types
这种方式可以吗?
是不可以的。
什么情况下才可以使用import from
这种方式呢?
这种方式就是相当于自己在起名字。
只有导出的方式是export default的时候。
普通导出的方式,在导入的时候,只能够写大括号。就是下面的这种方式。
这样,在methods当中,提交mutations的时候,写方法事件的时候,我们就可以使用下面的方式:
这个常量,我们怎么能够在./store/index.js当中,也使用呢?
第一个,当然也是按照上面的方式,来进行导入的。
第二个,在./store/index.js当中的mutations当中的方法,是可以写成下面的格式的:
官方网站推荐我们在写mutations的时候,就是通过这种方式。
2 - ppt
第154个视频 - vuex-actions的使用详解
给大家演示一个东西。
第一步:我们看看有一个修改state当中对象属性的方法:
第二步,这种操作肯定是响应式的,因为我们在state当中定义这各info对象的时候,已经是初始化了之后的。
第三步,看看效果。
我们点击了按钮,触发了事件函数,然后看看,确实是响应式的:
我们通过上面三步做了一个小小的演示。
这种修改就是一个同步的操作。
问题:假如我们不用同步的修改,我们使用异步的修改呢?
第一步:代码修改:
第二步:看看效果。
第三步:疑问?
我们的devtools当中,还是kobe,没有修改掉的。
这个就是问题。
这个就不利于调试。
这个就让devtools失效了。
不能够跟踪了。
这个info对象已经发生变化了,这个devtools跟踪不到了。
解释
为什么会出现这种状况呢?
在mutations当中进行异步操作,就是有这个弊端,页面上没有什么错误,但是devtools当中没有实时更新和记录。
vue官网推荐我们,不要在mutations当中进行异步的操作,不然的话,devtools就失效了。
结论
如果确实想要做异步操作的时候,就用action。
如果你没有异步操作,就用mutations。组件当中是可以直接提交mutations的。
如果提交的时候,还有异步操作,就必须放到actions当中。
例如网络请求的这些东西。
actions是类似于mutations,是进行异步操作的。
基本讲解
如果有异步操作的时候,就写到actions当中。mutations当中函数方法,都有默认的参数叫做state。
actions当中有默认的参数,叫做context,这个context可以就是理解成为store对象。
我们在actions当中定义一个方法,然后在这里写方法来进行异步操作。
我们可以在actions的方法当中,直接修改state吗?
当然是不可以的。
我们官方文档已经说过了,修改state的唯一的途径,就是通过mutations的。
我们不能够在actions当中直接来修改state的。
这个一定要记住的。
如果是异步想要修改的时候,必须像是下面的做法。
正确的异步修改state方法
我们原来是通过按钮来修改state当中的对象属性。
- 点击按钮
- 执行按钮当中的事件函数
- 事件函数当中执行this.$store.commit()
- commit当中写的是mutations当中的方法的名字。
现在我们是通过actions来提交了commit,我们的正确的业务实现的顺序,应该是这样的。
- 点击按钮
- 执行按钮当中的事件函数。
- 事件函数当中执行this.$store.dispatch
- 为什么是dispatch请看下面的图
- 然后在dispatch()当中写的是actions当中方法的名字。
- actions的方法当中写的是commit
- commit当中写的mutations当中的方法的名字
- 执行mutations当中的方法,修改了state。
看看效果:异步操作的效果,是正常的。
第二个问题,mutations能够传递参数,actions可以吗
肯定是可以的。
第一步,actions当中的方法使用payload来承接。
第二步,dispatch的时候,是可以传参的。
第三步,看看效果。
讲第二个东西:actions异步改state要不要告诉外面已经修改成功
actions当中是异步操作,actions当中是进行了commit,commit到了mutations当中,mutations来执行真正的修改的动作。
-
你需要通知页面,组件,已经修改成功了。
-
组件说:我知道了,你通知了我之后,我要做一点其他的操作。
-
那么我们怎么做?
-
第一步:怎么判断什么时候修改成功呢?
- 一般认为actions当中commit了,就是修改成功了。
-
第二步,我们需要在commit了之后回调。
-
第三步,我们怎么做呢?
这种方案的意思,就是我们在事件函数当中调用actions的方法的时候,dispatch的时候,我们传参进去一个函数。
然后我们在actions方法内部,如果是已经修改成功了,就是commit了之后,就调用参数当中函数,进行回调。
回调的效果检验
这样写,有另外的问题,就是我既需要传参,也需要回调的时候,咋整?!!!!
既要回调,也要传参的情况
传统解决方法:
第一步:组件调用,进行传参的时候,传入一个对象:
第二步:在actions的方法当中,直接通过对象来获取参数和回调函数就可以了。
问题: 上面的做法不够优雅
携带的信息和回调的方法是耦合的,是放在一起的。
所以我们要用一种更加优雅的方式来做这种事情的。
第一步:组件当中调用$store.dispatch的时候还是正常地传参。然后在actions当中进行commit的时候还是正常地commit。
第二步,现在是想要用一种优雅地方式,在commit之后,回调给组件调用的地方。
我们是在actions当中返回了一个promise对象。
然后是在组件的事件函数当中写了一个then。
ppt
第156个视频 - vuex-modules的使用详解
如果我们将所有的状态,往一个state对象当中塞,就会造成这个state比较臃肿。
而我们现在推荐单一状态树。又意味着,你只能够创建一个state。
其实vuex后来也考虑到了这个问题。
它建议你可以在store当中具体分模块。
vue官网推荐,当你有一些东西,单独做一些抽离的时候,可以继续定义我们的模块。
看一下笔记
做一点演练
我们定义了一个moduleA,然后我们在moduleA当中设置一些状态。
我们定义了一个名字,叫做zhangsan。
第三步,我们在页面当中进行演示。
通过上面的两张图,我们知道modules当中的玩意,最后都会放到state当中的。看一下第一张图当中的a的object。
所以,第三步当中,我们在页面当中进行展示的时候,是这样来获取的:
第四步,看一下结果:
结果是正确的。
模块当中的mutations
第一步,在模块当中我们写一个updateName方法
第二步,我们在页面当中,写一个button,绑定一个事件函数,然后在函数当中,我们应该进行commit。
注意,虽然我们的mutations是定义在模块当中的,但是我们提交的时候,还是正常地提交,一般情况下会先去store对象的mutations当中去寻找,有没有对应的方法,如果没有的话,就会去模块当中的mutations当中去寻找对应的方法的。
注意,我们直接写在state对象当中的mutations方法,和我们直接在模块当中写的mutations方法,一般情况下,一般情况下,方法的名字是不要重复的哦。
第三步,验证效果
点击按钮,修改名字之后,就是下面的状态:
模块当中的getters
第一步,我们在模块当中定义一个getter
第二步,我们在组件当中获取一下子。
这个getter也是不关心,你是定义在state对象当中的,还是定义在模块当中的。
即使你是把fullname定义在模块当中,在使用的时候,也是跟之前一样的。
第三步,看看结果:
模块当中的getter第二个问题
我们之前在讲getter的时候,遇到这样的一个情况,就是其中一个getter想要用另外的一个getter,这个时候就是这个样子的:
那么在模块的getter当中呢?
如果是出现了类似上面的情况,怎么办呢?
然后直接用一下:
然后我们看一下效果:
说明,在module当中也是可以这样用的。
模块当中的getter想要使用store对象当中的state
在模块的getter当中,第一个参数是state,第二个参数是getters,第三个参数是rootState。
第二步,我们来使用一下子。
第三步,我们来看一下效果:
模块当中的actions
actions就是进行一些异步操作。
第一步,我们在actions当中写一个方法,actions当中的方法,有一个参数,叫做context。
在模块当中的,actions当中的,方法的参数,context参数,这个就不是store对象了。
在store对象当中的,actions当中的,方法的参数,context参数,这个就是store对象。
store对象的commit,就是我们store对象当中,直接写的mutations。
但是我们模块当中的actions当中的context,只有commit到模块自己的mutations当中的。
上面的这是第一步。
第二步,我们在组件当中设置一个按钮。
第三步,我们在组件的methods当中定义一个事件的方法:
效果:
点击了按钮之后,异步修改名字:
模块当中的actions当中的context就会提交到模块当中的mutations当中的。
我们可以打印一下context对象。
从上面我们可以看到从context当中拿到rootGetters,从context当中拿到rootState。
讲到这里就讲完了模块。
第157个视频 - vuex-store文件夹的目录组织
先看看PPT
本来我们应该写一个context对象的,但是我们可以利用对象的结构语法,写成上面的样子。
对象的结构
第一步,定义一个对象。
第二步,将对象当中的属性,一一取出。
传统的写法是这样的:
es6当中的解构写法是比较简单的,是这样的:
而且大括号当中的,name,age,height这些顺序是可以换的。
回去看actions的写法
context当中有很多的属性,但是我们用对象的解构的写法,取出来三个属性,就是state,commit,rootState。
vuex-store项目的结构
我们现在vuex相关的代码,都是写到了index.js当中的。
代码有点乱的。
不方便管理。
vuex推荐我们以某种目录结构来组织代码。
其实就是代码抽离。
下面我们来重构一下代码。
第一步,我们将store对象当中的state当中的代码,剪掉。
第二步,我们放到store对象外面:
第三步,修改store对象当中的state:
这是es6的对象属性字面量增强写法。
mutations可能你也是想要用上面的抽取的方式。
但是vuex是不推荐mutations这样做的。
推荐的做法
store对象是在index.js当中的。
- state当中的代码,是推荐放在index.js当中的。做一个对象抽离。
- mutations当中的代码是推荐你搞一个文件。
actions也是同样的做法。
getters也是同样的做法。
modules推荐的做法
推荐建一个modules文件夹。
在文件夹里面创建一个js文件,就是modulesA.js
如果是多个模块的话,就都可以放在文件夹里面进行管理了。
总结
经过上面的代码抽取。代码就变得非常整洁。
state为什么不抽取呢?
因为state是整个vuex的状态,一般是不抽取的。
灵活一点。
mutations、actions、getters、modules这些东西都会抽取的。
到这里,第8天的课程就结束了。