国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - vue.js - Vue.js源碼分析之自定義指令詳解

Vue.js源碼分析之自定義指令詳解

2022-03-02 16:42大沙漠 vue.js

這篇文章主要給大家介紹了關于Vue.js源碼分析之自定義指令的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

除了核心功能默認內置的指令 (v-model 和 v-show),Vue 也允許注冊自定義指令。

官網介紹的比較抽象,顯得很高大上,我個人對自定義指令的理解是:當自定義指令作用在一些DOM元素或組件上時,該元素在初次渲染、插入到父節點、更新、解綁時可以執行一些特定的操作(鉤子函數()

自定義指令有兩種注冊方式,一種是全局注冊,使用Vue.directive(指令名,配置參數)注冊,注冊之后所有的Vue實例都可以使用,另一種是局部注冊,在創建Vue實例時通過directives屬性創建局部指令,局部自定義指令只能在當前Vue實例內使用

自定義指令可以綁定如下鉤子函數:

?bind      ;只調用一次,元素渲染成DOM節點后,執行directives模塊的初始化工作時調用,在這里可以進行一次性的初始化設置。
?inserted       ;被綁定元素插入父節點時調用 (僅保證父節點存在,但不一定已被插入文檔中)。
?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 charset="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);},                      //打印父節點
            inserted: function (el) {console.log(el.parentElement);el.focus()}      //打印父節點,并將當前元素處于聚焦狀態
        })
        var app = new Vue({el:"#d"})
    </script>
</body>
</html>

輸出如下:

Vue.js源碼分析之自定義指令詳解

可以看到input元素自動獲得焦點了,控制臺輸出如下:

Vue.js源碼分析之自定義指令詳解

可以看到對于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 = parseModifiers(name);
      if (modifiers) {
        name = name.replace(modifierRE, "");
      }
      if (bindRE.test(name)) { // v-bind                          //bindRD等于/^:|^v-bind:/ ,即該屬性是v-bind指令時
        /*v-bind的分支*/
      } else if (onRE.test(name)) { // v-on
        /*v-on的分支*/
      } else { // normal directives
        name = name.replace(dirRE, "");                         //去掉指令前綴,比如v-show執行后等于show
        // parse 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);
        }
      }
    } else {
      /*非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 = false;
}

例子里的p元素執行到這里時對應的AST對象如下:

Vue.js源碼分析之自定義指令詳解

接下來在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(oldVnode.data.directives, oldVnode.context);          
  var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);                 //調用normalizeDirectives$1()函數規范化參數
     
  var dirsWithInsert = [];
  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.inserted) {                            //如果有定義了inserted鉤子函數
        dirsWithInsert.push(dir);                                     //則保存到dirsWithInsert數組里
      }
    } else {
      // existing directive, update
      dir.oldValue = oldDir.value;
      callHook$1(dir, "update", vnode, oldVnode);
      if (dir.def && dir.def.componentUpdated) {
        dirsWithPostpatch.push(dir);
      }
    }
  }
  if (dirsWithInsert.length) {                                    //如果dirsWithInsert存在(即有綁定了inserted鉤子函數)
    var callInsert = function () {                                  //定義一個callInsert函數,該函數會執行dirsWithInsert里的每個函數
      for (var i = 0; i < dirsWithInsert.length; i++) {
        callHook$1(dirsWithInsert[i], "inserted", vnode, oldVnode);   
      }
    };
    if (isCreate) {                                                 //如果是初始化  
      mergeVNodeHook(vnode, "insert", callInsert);                    //則調用mergeVNodeHook()函數
    } else {
      callInsert();
    }
  }

  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 present, unbind
        callHook$1(oldDirs[key], "unbind", oldVnode, oldVnode, isDestroy);
      }
    }
  }
}

writer by:大沙漠 QQ:22969969

對于bind鉤子函數來說是直接執行了,而對于inserted鉤子函數則是把函數保存到dirsWithInsert數組里,再定義了一個callInsert函數,該函數內部通過作用域訪問dirsWithInsert變量,并遍歷該變量依次執行每個inserted鉤子函數

mergeVNodeHook()鉤子函數的作用是把insert作為一個hooks屬性保存到對應的Vnode的data上面,當該Vnode插入到父節點后會調用該hooks,如下:

function mergeVNodeHook (def, hookKey, hook) {      //第2074行  合并VNode的鉤子函數 def:一個VNode hookKey:(事件名,比如:insert) 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()返回一個閉包函數,參數為執行的回調函數
  } else {
    /* istanbul ignore if */
    if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {
      // already a merged invoker
      invoker = oldHook;
      invoker.fns.push(wrappedHook);
    } else {
      // 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里了,如下:

Vue.js源碼分析之自定義指令詳解

最后等到該VNode插入到父節點后就會執行invokeCreateHooks()函數,該函數會遍歷VNode.hook.insert,依次執行每個函數,也就執行到我們自定義定義的inserted鉤子函數了。

總結

到此這篇關于Vue.js源碼分析之自定義指令的文章就介紹到這了,更多相關Vue.js自定義指令內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/greatdesert/p/11171785.html

延伸 · 閱讀

精彩推薦
  • vue.js用vite搭建vue3應用的實現方法

    用vite搭建vue3應用的實現方法

    這篇文章主要介紹了用vite搭建vue3應用的實現方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下...

    Asiter7912022-01-22
  • vue.jsVue項目中實現帶參跳轉功能

    Vue項目中實現帶參跳轉功能

    最近做了一個手機端系統,其中遇到了父頁面需要攜帶參數跳轉至子頁面的問題,現已解決,下面分享一下實現過程,感興趣的朋友一起看看吧...

    YiluRen丶4302022-03-03
  • vue.jsVue2.x-使用防抖以及節流的示例

    Vue2.x-使用防抖以及節流的示例

    這篇文章主要介紹了Vue2.x-使用防抖以及節流的示例,幫助大家更好的理解和學習使用vue框架,感興趣的朋友可以了解下...

    Kyara6372022-01-25
  • vue.jsVue2.x 項目性能優化之代碼優化的實現

    Vue2.x 項目性能優化之代碼優化的實現

    這篇文章主要介紹了Vue2.x 項目性能優化之代碼優化的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    優小U9632022-02-21
  • vue.js梳理一下vue中的生命周期

    梳理一下vue中的生命周期

    看過很多人講vue的生命周期,但總是被繞的云里霧里,尤其是自學的同學,可能js的基礎也不是太牢固,聽起來更是吃力,那我就已個人之淺見,以大白話...

    CRMEB技術團隊7992021-12-22
  • vue.js詳解vue 表單綁定與組件

    詳解vue 表單綁定與組件

    這篇文章主要介紹了vue 表單綁定與組件的相關資料,幫助大家更好的理解和學習使用vue框架,感興趣的朋友可以了解下...

    Latteitcjz6432022-02-12
  • vue.jsVue多選列表組件深入詳解

    Vue多選列表組件深入詳解

    這篇文章主要介紹了Vue多選列表組件深入詳解,這個是vue的基本組件,有需要的同學可以研究下...

    yukiwu6752022-01-25
  • vue.jsVue中引入svg圖標的兩種方式

    Vue中引入svg圖標的兩種方式

    這篇文章主要給大家介紹了關于Vue中引入svg圖標的兩種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的...

    十里不故夢10222021-12-31
主站蜘蛛池模板: 国产视频一区在线 | 精品人成 | 国产一区二区精品在线 | 免费的av电影 | 亚洲精品视频大全 | 国内美女人妻一级毛片免费看 | 色播av| 黄色毛片免费网站 | 国产综合在线观看视频 | 依人在线| 国产精品日韩高清伦字幕搜索 | 亚洲精品电影在线观看 | 黄色欧美视频 | 欧美日韩在线视频观看 | 国产精品区二区三区日本 | 人人澡人人射 | 久久久亚洲综合 | 日韩成人 | 色偷偷888欧美精品久久久 | 国产精品成人一区 | 国产91久久精品一区二区 | 国产成人精品网站 | 国产精品一级 | 一区二区三区中文字幕 | 免费看操片 | 黄色网页观看 | 久久久天堂国产精品 | 国产成人一区二区啪在线观看 | 国产精品久久久久久久久久久天堂 | 欧美日韩一区二区三区在线观看 | 亚洲视频天堂 | 午夜成人在线视频 | 美女在线视频一区二区 | 日韩久久精品一区二区 | 欧美日韩一区二区三区不卡视频 | 综合精品久久久 | 国产精品久久综合 | 在线国产视频 | 日韩a电影 | av中文字幕在线观看 | 欧美成人免费视频 |