详解: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