详解:el-button的disabled神奇不生效问题

接着上期

我们来看一下它到底为什么禁用不了

首先呢我们点进这个源码

打开这块有个disabled

然后用到了一个buttonDisabled

这是一个计算属性

那我们先在这加一个输出看一下

我们在这个文件里加

不行我们得找到这个文件

那为什么找这个文件

之前的视频我讲过哈

我们在这加一个console

好然后呢我们来看一下

那输出了一个这块太快了

我们把它改一下

我们在这啊给加一个button

来手动切换这个show

然后呢把这个mounted的注掉

我们再来看一下

刷新一下

大家看输出一个了

现在是其他按钮

我点一下这

他变成禁用的了

但是这块没输出说明什么呢

说明他现在没有走这个buttonDisabled

这个计算属性

那为什么第一次(加载其他按钮)走了

第二次(切换禁用按钮)没走呢

我们这样我们把这个输出啊

换成一个debugger

好我们再来看一下刷新

好我们看一下他第一次走的时候

他的堆栈信息,在这里

打开这有一个computedGetter

比如说他走到了这行代码

所以才走到了我们现在这个

buttonDisabled里面

那我们就来看一下

他第二次切换的时候

有没有走到这一行

我们找到这个文件,vue.runtime.esm.js

我在这里已经打开了

然后呢他在5492这一行

我们找到这一行

然后呢我们在这加一个

断点但是呢我们要加一个key

否则他这个代码比较多

因为他这个属性啊是buttonDisabled

我们把这个复制过来

然后呢我们加个debugger

然后把这个debugger去掉

不管他了

好我们再刷新一下

大概这第一次他走到这了

这个dirty现在是true

所以他会走到里面

然后放行

这是(加载页面的)第2次

为什么(加载页面的时候)走两次我们先不管他

好这正常了

然后点一下这按钮

走到这了

大家看但是他这个dirty现在是false

所以他不会走到这里面

也就不会

走到

这个计算属性里边

不会走这里边

而是直接走到下边

watcher.value

那为什么这个dirty是false呢

大家还记得我上一期视频说

产生这个BUG的原因

是vue共用了虚拟节点吧

但是他只是一个表面原因

因为虽然vue共用了虚拟节点

但是呢他在切换的过程中

他把所有的属性都已经挪过来了

关于这个呢

我们可以来看一下源码里的这个方法

我们可以看一下

update child component的这个方法

然后呢我们往下找

找到这个地方

大家看 他在这赋值

我们呀在这打一个断点debugger

好我们再来试一下啊刷新

现在啊这个应该是其他的组件

我先给他禁用掉断点,先给他放开

好然后呢我把断点打开,点一下

看一下这是哪个

el-button

好我们看一下这个

对它

他新的值现在 disabled 是 true

旧的值呢

他没有disabled,说明…

他现在要切换到禁用的那个按钮

我们先

放一下这个先过一下

然后我们再点一下

我们再来看

新的值是没有的

旧的值呢是disabled:true

所以呢可以说明

虽然他这两个按钮共用了一个虚拟节点

但是这个disabled的这个属性其实是

切换过去的

那为什么切换过去之后

他这个方法不执行呢

这就需要提到我们这期视频的重点了

我们都知道:computed属性他要保证性能

我们不能让他每次读取这里面的属性

都执行下这里边的代码

这样的话性能就会比较低

所以呢,vue想了一个办法

他会在你读取这个属性的过程中

执行这个方法的时候

他记录着点:你中间都读取了哪些props

或者data

当这些属性再次发生变化的时候

然后你再去读取这个计算属性

vue才会重新执行一下这里面的代码

否则在用户重复使用这

同一个计算属性的时候呢

vue会返回一个缓存的结果

他就不会再执行一下这里边的代码了

所以你看

vue就需要一个东西来管理这个computed属性

那这个时候呢Watcher就派上用场了

我们来看一下Watcher的源码

它里边有几个关键的属性哈

一个是这个value

他是用来缓存这个

最后一次计算的结果

然后呢还有一个是dirty

用来标记刚才那个value的数据

是不是已经脏了

如果脏了就需要重新执行一下…

计算属性那个方法

那还有一个比较重要的呢

就是这个deps

这个deps

就是这个计算属性所依赖的所有的

props或者datas

在这里面所记录的

实际上

vue会给每一个组建的props属性

或者data属性

都创建一个Dep对象

那我们可以来看一下这段代码

initState

这里面呢无论是initProps

或者initData

最终我们来看它都会调一个…

这个方法

define一个reactive

然后我们点进去

他都会先创建一个Dep对象

然后呢再去拦截

做这个getter和setter的这个拦截

每次他在set的时候呢

(也就是说这个属性值发生变化了嘛)

他走这个拦截

他都会去调一下这个dep的notify

就是在通知

所有依赖这个属性值的一些目标

他就会通知到

那这里呢给大家留一个思考

我们评论区见哈

按照刚才的说法呢

每一个dep对象都会记录

依赖自己的那些watcher

那为什么watcher还要反向的记录一遍

这些Dep对象呢

因为这个问题

跟咱们今天要讲的这个BUG啊

不起绝对性的作用

所以呢这里我就不展开讲了

好然后呢我们再回到今天这个例子

为什么他第二次切换的时候没走这呢

因为他在第一次执行完这个方法以后

你会发现watcher和

dep的关系是这样的

他这个watcher

依赖了哪些Dep呢?

他首先第一个是没有这个属性的

所以呢他走了后边这个

也就说他依赖了这个elForm的disabled

只有这个el-form变化了

或者这个disabled变化了

他才会

重新计算这个属性

否则他一直返回这么一个缓存的值

所以呢我们在切换这个"show"的时候

虽然这个disabled值变过去了

但是

对于这个watcher来说

我已经不关心你这个属性变不变了

因为他第一次执行这个方法的时候啊

这块是false

所以代码就没有去尝试读取这个属性

导致这个属性的依赖关系就

没有跟watcher建立起来

所以呢他(Watcher)只针对后边这个变化

去监听

所以当我们再去切换的时候呢

他变不变

已经不会再去影响这个Watcher

是否需要更新缓存值了

所以呢

我们可以这样验证一下我们在这

就把这个变量放这一下

也不用做什么处理

代表着我们再尝试读一下

只要一尝试去读取他

他这个Dep

和watcher的关系就建立起来了

我们

可以试一下

切换

哎?~ 啊对!放错了,不应该放这啊

放这个文件里

我们再来试一下

切换哎可以了大家看

了解了这个原理之后

其实我们可以自己写一个例子

来复显他这个问题

我这写了一个disabled

大家看 这个页面哈

这里边只有一个

test来引用这个

计算属性

然后里边每次做一个输出

代表这个计算属性被执行了

还是下边一个三元运算符

window.g(我随便写的g) 这个计肯定默认是没有的

所以呢他会走这

他会一直显示这个“xiaoshanB”这个变量

对应的话呢

这两个变量也会放在两个input的里边

我现在修改这个文本框,他就会变对吧

修改前面的他不会变

但是如果我现在把

这个window.g

等于一个

window.g已经有值了对吧

按说的话他有值

这地方应该显示这个了

但是大家看:第一 现在没变

而且(第二)我在这修改这个a

他也没有变化

唯独我在这把b稍微改一下

大家看他就会变成a了

这回我在这再改他就变回来了

所以呢归根结底啊

如果我们在

计算属性里边用到的一些属性

他不是

vue组件的data属性或者props属性

像这种他就会有可能出现BUG

最后更新于