vue響應(yīng)式原理

image.png

注意右側(cè)的調(diào)用隊(duì)列 進(jìn)行初始化的時(shí)候 this._init() 初始化的時(shí)候調(diào)用 initState() initData() initData中會(huì)調(diào)用根節(jié)點(diǎn)的observe 將數(shù)據(jù)變成響應(yīng)式對(duì)象


image.png
// initData()中調(diào)用根節(jié)點(diǎn)的 observe 
// observe data
  observe(data, true /* asRootData */);
  observe只處理 對(duì)象 
 */
function observe (value, asRootData) {
// 不是對(duì)象 或 是 VNode就返回 這是遞歸的出口
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  var ob;
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
// 這是一個(gè)observe的自己
    ob = value.__ob__;
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
//  創(chuàng)建Observer的對(duì)象
    ob = new Observer(value);
  }
  if (asRootData && ob) {
    ob.vmCount++;
  }
  return ob
}

// Observer 對(duì)象
var Observer = function Observer (value) {
  this.value = value;
// watch的管理者 收集依賴 和派發(fā)更新中使用
  this.dep = new Dep();
  this.vmCount = 0;
// 定義不可枚舉的值 作為標(biāo)識(shí)
  def(value, '__ob__', this);
  if (Array.isArray(value)) {
    if (hasProto) {
      protoAugment(value, arrayMethods);
    } else {
      copyAugment(value, arrayMethods, arrayKeys);
    }
    this.observeArray(value);
  } else {
  
    this.walk(value);
  }
};
// 如果是對(duì)象 每個(gè)對(duì)象中調(diào)用 defineReactive
Observer.prototype.walk = function walk (obj) {
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    defineReactive$$1(obj, keys[i]);
  }
};
// 如果是數(shù)組 遞歸調(diào)用observe每一個(gè)對(duì)象 所以都會(huì)走到 defineReactive
Observer.prototype.observeArray = function observeArray (items) {
  for (var i = 0, l = items.length; i < l; i++) {
    observe(items[i]);
  }
};

下面來(lái)看看defineReactive的代碼

function defineReactive$$1 (
  obj,
  key,
  val,
  customSetter,
  shallow
) {
// Dep是一個(gè)watch收集管理器
  var dep = new Dep();

  var property = Object.getOwnPropertyDescriptor(obj, key);
// 如果是不可枚舉的 就直接return 所以不可變更對(duì)象可以使用Object.frezone
  if (property && property.configurable === false) {
    return
  }
 // ...  
  var childOb = !shallow && observe(val);
// 利用es5的defineProperty  劫持getter/setter  getter的時(shí)候 通過(guò)dep.depend()建立依賴收集 setter的時(shí)候dep.notify() 派發(fā)更新
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val;

      if (getter && !setter) { return }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal);
      dep.notify();
    }
  });
}
// 派發(fā)更新 執(zhí)行堆棧dep.notify() watch.update() queueWatcher(this) flushSchedulerQueue() 執(zhí)行watcher的run方法 如下源碼所示
dep.notify
Dep.prototype.notify = function notify () {
  // stabilize the subscriber list first
  var subs = this.subs.slice();
  if (process.env.NODE_ENV !== 'production' && !config.async) {
    // subs aren't sorted in scheduler if not running async
    // we need to sort them now to make sure they fire in correct
    // order
    subs.sort(function (a, b) { return a.id - b.id; });
  }
  for (var i = 0, l = subs.length; i < l; i++) {
// subs是一個(gè)watch的數(shù)組
    subs[i].update();
  }
};


/**
 * Flush both queues and run the watchers.
 */
function flushSchedulerQueue () {
  currentFlushTimestamp = getNow();
  flushing = true;
  var watcher, id;

  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child)
  // 2. A component's user watchers are run before its render watcher (because
  //    user watchers are created before the render watcher)
  // 3. If a component is destroyed during a parent component's watcher run,
  //    its watchers can be skipped.
  queue.sort(function (a, b) { return a.id - b.id; });

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index];
    if (watcher.before) {
      watcher.before();
    }
    id = watcher.id;
    has[id] = null;
    watcher.run();
    // in dev build, check and stop circular updates.
    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
      circular[id] = (circular[id] || 0) + 1;
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn(
          'You may have an infinite update loop ' + (
            watcher.user
              ? ("in watcher with expression \"" + (watcher.expression) + "\"")
              : "in a component render function."
          ),
          watcher.vm
        );
        break
      }
    }
  }
image.png

Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get();
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
var oldValue = this.value;
this.value = value;
if (this.user) {
try {
// 執(zhí)行用戶定義的watch的回調(diào) 參數(shù) newValue, oldValue
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for watcher "" + (this.expression) + """));
}
} else {
this.cb.call(this.vm, value, oldValue);
}
}
}
};

// mountComponent vue數(shù)據(jù)更新驅(qū)動(dòng)視圖的原理
// 定義渲染watcher  watch的get函數(shù) 對(duì)應(yīng)的updateComponent  會(huì)改變視圖的變化 既是渲染watch初始化的時(shí)候被訂閱了對(duì)應(yīng)的響應(yīng)式數(shù)據(jù) 數(shù)據(jù)發(fā)生了變化會(huì)觸發(fā)這個(gè)視圖更新 
// 這里會(huì)執(zhí)行 watch的run 會(huì)執(zhí)行 this.get 求職 會(huì)執(zhí)行 updateComponent函數(shù) 這樣會(huì)觸發(fā)視圖的更新
簡(jiǎn)單來(lái)說(shuō) 響應(yīng)數(shù)據(jù)發(fā)生變化 會(huì)觸發(fā) defineReactive 的setter中this.get 然后會(huì)觸發(fā) 渲染watch的updateComponent 從而達(dá)到視圖的變化
    updateComponent = function () {
      vm._update(vm._render(), hydrating);
    };
  }

  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before: function before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate');
      }
    }
  }, true /* isRenderWatcher */);
  hydrating = false;

在vue中, 數(shù)據(jù)模型下的所有屬性,會(huì)被Vue使用Object.defineProperty(Vue3使用Proxy)進(jìn)行數(shù)據(jù)劫持代碼里。響應(yīng)式的機(jī)制是觀察者模式, 數(shù)據(jù)是被觀察的一方, 一旦發(fā)生變化, 通知所有的觀察者, 這樣觀察者可以做出響應(yīng), 當(dāng)觀察者為視圖的時(shí)候,會(huì)更新視圖。

Vue的響應(yīng)式系統(tǒng)的三個(gè)重要概念, Dep, Observer, Watcher.

Dep 調(diào)度中心-訂閱器

Dep是負(fù)責(zé)調(diào)度和訂閱watcher的 dep在getter觸發(fā)了dep.depend 收集依賴 在setter的時(shí)候 觸發(fā)了dep.notify 派發(fā)更新

Observer 發(fā)布者

Observer是發(fā)布者, 主要作用是vm初始化的時(shí)候, 調(diào)用defineReactive函數(shù), 使用Object.defineProperty方法對(duì)對(duì)象的每一個(gè)子屬性進(jìn)行數(shù)據(jù)劫持、監(jiān)聽(tīng), 就是為每個(gè)屬性添加上gettersetter, 使屬性變成響應(yīng)式。

Watcher-觀察者

Watcher 扮演的角色是訂閱者/觀察者,他的主要作用是為觀察屬性提供回調(diào)函數(shù)以及收集依賴,當(dāng)被觀察的值發(fā)生變化時(shí),會(huì)接收到來(lái)自調(diào)度中心Dep的通知,從而觸發(fā)回調(diào)函數(shù)。

而Watcher又分為三類(lèi),normal-watcher、 computed-watcherrender-watcher。

normal-watcher:在組件鉤子函數(shù)watch中定義,即監(jiān)聽(tīng)的屬性改變了,都會(huì)觸發(fā)定義好的回調(diào)函數(shù)。
computed-watcher:在組件鉤子函數(shù)computed中定義的,每一個(gè)computed屬性,最后都會(huì)生成一個(gè)對(duì)應(yīng)的Watcher對(duì)象,但是這類(lèi)Watcher有個(gè)特點(diǎn):當(dāng)計(jì)算屬性依賴于其他數(shù)據(jù)時(shí),屬性并不會(huì)立即重新計(jì)算,只有之后其他地方需要讀取屬性的時(shí)候,它才會(huì)真正計(jì)算,即具備lazy(懶計(jì)算)特性。
render-watcher:每一個(gè)組件都會(huì)有一個(gè)render-watcher, 當(dāng)data/computed中的屬性改變的時(shí)候,會(huì)調(diào)用該Watcher來(lái)更新組件的視圖。
這三種Watcher也有固定的執(zhí)行順序,分別是:computed-render -> normal-watcher -> render-watcher。這樣就能盡可能的保證,在更新組件視圖的時(shí)候,computed 屬性已經(jīng)是最新值了,如果 render-watcher 排在 computed-render 前面,就會(huì)導(dǎo)致頁(yè)面更新的時(shí)候 computed 值為舊數(shù)據(jù)。

小結(jié)

image.png

Observer 負(fù)責(zé)將數(shù)據(jù)進(jìn)行攔截,Watcher 負(fù)責(zé)訂閱,觀察數(shù)據(jù)變化, Dep 負(fù)責(zé)接收訂閱并通知 Observer 和接收發(fā)布并通知所有 Watcher。

整理的文章 可以參考我的語(yǔ)雀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容