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       ;한 번 만 호출 하고 명령 과 요 소 를 풀 때 호출 합 니 다.
각 갈고리 함 수 는 엘(대응 하 는 DOM 노드 참조),binding(명령 에 대한 확장 정보,대상),vnode(이 노드 에 대응 하 는 가상 VN 오),oldVnode(이전 VNode,update 와 componentUpdated 갈고리 에서 만 사용 가능)등 네 개의 매개 변 수 를 가 질 수 있 습 니 다.
bid 갈고리 함수 가 실 행 될 때 이 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>
출력 은 다음 과 같 습 니 다:

input 요소 가 자동 으로 초점 을 얻 는 것 을 볼 수 있 습 니 다.콘 솔 출력 은 다음 과 같 습 니 다.

bid()갈고리 에 있어 서 부모 노드 는 가 져 올 수 없습니다.Vue 내부 에서 bid()갈 고 리 를 실행 한 후에 야 현재 요 소 를 부모 요소 의 하위 노드 에 삽입 할 수 있 기 때 문 입 니 다.
소스 코드 분석
템 플 릿 을 분석 하여 DOM 을 AST 대상 으로 변환 할 때 processAtts()함 수 를 실행 합 니 다.다음 과 같 습 니 다.

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 대상 은 다음 과 같 습 니 다.

다음은 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);
  }
}
_update 는 명령 을 초기 화하 고 업데이트 하 는 것 입 니 다.다음 과 같 습 니 다.

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);
      }
    }
  }
}
저자:대사 막 QQ:29269969
bind 갈고리 함수 에 있어 서 는 직접 실 행 됩 니 다.inserted 갈고리 함수 에 대해 서 는 함 수 를 dirs Withinert 배열 에 저장 하고 callInsert 함 수 를 정의 합 니 다.이 함수 내 부 는 역할 영역 을 통 해 dirs Withinert 변 수 를 방문 하고 이 변 수 를 옮 겨 다 니 며 각 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 에 삽입 합 니 다.다음 과 같 습 니 다.

마지막 으로 이 VNode 가 부모 노드 에 삽입 되면 invokeCreate Hooks()함 수 를 실행 합 니 다.이 함 수 는 VNode.hook.insert 를 옮 겨 다 니 며 각 함 수 를 순서대로 실행 합 니 다.또한 우리 가 정의 한 inserted 갈고리 함수 도 실 행 됩 니 다.
총결산
Vue.js 소스 코드 분석 에 관 한 사용자 정의 명령 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 Vue.js 사용자 정의 명령 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 바 랍 니 다!

좋은 웹페이지 즐겨찾기