Vue 양 방향 데이터 바 인 딩 원리 분석

기본 원리
Vue.데이터 납치 와 게시 자-구독 자 모드 를 결합 하 는 방식 으로 Object.defineProperty()를 통 해 각 속성의 setter 와 getter 를 납치 하고 데이터 가 변동 할 때 구독 자 에 게 메 시 지 를 발표 하여 해당 함수 의 리 셋 을 촉발 합 니 다.
사고의 방향 을 정리 하 다.
mvvm 의 양 방향 연결 을 실현 하려 면 다음 과 같은 몇 가 지 를 실현 해 야 합 니 다.
1.데이터 감청 기 Observer 를 실현 하여 대상 의 모든 속성 을 감청 할 수 있 고 변화 가 발생 하면 최신 값 을 받 아 구독 자 에 게 알 릴 수 있 습 니 다.
2.해석 기 Compile 을 실현 하고 모든 하위 요소 노드 의 명령 을 스 캔 하고 해석 하 며 템 플 릿 명령 에 따라 데 이 터 를 교체 하고 보기 와 해당 하 는 리 셋 함 수 를 초기 화 합 니 다.
3.Watcher 를 실현 하고 Observer 와 Compile 의 다리 로 서 속성 변동 알림 을 구독 할 수 있 으 며 명령 에 연 결 된 리 셋 함 수 를 실행 하여 보 기 를 업데이트 할 수 있 습 니 다.
4.mvvm 의 입구,상기 3 자 통합
흐름 도 는 다음 과 같다.

분포 실현
1. MVVM.js

function MVVM(options) {
  this.$options = options || {};
  var data = this._data = this.$options.data;
  var me = this;

  //     
  //    vm.xxx -> vm._data.xxx
  Object.keys(data).forEach(function(key) {
    me._proxyData(key);
  });
  //       
  //     Object.defineProperty    
  this._initComputed();

  observe(data, this);

  this.$compile = new Compile(options.el || document.body, this)
}

MVVM.prototype = {
  $watch: function(key, cb, options) {
    new Watcher(this, key, cb);
  }
}

MVVM 입구 파일,Observer/copile/Watcher 세 가 지 를 통합 하여 데이터 변화->보기 업데이트;보기 변화->데이터 변 경 된 양 방향 바 인 딩 효과(갈고리 함 수 를 결합 하여 Vue 생명주기 의 각 단계 의 작용 을 이해한다)
2. Observer.js

function Observer(data) {
  Object.keys(data).forEach(function() {
    defineReactive(data, key, data[key]);
  });
}
function defineReactive (data, key, val) {
  var dep = new Dep();
  var childObj = observe(val);

  Object.defineProperty(data, key, {
    enumerable: true, //    
    configurable: false, //    define
    get: function() {
      if (Dep.target) {
        dep.depend();
      }
      return val;
    },
    set: function(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      //     object  ,    
      childObj = observe(newVal);
      //      
      dep.notify();
    }
  });
}

모니터링 이 필요 한 대상 의 모든 속성 을 재 귀적 으로 옮 겨 다 니 며 Object.defineProperty 를 통 해 setter 와 getter 를 설정 합 니 다.새로운 속성 값 을 설정 할 때 해당 setter 를 터치 하여 구독 자 에 게 알 립 니 다.

function Dep() {
  this.id = uid++;
  this.subs = [];
}
Dep.prototype = {
  addSub: function(sub) {
    this.subs.push(sub);
  },
  depend: function() {
    Dep.target.addDep(this);
  },
  notify: function() {
    this.subs.forEach(function(sub) {
      sub.update();
    });
  }
};
구독 자 모드,각 속성 은 하나의 Dep 를 유지 하고 자신의 구독 자(즉 watcher)를 기록 하 며 notify 는 모든 구독 자 에 게 해당 하 는 update 방법 을 실행 하고 보 기 를 업데이트 하 라 고 알려 줍 니 다.
3. Compile.js
Compile 은 두 가지 일 을 했다.
1.템 플 릿 명령 을 해석 하고 변 수 를 교체 하 며 렌 더 링 보 기 를 초기 화 합 니 다.
2.watcher 를 생 성하 고 리 셋 함 수 를 등록 하 며 감청 데 이 터 를 추가 하 는 구독 자,데이터 변동 시 보 기 를 업데이트 합 니 다.

분석 절 차 는 다음 과 같다.
1.DOM 을 문서 조각 fragment 로 변환 하여 조회 효율 향상
2.모든 요소 노드 와 하위 노드 를 옮 겨 다 니 며 해당 하 는 명령 렌 더 링 함수 렌 더 링 을 호출 하고 해당 하 는 명령 업데이트 함수 로 바 인 딩 합 니 다.
3.fragment 를 실제 DOM 에 추가
요소 옮 겨 다 니 기

function compileElement (el) {
  var childNodes = el.childNodes,
    me = this;
  [].slice.call(childNodes).forEach(function(node) {
    var text = node.textContent;
    var reg = /\{\{(.*)\}\}/;
    //       
    if (me.isElementNode(node)) {
      me.compile(node);
    // {{}}    
    } else if (me.isTextNode(node) && reg.test(text)) {
      me.compileText(node, RegExp.$1);
    }
    //        
    if (node.childNodes && node.childNodes.length) {
      me.compileElement(node);
    }
  });
}
컴 파일 요소 노드

compile: function(node) {
  var nodeAttrs = node.attributes,
    me = this;
  [].slice.call(nodeAttrs).forEach(function(attr) {
    //    v-xxx  
    // <span v-html="content"></span>
    var attrName = attr.name; // v-html
    if (me.isDirective(attrName)) {
      var exp = attr.value; // content
      var dir = attrName.substring(2);
      //     
      if (me.isEventDirective(dir)) {
        compileUtil.eventHandler(node, me.$vm, exp, dir);
        //     
      } else {
        compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
      }
      node.removeAttribute(attrName);
    }
  });
}
명령 처리 및 업데이트 함수

var compileUtil = {
  html: function(node, vm, exp) {
    this.bind(node, vm, exp, 'html');
  },
  
  bind: function(node, vm, exp, dir) {
    var updaterFn = updater[dir + 'Updater'];
    //         
    updaterFn && updaterFn(node, this._getVMVal(vm, exp));
    //    Watcher,     
    new Watcher(vm, exp, function(value, oldValue) {
      //            
      updaterFn && updaterFn(node, value, oldValue);
    });
  },
}

var Updater = {
  htmlUpdater: function(node, value) {
    node.innerHTML = typeof value == 'undefined' ? '' : value;
  }
}

4. Watcher.js
Watcher 는 Observer 와 Compile 간 통신 의 다리 로 서 속성 변화 구독 자로 서 다음 과 같은 일 을 했 습 니 다.
1.자신 예화 시 속성 구독 기 집합 dep 에 자신 추가
2.자신 에 게 update 방법 이 필요 합 니 다.
3.dep.notice 를 호출 할 때 watcher 는 자신의 update 를 호출 하여 Compile 에서 정의 하 는 리 셋 을 실행 합 니 다.

function Watcher(vm, expOrFn, cb) {
  this.cb = cb;
  this.vm = vm;
  this.expOrFn = expOrFn;
  this.value = this.get();
}

Watcher.prototype = {
  update: function() {
    this.run();
  },
  run: function() {
    var value = this.get();
    var oldVal = this.value;
    if (value !== oldVal) {
      this.value = value;
      this.cb.call(this.vm, value, oldVal);
    }
  },
  get: function() {
    Dep.target = this;
    var value = this.getter.call(this.vm, this.vm);
    Dep.target = null;
    return value;
  }
};

여기 서 주의해 야 할 점 은 워 치 를 예화 할 때 get 방법 을 호출 하고 Dep.target=curInstance 를 통 해 속성 값 을 얻 은 getter 방법 을 강제로 촉발 하여 속성의 구독 기 에 현재 워 치 인 스 턴 스 를 추가 하 는 것 입 니 다.
작은 매듭
양 방향 바 인 딩 의 원 리 는 간단 합 니 다.데이터 납 치 를 통 해 새로운 속성 치 를 설정 할 때 구독 자 를 통 해 보 기 를 업데이트 합 니 다.명령 을 컴 파일 하고 변 수 를 교체 하 며 업데이트 함 수 를 구독 자 에 게 연결 합 니 다.이벤트 바 인 딩 에 대응 하여 addEventListener 를 호출 하여 감청 합 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기