【Vue原理】Directive

喜夏-厌秋 提交于 2020-12-19 02:39:28


↑点击上方 “神仙朱” 一起研究Vue源码吧


专注 Vue 源码分享,为了方便大家理解,分为了白话版和 源码版,白话版让大家可以轻松理解工作原理,源码版让大家更清楚内部操作和 Vue的美,来,一起学习吧嘿!



今天是除夕啦,大家新年快乐!身体健康!2019多多开花,耶,嗨起来!

言归正传!

我相信应该大家应该都使用过 Directive 指令,有时为了能够直接操作DOM,而指令中最重要的莫过于是 钩子函数了,指令一共有五个钩子函数,他们不会在不同的阶段触发,文档也已经说明

当然了,其实你只要了解它是什么时候触发的,就完全可以用在项目。但是我们是不会满足于此的,我要知道他是怎么触发的,怎么调用到我设置的钩子的

今天,我们就来简单说一下这几个钩子都是怎么被调用

你能相信我写 Directive 花了一个星期啊,不是有多难,而是我不知道怎么下手写啊,根本不知道怎么描述会简单好了解,吐血 


钩子如何调用

首先,Vue 在绑定了指令的DOM 创建之后,插入页面之前,对一些DOM 本身的事件或者属性等进行处理。

其中,就包含对本DOM的所有指令进行处理

怎么处理呢?每一个钩子函数都不一样,所以我们会分不同钩子说明

首先,处理时,Vue 要判断哪些指令是新的还是旧的

所以需要比较 旧节点上的指令 和 新节点上的指令

比如 新指令比旧指令 多了一个指令 ,如下

// 新指令 newDir 如下

newDir={  

   "v-test":{        

        name: "test",    

       rawName: "v-test"    

   },

   "v-test2":{        

        name:"test2"

       rawName:"v-test2"    }  

}


// 旧指令 oldDir 如下,少了一个 v-test2

oldDir={  

   "v-test":{        

        name: "test",    

       rawName: "v-test"    }   }


如果是新指令

遍历新指令对象时,当 'v-test2' 指令不存在 旧指令对象中,则表示这个是新指令,需要初始化


1bind

遍历遇到新指令时,直接执行 bind 钩子函数,并传入参数

for(i in newDir){    

    var dir = newDir[i]    

    if( !oldDir[i]){

       dir.bind(....参数)

    } 

}


2inserted

按文档的说明,我们就知道,inserted 是在节点插入父节点调用

而所有节点的所有钩子,会放在同一时间一起处理,并不是插入一个节点,就执行一个节点的 inserted 钩子

inserted 分为 保存 和 执行 两个步骤

因为inserted 需要在 节点插入之后才执行,而现在处理是在 节点插入之前,所以只能先保存起来,用于后面执行。

简单实现如下

1、使用一个数组 保存 DOM 所有新指令的 inserted 钩子

var dirInserted = []

for(i in newDir){    

    var dir = newDir[i]    

    if( !oldDir[i]){

       dir.bind(....参数

       dirInserted.push(dir.inserted)

    }

}


2、新建一个函数,专门遍历这个数组,逐个执行 inserted 钩子

var callback = function(){    

    for(var i=0;i=dirInserted.length;i++){

       dirInserted[i](....参数)

    }

}


3 callback 保存进当前节点的一个地方,为了后面使用

dom.insert = callback


4、把 所有含有 callback 的节点,也放到一个数组

var insertedVnodeQueue=[]

if( 存在inserted dom ){

   insertedVnodeQueue.push( dom )

}


5、当节点插入后,开始遍历insertedVnodeQueue

for (var i = 0; i <insertedVnodeQueue.length; ++i) {

  insertedVnodeQueue[i].insert(); }

为了验证 inserted 钩子 并不是插入一个节点,就执行一次,我特地在 插入节点的函数后面打印一句话,于是可以看到如下的打印顺序

bind---> SPAN

插入DOM span


bind---> P

插入DOM p


bind---> DIV

插入DOM div


inserted---> SPAN inserted---> P inserted---> DIV


如果是旧指令

遍历新指令对象时,当 v-test 指令也存在旧指令对象中,则表示这个是旧指令,需要更新


1update

碰到旧指令,会直接执行 update 钩子函数,并传入参数

for(i in newDir){    

    var dir = newDir[i]    

    if( !oldDir[i]){

       dir.bind(....参数)

    }else{

        dir.update(...参数)

    } 

}


2componentUpdated

componentUpdated 的保存方式 和 inserted 差不多,但是执行方式却不一样。

哎哟,这个钩子是,更新一个节点,就马上执行该节点钩子的喔。跟 inserted 完全不一样喔,虽然我也不懂为什么

我也在源码内更新 DOM 后加了一句打印,然后我们看下执行顺序

update---> DIV


update---> SPAN

完成更新DOM span

componentUpdated---> SPAN


update---> P

完成更新DOM p

componentUpdated---> P


完成更新DOM div

componentUpdated---> DIV


3unbind

当 新指令 比 旧指令少了,比如下面这样

// 旧指令

{  

   "v-test":{        

        name: "test",    

       rawName: "v-test"

   },    

    "v-test2":{        

        name:"test2"

       rawName:"v-test2"    }  

}

// 新指令

{  

   "v-test":{        

        name: "test",    

       rawName: "v-test"    }   }

那么 v-test2 指令被去掉了,这时肯定会触发 unbind 钩子

unbind 钩子也是直接调用的,没有那么多转来转去的逻辑幺蛾子,但是需要两个判断条件

1不是新节点。刚刚创建的指令,指令肯定都是新的,就没必要往下走,浪费性能

2某个旧指令 不存在 新指令对象中。证明这个指令已经被移除

if( 不是新节点 ){    

    for(i in oldDir){    

        var dir = oldDir[i]    

        if( !newDir[i]){  

            dir.unbind(....参数)    

        }  

    } 

}



历史分享

【Vue原理】Vue源码阅读总结大会 - 序

【Vue原理】学会调试Vue源码

【Vue原理】响应式原理 - 白话版

【Vue原理】Props - 白话版

【Vue原理】月老Computed - 白话版

【Vue原理】Watch - 白话版

【Vue原理】Mixin - 白话版

【Vue原理】Methods - 源码版



最后

希望你认真阅读后,能豁然开朗。如果你懂了,我的目的也达到了,如果你还不懂,可以留言告诉我哦,百分百回复好吧

当然如果本文有任何描述不当的问题,欢迎后台联系本人,领取红包

如果你能转发一下,就更好啦,技术人交流 只用文章


关注我可以说是相当优秀了

本文分享自微信公众号 - 神仙朱(skying-zhu)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!