![bbf6df5ad0809afc1b329560d76bafdf.png](https://img-blog.csdnimg.cn/img_convert/bbf6df5ad0809afc1b329560d76bafdf.png)
1.watch一个融合多个数据源的computed以及发散
业务场景:多个数据触发同一个操作
熟悉中后台开发的同学应该不会对列表过滤翻页这样的场景感到陌生
这样的页面有一个特性,就是多种操作会出发同一个后端接口(获取列表数据)
正常的思路,mounted,handlePageChange,handleFilterChange等处分别调用一次封装好的异步逻辑函数
然而如果页面的功能日趋复杂化,维护的时候就会出现逻辑分散,难以理解的问题
所以,如何将多个功能点触发同一段异步的逻辑“集中起来”,就是我们开发中的痛点
其实,我们完全可以使用一个computed监听多个数据的变化,然后再利用一个watch对该computed进行监听,在watch内部编写我们的公共异步逻辑
![c248c834f23956195e2ee8fc96479eb8.png](https://img-blog.csdnimg.cn/img_convert/c248c834f23956195e2ee8fc96479eb8.png)
这里涉及到的知识点.主要是watch的对象用法,以及watch监听computed
当然,既然我们这篇文章叫做高级用法,对标高级工程师,哪能就这么讲下用法就过去了?
我相信挺多同学看到这里会有一个疑问: watch中的immediate和mounted功能非常相似,他们有什么区别呢?
首先我们来看下,他们执行顺序之间的差异
![a52339be9ad5eae90301bb65a8d1b773.png](https://img-blog.csdnimg.cn/img_convert/a52339be9ad5eae90301bb65a8d1b773.png)
问题一下子就来了,为啥会是这个顺序?
我们直接来看下vue的位于instance/index.js源码
![1b2a03a7e6fb429c9c5f304410082ab2.png](https://img-blog.csdnimg.cn/img_convert/1b2a03a7e6fb429c9c5f304410082ab2.png)
我们可以看到这段代码中"混入"了一些初始化的内容
点进去继续探索一下
![c5019f9aaa01b7f9ab6d9816a4c916f5.png](https://img-blog.csdnimg.cn/img_convert/c5019f9aaa01b7f9ab6d9816a4c916f5.png)
initLifecycle
是不是非常清晰了?
那么我们再来看一下我们特别关注的initState中的内容
![73319d9e9eb5aba2791a29e3ff8a37cb.png](https://img-blog.csdnimg.cn/img_convert/73319d9e9eb5aba2791a29e3ff8a37cb.png)
因为computed挂载顺序先于watch,所以我们上面用watch来监听computed是非常合理的
无非也就都是个watcher而已
同时这里提出一个问题供大家思考: 为什么vue的设计者要使用这样的顺序呢?
而initWatch会创建一个watcher并执行$watch,而$watcher中如果发现watch携带了immediate标识会立即执行
![d1afcc43001e24c94dc15433434f14d0.png](https://img-blog.csdnimg.cn/img_convert/d1afcc43001e24c94dc15433434f14d0.png)
而这时我们会发现$watch竟然还返回了一个叫做unwatchFn的东西
这玩意就是我们平时用于卸载watch的函数,他可以清空该watch的执行队列
例如我们有一个需求,当某个数据变化三次以后便取消对该数据的监听,不在触发watch中的handler逻辑,我们就可以
const
那么这里老师想要提出一个问题,这段逻辑可以写在beforeCreated之中吗?
带着这个疑问我们继续后面的学习吧~
(deep, 监听某个子属性,触发多个handler后面再写吧,主要是我觉得这几个点太基础了,说起来没啥意思)
2. 使用Object.freeze()冻结项目所使用的常量
日常开发中我们常常会碰到许多常量
例如常规的一些表格columns,或者是大量表单元素的label
他们的统一的特点是一旦编写就几乎不会发生任何变化,且通常会被我们抽取到公共的contants.js中
![e45e7bd5f3cc3f32fefe7d4a86a4bc2c.png](https://img-blog.csdnimg.cn/img_convert/e45e7bd5f3cc3f32fefe7d4a86a4bc2c.png)
但是我们在使用的时候,必须将其引入后放置到data中
然而,对vue源码比较熟悉的同学一定知道,只要数据进入的data,vue就会对其监听
以图在数据变化时更新视图
显然,这对于永远不会变化的常量来说造成了大量的性能浪费,拖慢了代码的计算速度
有没有好的办法来解决这个问题呢?
当然是有的,而且vue的官方文档也曾隐晦的暗示过我们
![d1d361a50500a0a89ef841593cb2f972.png](https://img-blog.csdnimg.cn/img_convert/d1d361a50500a0a89ef841593cb2f972.png)
我们只需要把contants.js export default出来的大对象用Object.freeze()处理一下
则contants下辖的所有字段都不会再触发vue的监听了
![b94260d2602ffc356b728fafa16be50b.png](https://img-blog.csdnimg.cn/img_convert/b94260d2602ffc356b728fafa16be50b.png)
这里非常值得注意的一点是freeze处理后的对象的第一层键值对是无法修改的,但是如果深层含有数组的话,其实还是"允许"开发者修改的
![326cac066e6da794607fa25a72ee9fa6.png](https://img-blog.csdnimg.cn/img_convert/326cac066e6da794607fa25a72ee9fa6.png)
只不过这种修改已经无法触发vue的rerender了
![05b22ec5c1e286f0c004a1b61e40bcf7.png](https://img-blog.csdnimg.cn/img_convert/05b22ec5c1e286f0c004a1b61e40bcf7.png)
至于为什么freeze可以达成这样的效果呢?
我们可以看下位于core/instance/observer/index里面的defineReactive方法
![8f5e283abc3c58244d72ccf82c14108e.png](https://img-blog.csdnimg.cn/img_convert/8f5e283abc3c58244d72ccf82c14108e.png)
我们可以看到,当获取到需要监听的对象时,会先判断是否可以获取property的描述
如果获取不到描述,直接return,不会对对象进行进一步监听
到这里,我们可以扩展一下我们的知识面
了解下例如Object.preventExtensions(), Object.seal() 以及最最核心的defineProperty中的configurable, writeable以及enumerable
3. 通过$options在非template中使用过滤器方法
![49ee14cde2558f0c155b84d9d61b4dd7.png](https://img-blog.csdnimg.cn/img_convert/49ee14cde2558f0c155b84d9d61b4dd7.png)
filter在我们日常开发中非常常见,可以实现的功能也非常多
例如: 为列表加上一个升序的key, 或者使用moment处理时间
![829c6ca6a9faff5dfb3b58809e3498f5.png](https://img-blog.csdnimg.cn/img_convert/829c6ca6a9faff5dfb3b58809e3498f5.png)
![2ef0598fe98b2d88bb7a0fb3c5bd5f39.png](https://img-blog.csdnimg.cn/img_convert/2ef0598fe98b2d88bb7a0fb3c5bd5f39.png)
可是目前这种写法仅限于在胡子语法或者v-bind中才可以使用
那么如果我们想要在类似methods中使用我们已经注册到全局或者单个组件内部的filter时该怎么做呢?
其实也非常简单,vue为我们提供了一个强大的内置api----$options
首先我们来打印一下this.$options看看他里面都有什么吧
![4e84bf9a5c9d782787353bb119a96fbf.png](https://img-blog.csdnimg.cn/img_convert/4e84bf9a5c9d782787353bb119a96fbf.png)
乍一看好像也没啥有用的东西
那我们直接打印一下filters吧
![6252f5e71c6ece1f35fac1f56204732d.png](https://img-blog.csdnimg.cn/img_convert/6252f5e71c6ece1f35fac1f56204732d.png)
![06fe6171d405c6dece8c8847e3779270.png](https://img-blog.csdnimg.cn/img_convert/06fe6171d405c6dece8c8847e3779270.png)
惊不惊喜?意不意外?
为什么会出现这种状况呢?
其实原理也很简单,对于vue的学习,越是探索到源码级别,其实就越会有一种体会
学习vue本质上其实是在学习js基础,设计模式以及算法
对于vue来说,无论是挂载在某一个组件上的filters还是注册在全局的公共组件,其实本质上都是挂载在vue实例的某一层原型链上
而原型链有什么特性呢?
那肯定就是如果在某一层级找不到该属性,就会递归式的向上寻找上级原型链上是否存在该属性
4. 通过hook:hookName来把控生命周期逻辑
日常开发中,常常会碰到一个场景
进入一个列表页面,我们需要使用定时器轮训列表接口来获取最新的数据
而且我们还需要在组件销毁的时候同步销毁定时器
这本来是一段非常正常的逻辑,涉及到的时mounted和beforeDestroy两个狗子
但是,如果我们的页面逻辑日趋庞大,创建定时器和销毁定时器的逻辑分离过远,难以维护和理解
而且我们需要单独配置一个data字段来保存timer
那么我们有没有一种办法可以将两处逻辑放在一起呢?
![d602529dca557581ac17fb0ee804480e.png](https://img-blog.csdnimg.cn/img_convert/d602529dca557581ac17fb0ee804480e.png)
这里我们直接在mounted里面监听一次beforeDestory这个钩子,并清除定时器的触发
眼尖的同学可能会发现我在这段逻辑的上部还监听了updated这个钩子
那么我们这里就会有一个疑问:
通过$on这类逻辑监听的钩子中编写的逻辑和正常写在vue语法中的生命周期是否能够合并?
如果能够合并,他们的先后顺序又是怎么样的?
![e2185d983467064a20ed1e2729a29373.png](https://img-blog.csdnimg.cn/img_convert/e2185d983467064a20ed1e2729a29373.png)
![e762793085e3935dff840b61741403fb.png](https://img-blog.csdnimg.cn/img_convert/e762793085e3935dff840b61741403fb.png)
大橘已定!bingo!
首先,两段逻辑都会执行,生命周期已经被合并
其次,我们的在updated中编写的逻辑会先执行,随后,才会执行其他位置通过$on这类方法新增的逻辑
![d2b2391149d4d46055df65e4072e50b2.png](https://img-blog.csdnimg.cn/img_convert/d2b2391149d4d46055df65e4072e50b2.png)
这时候肯定有抖抖嗖嗖的同学要问了,老师啊,这玩意还有别的用处吗
当然有,我们可以把思维发散一下
既然我们可以通过$on以及$once监听hook,那么我们为啥不可以直接使用v-on来直接监听呢?
如果顺带在联系一下组件化的思想,是不是我们可以通过这种方法,直接鉴定子组件的生命周期呢?
![faf14cc49de2f04c54f656c7d9fd7dc5.png](https://img-blog.csdnimg.cn/img_convert/faf14cc49de2f04c54f656c7d9fd7dc5.png)
![8b2167f10b99bc0957f94c90656a614a.png](https://img-blog.csdnimg.cn/img_convert/8b2167f10b99bc0957f94c90656a614a.png)
![f7ef269f6b210ed535e106484f3ad5a2.png](https://img-blog.csdnimg.cn/img_convert/f7ef269f6b210ed535e106484f3ad5a2.png)
运行一下,我们来看一下结果和执行顺序
![703d98dd8d8c8ac0071d9c6fb2ce99c5.png](https://img-blog.csdnimg.cn/img_convert/703d98dd8d8c8ac0071d9c6fb2ce99c5.png)
我相信这里肯定有同学要按捺不住内心的喜悦了
满脑子都是什么父组件操控子组件mouted的逻辑,还有什么公共钩子云云
其实这些靠监听子组件生命周期是无法实现的
主要原因是vue并没有为这块逻辑暴露子组件的vm实例
其实我们处于vue设计者的角度来思考一下,如果真的向父组件暴露的vm的话,肯定会造成逻辑管理的混乱,尤其是vue已经为我们提供了诸如mixin,$listeners这类成熟优雅的api的前提下
也可能会有同学读到这里觉得
我靠,这种用法为啥官网上都没写?你是从哪知道的?
答案其实也很简单,我是通过对于vue源码的阅读了解这种用法的
这两种用法的源码分别位于vuecore部分的instance(实例公共方法)以及lifecyles(vue生命周期的挂载混入公共方法)中
![88012f30eb485e7611598dee7e1ea209.png](https://img-blog.csdnimg.cn/img_convert/88012f30eb485e7611598dee7e1ea209.png)
![f59b85890d3eaaac3553e6cd6f6b815c.png](https://img-blog.csdnimg.cn/img_convert/f59b85890d3eaaac3553e6cd6f6b815c.png)
监听子组件生命周期触发,实际开发中用处比较多的是监听子组件是否更新,尤其是比较小众的第三方组件在没有提供api时,父组件可以根据钩子的触发来把控子组件的所处的钩子流程,完成相应的操作
5. 组件间通信一网打尽
其实这里我主要想提下provide和inject
很多开发同学对provide和inject关注甚少,甚至慢慢沦为了只在面试中考察知识宽度的一个知识点而已
为什么会这样呢?
原因其实也很简单,就是因为官方文档上的一句话
![9955fee6d74d8e9c2924a26d0ab55d2d.png](https://img-blog.csdnimg.cn/img_convert/9955fee6d74d8e9c2924a26d0ab55d2d.png)
然而,其实在我们日常开发中经常会封装符合自身业务场景的组件库,而这些组件库或者公共方法只要加个install方法不就是所谓的"高阶插件"了吗?
除此之外,provide/inject最被人诟病的莫过于官网中提到的
![04c4fdd70c8f2833e2cd112c904a7dc8.png](https://img-blog.csdnimg.cn/img_convert/04c4fdd70c8f2833e2cd112c904a7dc8.png)
大多数同学对着官网的demo敲了一遍,发现provide里面连this都获取不到,直接弃坑
然而事实真的是这样吗?
首先,我们上面的源码解析中有提到在initMixin中的initState方法中,属性的挂载顺序是
inject=>
state(props,methods,data,computed,watch)=>
provide=>
那么按照源码的思路,provide是一定可以使用state里面的数据的
我们只需要将一个返回对象的函数传给provide即可
![dcf9800c151480c42069082b5aa488e0.png](https://img-blog.csdnimg.cn/img_convert/dcf9800c151480c42069082b5aa488e0.png)
相信很多同学在学习provide.inject的都是用的上图中pageProps这种方式
![8872378254486805f6ad1a80ddf63cba.png](https://img-blog.csdnimg.cn/img_convert/8872378254486805f6ad1a80ddf63cba.png)
在子组件中承接一下,发现不是响应式的
换句话说,父组件的this.page变化,并没有触发子组件的updated和beforeUpdate
![33459073e226ccd1f30b9db711b0c26c.png](https://img-blog.csdnimg.cn/img_convert/33459073e226ccd1f30b9db711b0c26c.png)
然后很多同学们就微笑着关闭了vscode
其实,事实并不是这样的,只不过我们没有掌握provide/inject的真正用法
![dcf9800c151480c42069082b5aa488e0.png](https://img-blog.csdnimg.cn/img_convert/dcf9800c151480c42069082b5aa488e0.png)
回到这块代码,你会发现,老师使用了parent这个属性向父组件下辖的所有层次的子组件下发了该父组件的this
而上面我们有提到过,this上面有个神奇的属性$options里面犹如一个舔狗狗场
![8d5ef4ec6400017c5e28aedc18736f92.png](https://img-blog.csdnimg.cn/img_convert/8d5ef4ec6400017c5e28aedc18736f92.png)
所以我们就猥琐的使用出奥义watch
![1e0c2a18c1022431723a4d0b024c9607.png](https://img-blog.csdnimg.cn/img_convert/1e0c2a18c1022431723a4d0b024c9607.png)
这不就是大家心心念念的响应式吗?
另外,provide/inject另一个强大的功能在于,可以通过传输this直接调用父组件的方法,来调控父组件的某些state
换个思路来理解,你会发现provide/inject就是一个远程打击版的props+emit
讲完了provide/inject,我们来聊聊2.6新推出的observable
![7681c897fe03b7508a78141010ab2600.png](https://img-blog.csdnimg.cn/img_convert/7681c897fe03b7508a78141010ab2600.png)
其实没啥好讲的,因为observable就是一个乞丐版的vuex
主要是用于例如内嵌app的h5页面开发,或者简单的spa,因为这类应用不需要vuex这种体量级的包
但是他仍然可以做为provide/inject的替代品使用,他的缺点和react的useContext一样明显,相信也不需要多说
不过既然上面两种方法都提到了子组件修改父组件属性的问题,也可以顺带提一嘴v-bind.sync修饰符
![c9873f6d75a1c1c34f008b36b8cf57f1.png](https://img-blog.csdnimg.cn/img_convert/c9873f6d75a1c1c34f008b36b8cf57f1.png)
直接使用update:[key]的方法直接修改父组件的data
![fc0f4df21da5556b564776085455a224.png](https://img-blog.csdnimg.cn/img_convert/fc0f4df21da5556b564776085455a224.png)
![0c9244a178dd769551bc5d31d1f71a4b.png](https://img-blog.csdnimg.cn/img_convert/0c9244a178dd769551bc5d31d1f71a4b.png)
谁能想到我的儿子竟然会自己挑妈妈了呢?
![5aa8f49a3d6f0a9ae4029ba75e2bc893.png](https://img-blog.csdnimg.cn/img_convert/5aa8f49a3d6f0a9ae4029ba75e2bc893.png)
回归到vue的源码
![ec1930de489cee404309d4e9f66bd0b0.png](https://img-blog.csdnimg.cn/img_convert/ec1930de489cee404309d4e9f66bd0b0.png)
其实vue关于v-bind的修饰符的源码思路也是很清晰的
解析出修饰符,将事件返回的$event直接赋值给vm对应的key就好了,而这个事件的名字是"update:""拼上[key],和我们的写法完全一致
至于其他的集中通讯方式,例如vuex,$parents,$root,$listeners,$attr,eventBus以及最常见的props传值就不再赘述了
不过老师还是想要讲解一下这集中通信方式的边界和使用场景是怎样的(排名分先后)
- vuex
- 优点:可以使用开发工具可视化所有数据,便于调试,写法优雅,性能较好
- 缺点:包较大,如果module分布混乱或者命名不规范,维护非常困难,且spa公用一个状态树,造成性能浪费
- 适用场景: 任何场景,甚至可以完全取代props传值
- props/$emit
- 优点:简单方便,而且提供有.sync修饰符,状态随组件卸载销毁,性能较好
- 跨级组件通讯非常麻烦,难以维护(我对此深恶痛绝)
- 使用场景:任何父子组件通讯场景
- provide/inject && Vue.observable
- 优点:跨组件通信方便,放在公共组件(无论是业务还是抽象包装类组件或者轮盘组件)中表现优异,点对点注入,性能优异
- 缺点:对于小白来说维护困难,需要开发者对于逻辑有十分清晰的把控力,并且需要详细注释
- 使用场景:公共组件或者插件开发中
- ,$parents,$root,$listeners,$attr,eventBus
- 使用场景:能少用就少用,没啥滋味
6. 侵入Vue实例,构建自定义生命周期函数
日常开发中,经常会碰到一些事件触发的公共逻辑
例如,多个页面,多次通过点击按钮的操作触发同一段异步逻辑
或者,需要频繁监听onsize等需要挂载在window上的事件
如果每个页面我们都在具体需要触发事件的页面或者功能区重复写同一段逻辑
未免也太麻烦了
此时,不如直接抽象触一个属于自己生命周期函数
使其能够像vue自身携带的mouted,created等钩子一样工作,岂不美哉?
首先,我们需要了解vue的生命周期函数是在core/instance/index中初始化Vue类的时候通过initMixins挂载上去的
![e1e81ca6311a1143d8091d8c557f683f.png](https://img-blog.csdnimg.cn/img_convert/e1e81ca6311a1143d8091d8c557f683f.png)
这里的逻辑我们是无法修改的,原因在于这部分逻辑完全内置,且在实例化的时候,Vue已经从事件池中将相关的事件打捞并执行了
换句话说,实例化之前挂载我们的侵入性自定义钩子的方案是不可行的
所以,我们只能从已经实例化的vm上着手侵入构建自定义钩子
首先,在上文中其实我们有提到过
vue生命周期是有一定的合并策略的,而不同的钩子的合并策略略有不同
而且,这种合并策略其实vue是通过Vue.config暴露给用户的
所以,如果我们想要构建属于自己的生命周期,需要做的第一件事就是为我们的生命周期函数赋值一个合并策略
而合并的方式,我个人认为
一定要优雅
![42f3abfea97062b168f740a960a7f98d.png](https://img-blog.csdnimg.cn/img_convert/42f3abfea97062b168f740a960a7f98d.png)
通过插件的形式,无疑使我们最好的选择
回到代码本身,其实这一步就是在Vue.config.optionMergeStrategies中添加一个名字叫做ShangHook的自定义钩子合并策略
而我们的策略原型,选择的是我们最常见mouted
策略一旦嵌入到Vue中,实例化出的vm的$children就会在$options携带有我们的自定义钩子
到这里,我们走完了第一步,就是实例化之前的初始化
随后,我们需要再走一步,也就是实例化之后,将我们的钩子推入事件池
![e5e8a7b6354ad4c76b93059a646d00b2.png](https://img-blog.csdnimg.cn/img_convert/e5e8a7b6354ad4c76b93059a646d00b2.png)
此时,触发的事件已经建立,我们寻找到类名为shang的所有元素
随后为其挂载了一个点击事件,时间内执行了一个通知触发钩子的函数
保证事件和我们的钩子关联起来
![de879f80d39fd6aa367f4c087b1145c0.png](https://img-blog.csdnimg.cn/img_convert/de879f80d39fd6aa367f4c087b1145c0.png)
这个函数涵盖了很多功能点,接下来我会逐个解析
首先,由于我们使用的时mounted的合并策略,那么原则上说,除了在script里面挂载的钩子逻辑以外,他还会在hook:shangHook以及mixin中触发
此时,我们的shangHook就必定是一个数组,因为他并非是"阉割版钩子",而是一个可以像Vue内置的其他钩子一样行使各种智能的"完全体钩子"
而且,我们甚至可以为shangHook加一些Vue钩子所不具备的特殊功能,例如传参和回调函数
![7af568d5c53a543d2d55d7aa1a3146cd.png](https://img-blog.csdnimg.cn/img_convert/7af568d5c53a543d2d55d7aa1a3146cd.png)
![c58ba4305904d81b2e0f79e21de70523.png](https://img-blog.csdnimg.cn/img_convert/c58ba4305904d81b2e0f79e21de70523.png)
![fbf46639bda41a30b94afc949398f04e.png](https://img-blog.csdnimg.cn/img_convert/fbf46639bda41a30b94afc949398f04e.png)
我这里只是为了展示直观,所以只加了两个字符串
而实际上,我们在上文的notifyClickChange方法中,我们在递归调用的时候传入了vm实例,那么vm实例中$options中的任何东西我们都可以随意使用
同时这边还可以加入一些默认逻辑,例如每次点击都全局message一条信息
同时,我们在组件中使用shangHook的时候,还可以使用类似React_useState的回调语法来控制异步逻辑的执行,甚至可以触发例如beforeUpdate或者beforeDestory这类钩子
从此,React和Vue语法上的鸿沟,又浅了一分~~~
让我们记住这历史性的一刻~~~
不过此时,相信有一些同学已经注意到老师在notify方法中传入了触发钩子元素的class
而且,我们还通过class中是否还有实例的uid来判断是否触发钩子
这是为什么呢?
首先,Vue内置的钩子是从原则上来说并不是一个组件触发钩子,其他无嵌套关系的组件也会触发
更多的是,谁触发了某个钩子,尽量不影响到其他无关组件的状态
而我们想要完成这种沙箱的效果,就需要给触发钩子的组件打上一个标记,那么uid就是最好的选择
我们只需要在需要出发钩子的元素上加一个包含当前组件uid的类名即可
![1430fd306024938829d82c777d319db8.png](https://img-blog.csdnimg.cn/img_convert/1430fd306024938829d82c777d319db8.png)
至此,我们的自定义钩子已经全部完成,这里老师提几个优化点供同学们思考
1.uid的挂载到类名当中,是否可以用webpack的loader来自动化添加?
2.notify中的函数使用递归方式过于耗费性能,可有更好的办法可以直接定位到触发钩子的组件?
好了,带着这些问题,我们开始后面的学习吧~
7. 自定义指令通过虚拟节点获取当前组件原发环境下的$options
日常开发中我们经常碰到点击一个操作按钮,然后弹窗提示用户是否确定操作?
如果用户点击确定,则调取一段异步逻辑,异步完成之时,通过全局message提示用户操作成功
这种场景,就非常适合我们使用自定义钩子来处理
![6596a8c752935ade9750fea5359efac8.png](https://img-blog.csdnimg.cn/img_convert/6596a8c752935ade9750fea5359efac8.png)
自定义钩子的挂载过程中,会为我们提供四个生命周期函数
bind,inserted unbind, update
这里我们只要使用到bind,也就是绑定元素的时候触发钩子
由于自定义全局指令的挂载必须要在实例化之前,而我们在指令的内置逻辑又需要用到ui框架提供的部分组件(这里使用的是我司常用的antd-vue)
所以我们先要保证ui插件先安装,随后在Vue的原型链上寻找我们需要用的组件方法,同时注册一个点击事件,顺便调节一下按钮的样式
这时候我们就碰到了一个问题---点击按钮所触发的异步必须由触发的组件提供,所以我们需要为自定义指令和挂载元素之间形成一个通讯链
幸运的是,vue已经为我们提供了binding参数,我们可以通过binding中的value拿到我们需要的方法
![8cd90e306fcd638e6136393ee328ff85.png](https://img-blog.csdnimg.cn/img_convert/8cd90e306fcd638e6136393ee328ff85.png)
随后,我们注意到binding后面还有一个vnode参数,而这个参数很多人会觉得没什么用处
原因也很简单,因为vnode就是我们常说的用于构建虚拟dom的虚拟节点
有很多难以理解的对象键值对,然而,vue却非常贴心的为我们在vnode中添加了context供我们使用
context提供的数据与$options非常类似,所以我们现在可以访问当前组件所在语法上下文中任意的属性和方法甚至生命周期
日常开发中,自定义指令由于虚拟节点的暴露,可以完成大量难以想象的公共功能,这种优越的用法,供各位同学参考使用
未完待续~最近上班有点忙,有空我就写点~呼~
绝对原创,希望大家多多支持~
![4b8d8d2f8df4b0d6f014adcf966d52e2.png](https://img-blog.csdnimg.cn/img_convert/4b8d8d2f8df4b0d6f014adcf966d52e2.png)
源码的分析我会随后更新到专栏中
我会从宏观到微观方法,层层递进,选择一条其他作者未曾尝试的顺序来解析vue的源码
目标在于提升对vue的把控力,另外还致力于提升面试官的出题思路和面试者的答题宽度