首页 > 作文

Vue.js 源码分析(二十四) 高级应用 自定义指令详解

更新时间:2023-04-03 03:05:24 阅读: 评论:0

除了核心功能默认内置的指令 (v-modelv-show),vue 也允许注册自定义指令。

官网介绍的比较抽心理健康课教案象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些dom元素或组件上时,该元素在初次渲染、插入到父节点、更新、解绑时可以执行一些特定的操作(钩子函数()

自定义指令有两种注册方式,一种是全局注册,使用vue.directive(指令名,配置参数)注册,注册之后所有的vue实例都可以使用,另一种是局部注册,在创建vue实例时通过directives属性创建局部指令,局部自定义指令只能在当前vue实例内使用

自定义指令可以绑定如下钩子函数:

·bind      ;只调用一次,元素渲染成dom节点后,执行directives模块的初始化工作时调用,在这里可以进行一次性的初始化设置。
·inrted       ;被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
·update       ;所在组件的 vnode 更新时调用,但是可能发生在其子 vnode 更新之前。指令的值可能发生了改变,也可能没有。
·componentupdated ;指令所在组件的 vnode 及其子 vnode 全部更新后调用。
·unbind       ;只调用一次,指令与元素解绑时调用。

每个钩子函数可以有四个参数,分别是el(对应的dom节点引用)、binding(一些关于指令的扩展信息,是个对象)、vnode(该节点对应的虚拟vn哦的)和oldvnode(之前的vnode,仅在update和componentupdated钩子中可用)

bind钩子函数执行的时候该微信营销策略dom元素被渲染出来了,但是并没有插入到父元素中,例如:

<!doctype html><html lang="en"><head>    <meta chart="utf-8">    <title>document</title>    <script src="vue.js"></script></head><body>    <div id="d"><input type="" name="" v-focus></div>    <script>        vue.directive('focus', {                   bind:function(el){console.log(el.parentelement);},                      //打印父节点            inrted: function (el) {console.log(el.parentelement);el.focus()}      //打印父节点,并将当前元素处于聚焦状态        })        var app = new vue({el:"#d"})    </script></body></html>

输出如下:

可以看到input元素自动获得焦点了,控制台输出如下:

可以看到对于bind()钩子来说,它的父节点是获取不到的,因为vue内部会在执行bind()钩子后才会将当前元素插入到父元素的子节点里

源码分析

在解析模板将dom转换成ast对象的时候会执行processattrs()函数,如下:

function processattrs (el) {                     //解析vue的属性  var list = el.attrslist;   var i, l, name, rawname, value, modifiers, isprop;  for (i = 0, l = list.length; i < l; i++) {             //遍历每个属性     name = rawname = list[i].name;    value = list[i].value;    if (dirre.test(name)) {                                 //如果该属性以v-、@或:开头,表示这是vue内部指令      // mark element as dynamic      el.hasbindings = true;      // modifiers      modifiers = parmodifiers(name);      if (modifiers) {        name = name.replace(modifierre, '');      }      if (bindre.test(name)) { // v-bind                          //bindrd等于/^:|^v-bind:/ ,即该属性是v-bind指令时        /*v-bind的分支*/      } el if (onre.test(name)) { // v-on        /*v-on的分支*/      } el { // normal directives        name = name.replace(dirre, '');                         //去掉指令前缀,比如v-show执行后等于show        // par arg        var argmatch = name.match(argre);        var arg = argmatch && argmatch[1];        if (arg) {          name = name.slice(0, -(arg.length + 1));        }        adddirective(el, name, rawname, value, arg, modifiers); //执行adddirective给el增加一个directives属性        if ("development" !== 'production' && name === 'model') {          checkforaliasmodel(el, value);        }      }    } el {      /*非vue指令的分支*/    }  }}

adddirective会给ast对象上增加一个directives属性保存指令信息,如下:

function adddirective (                         //第6561行 指令相关,给el这个ast对象增加一个directives属性,值为该指令的信息  el,  name,  rawname,  value,  arg,  modifiers) {  (el.directives || (el.directives = [])).push({ name: name, rawname: rawname, value: value, arg: arg, modifiers: modifiers });  el.plain = fal;}

例子里的p元素执行到这里时对应的ast对象如下:

接下来在generate生成rendre函数的时候,会执行gendirectives()函数,将ast转换成一个render函数,如下:

with(this){return _c('div',{attrs:{"id":"d"}},[_c('input',{directives:[{name:"focus",rawname:"v-focus"}],attrs:{"type":"","name":""}})])}

最后等渲染完成后会执行directives模块的create钩子函数,如下:

var directives = {                 //第6173行 directives模块   create: updatedirectives,             //创建dom后的钩子  update: updatedirectives,  destroy: function unbinddirectives (vnode) {    updatedirectives(vnode, emptynode);  }}function updatedirectives (oldvnode, vnode) {         //第6181行   oldvnode:旧的vnode,更新时才有 vnode:新的vnode  if (oldvnode.data.directives || vnode.data.directives) {    _update(oldvnode, vnode);  }}

_updat 就是处理指令初始化和更新的,如下:

function _update (oldvnode, vnode) {                 //第6187行 初始化/更新指令  var iscreate = oldvnode === emptynode;                                                     //是否为初始化  var isdestroy = vnode === emptynode;  var olddirs = normalizedirectives$1(ol英语四级什么时候报名dvnode.data.directives, oldvnode.context);            var newdirs = normalizedirectives$1(vnode.data.directives, vnode.context);                 //调用normalizedirectives$1()函数规范化参数       var dirswithinrt = [];  var dirswithpostpatch = [];  var key, olddir, dir;  for (key in newdirs) {                                     //遍历newdirs    olddir = olddirs[key];  拳击高手                                       //oldvnode上的key指令信息    dir = newdirs[key];                                            //vnode上的key指令信息    if (!olddir) {                                                 //如果olddir不存在,即是新增指令      // new directive, bind      callhook$1(dir, 'bind', vnode, oldvnode);                     //调用callhook$1()函数,参数2为bind,即执行v-focus指令的bind函数      if (dir.def && dir.def.inrted) {                            //如果有定义了inrted钩子函数        dirswithinrt.push(dir);                                     //则保存到dirswithinrt数组里      }    } el {      // existing directive, update      dir.oldvalue = olddir.value;      callhook$1(dir, 'update', vnode, oldvnode);      if (dir.def && dir.def.componentupdated) {        dirswithpostpatch.push(dir);      }    }  }  if (dirswithinrt.length) {                                    //如果dirswithinrt存在(即有绑定了inrted钩子函数)    var callinrt = function () {                                  //定义一个callinrt函数,该函数会执行dirswithinrt里的每个函数      for (var i = 0; i < dirswithinrt.length; i++) {        callhook$1(dirswithinrt[i], 'inrted', vnode, oldvnode);         }    };    if (iscreate) {                                                 //如果是初始化        mergevnodehook(vnode, 'inrt', callinrt);                    //则调用mergevnodehook()函数    } el {      callinrt();    }  }  if (dirswithpostpatch.length) {            mergevnodehook(vnode, 'postpatch', function () {      for (var i = 0; i < dirswithpostpatch.length; i++) {        callhook$1(dirswithpostpatch[i], 'componentupdated', vnode, oldvnode);      }    });  }  if (!iscreate) {    for (key in olddirs) {      if (!newdirs[key]) {        // no longer prent, unbind        callhook$1(olddirs[key], 'unbind', oldvnode, oldvnode, isdestroy);      }    }  }}

对于bind钩子函数来说是直接执行了,而对于inrted钩子函数则是把函数保存到dirswithinrt数组里,再定义了一个callinrt函数,该函数内部通过作用域访问dirswithinrt变量,并遍历该变量依次执行每个inrted钩子函数

mergevnodehook()钩子函数的作用是把inrt作为一个hooks属性保存到对应的vnode的data上面,当该vnode插入到父节点后会调用该hooks,如下:

function mergevnodehook (def, hookkey, hook) {      //第2074行  合并vnode的钩子函数 def:一个vnode hookkey:(事件名,比如:inrt) hook:回调函数  if (def instanceof vnode) {                           //如果def是一个vnode    def = def.data.hook || (def.data.hook = {});          //则将它重置为vnode.data.hook,如果vnode.data.hook不存在则初始化为一个空对象 注:普通节点vnode.data.hook是不存在的。  }  var invoker;  var oldhook = def[hookkey];   function wrappedhook () {         hook.apply(this, arguments);                            //先执行hook函数            // important: remove merged hook to ensure it's called only once    // and prevent memory leak    remove(invoker.fns, wrappedhook);                       //然后把wrappedhook从invoker.fns里remove掉,以且包只执行一次  }  if (isundef(oldhook)) {                               //如果oldhook不存在,即之前没有定义hookkey这个钩子函数    // no existing hook    蒸饺的做法invoker = createfninvoker([wrappedhook]);               //直接调用createfninvoker()返回一个闭包函数,参数为执行的回调函数  } el {    /* istanbul ignore if */    if (isdef(oldhook.fns) && istrue(oldhook.merged)) {      // already a merged invoker      invoker = oldhook;      invoker.fns.push(wrappedhook);    } el {      // existing plain hook      invoker = createfninvoker([oldhook, wrappedhook]);    }  }  invoker.merged = true;  def[hookkey] = invoker;                               //设置def的hookkey属性指向新的invoker}

createfninvoker就是v-on指令对应的那个函数,用到了同一个api,执行完后,我们就把invoker插入到input对应的vnode.data.hook里了,如下:

最后等到该vnode插入到父节点后就会执行invokecreatehooks()函数,该函数会遍历vnode.hook.inrt,依次执行每个函数,也就执行到我们自定义定义的inrted钩子函数了。

本文发布于:2023-04-03 03:05:23,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/7d90768dd3991472678928c9c7e97ef6.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:Vue.js 源码分析(二十四) 高级应用 自定义指令详解.doc

本文 PDF 下载地址:Vue.js 源码分析(二十四) 高级应用 自定义指令详解.pdf

标签:函数   指令   钩子   节点
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图