欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue中watch和computed為什么能監(jiān)聽到數(shù)據(jù)的改變以及不同之處

 更新時間:2019年12月27日 09:27:09   作者:Jade_g  
這篇文章主要介紹了vue中watch和computed為什么能監(jiān)聽到數(shù)據(jù)的改變以及不同之處,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下

先來個流程圖,水平有限,湊活看吧-_-||

首先在創(chuàng)建一個Vue應(yīng)用時:

var app = new Vue({
 el: '#app',
 data: {
  message: 'Hello Vue!'
 }
})

Vue構(gòu)造函數(shù)源碼:

//創(chuàng)建Vue構(gòu)造函數(shù)
function Vue (options) {
 if (!(this instanceof Vue)
 ) {
  warn('Vue is a constructor and should be called with the `new` keyword');
 }
 this._init(options);
}
//_init方法,會初始化data,watch,computed等
Vue.prototype._init = function (options) {
 var vm = this;
 // a uid
 vm._uid = uid$3++;
 ......
 // expose real self
 vm._self = vm;
 initLifecycle(vm);
 initEvents(vm);
 initRender(vm);
 callHook(vm, 'beforeCreate');
 initInjections(vm); // resolve injections before data/props
 initState(vm);
 ......
};

在initState方法中會初始化data、watch和computed,并調(diào)用observe函數(shù)監(jiān)聽data(Object.defineProperty):

function initState (vm) {
 vm._watchers = [];
 var opts = vm.$options;
 if (opts.props) { initProps(vm, opts.props); }
 if (opts.methods) { initMethods(vm, opts.methods); }
 if (opts.data) {
  initData(vm);//initData中也會調(diào)用observe方法
 } else {
  observe(vm._data = {}, true /* asRootData */);
 }
 if (opts.computed) { initComputed(vm, opts.computed); }
 if (opts.watch && opts.watch !== nativeWatch) {
  initWatch(vm, opts.watch);
 }
}

1、observe

observe在initState 時被調(diào)用,為vue實例的data屬性值創(chuàng)建getter、setter函數(shù),在setter中dep.depend會把watcher實例添加到Dep實例的subs屬性中,在getter中會調(diào)用dep.notify,調(diào)用watcher的update方法。

/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 * 該函數(shù)在initState中有調(diào)用
 */
function observe (value, asRootData) {
 if (!isObject(value) || value instanceof VNode) {
  return
 }
 var ob;
 if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
  ob = value.__ob__;
 } else if (
  shouldObserve &&
  !isServerRendering() &&
  (Array.isArray(value) || isPlainObject(value)) &&
  Object.isExtensible(value) &&
  !value._isVue
 ) {
  ob = new Observer(value);
 }
 if (asRootData && ob) {
  ob.vmCount++;
 }
 re * Observer class that is attached to each observed
 * object. Once attached, the observer converts the target
 * object's property keys into getter/setters that
 * collect dependencies and dispatch updates.
 */
var Observer = function Observer (value) {
 this.value = value;
 this.dep = new Dep();
 this.vmCount = 0;
 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);
 }
};
/**
 * Walk through all properties and convert them into
 * getter/setters. This method should only be called when
 * value type is Object.
 */
Observer.prototype.walk = function walk (obj) {
 var keys = Object.keys(obj);
 for (var i = 0; i < keys.length; i++) {
  defineReactive$$1(obj, keys[i]);
 }
};
/**
 * Define a reactive property on an Object.
 */
function defineReactive$$1 (
 obj,
 key,
 val,
 customSetter,
 shallow
) {
 var dep = new Dep();
 var property = Object.getOwnPropertyDescriptor(obj, key);
 if (property && property.configurable === false) {
  return
 }
 // cater for pre-defined getter/setters
 var getter = property && property.get;
 var setter = property && property.set;
 if ((!getter || setter) && arguments.length === 2) {
  val = obj[key];
 }
 var childOb = !shallow && observe(val);
 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter () {
   var value = getter ? getter.call(obj) : val;
    //Dep.target 全局變量指向的就是指向當前正在解析生成的 Watcher
    //會執(zhí)行到dep.addSub,將Watcher添加到Dep對象的Watcher數(shù)組中
   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;
   /* eslint-disable no-self-compare */
   if (newVal === value || (newVal !== newVal && value !== value)) {
    return
   }
   /* eslint-enable no-self-compare */
   if (customSetter) {
    customSetter();
   }
   // #7981: for accessor properties without setter
   if (getter && !setter) { return }
   if (setter) {
    setter.call(obj, newVal);
   } else {
    val = newVal;
   }
   childOb = !shallow && observe(newVal);
   dep.notify();//如果數(shù)據(jù)被重新賦值了, 調(diào)用 Dep 的 notify 方法, 通知所有的 Watcher
 } }); }

2、Dep

Watcher的update方法是在new Dep的notify的方法中被調(diào)用的

/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 */
var Dep = function Dep () {
 this.id = uid++;
 this.subs = [];
};
//設(shè)置某個Watcher的依賴
//這里添加Dep.target,用來判斷是不是Watcher的構(gòu)造函數(shù)調(diào)用
//也就是其this.get調(diào)用
Dep.prototype.depend = function depend () {
 if (Dep.target) {
  Dep.target.addDep(this);
 }
};
//在該方法中會觸發(fā)subs的update方法
Dep.prototype.notify = function notify () {
 // stabilize the subscriber list first
 var subs = this.subs.slice();
 if (!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[i].update();
 }
};

3、watch

初始化watch,函數(shù)中會調(diào)用createWatcher,createWatcher會調(diào)用$watch,$watch調(diào)用new Watcher實例。

function initWatch (vm, watch) {
 for (var key in watch) {
  var handler = watch[key];
  if (Array.isArray(handler)) {
   for (var i = 0; i < handler.length; i++) {
    createWatcher(vm, key, handler[i]);
   }
  } else {
   createWatcher(vm, key, handler);
  }
 }
}
function createWatcher (
 vm,
 expOrFn,
 handler,
 options
) {
 if (isPlainObject(handler)) {
  options = handler;
  handler = handler.handler;
 }
 if (typeof handler === 'string') {
  handler = vm[handler];
 }
 return vm.$watch(expOrFn, handler, options)
}
Vue.prototype.$watch = function (
 expOrFn,
 cb,
 options
) {
 var vm = this;
 if (isPlainObject(cb)) {
  return createWatcher(vm, expOrFn, cb, options)
 }
 options = options || {};
 options.user = true;
 var watcher = new Watcher(vm, expOrFn, cb, options);
 if (options.immediate) {
  try {
   cb.call(vm, watcher.value);
  } catch (error) {
   handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
  }
 }
 return function unwatchFn () {
  watcher.teardown();
 }
};
}

2、computed

初始化computed,調(diào)用new Watcher(),并通過defineComputed函數(shù)將計算屬性掛載到vue實例上,使計算屬性可以在模板中使用

var computedWatcherOptions = { lazy: true }
function initComputed (vm, computed) {
 // $flow-disable-line
 var watchers = vm._computedWatchers = Object.create(null);
 // computed properties are just getters during SSR
 var isSSR = isServerRendering();
 for (var key in computed) {
  var userDef = computed[key];
  var getter = typeof userDef === 'function' ? userDef : userDef.get;
  //getter也就是computed的函數(shù)
  if (getter == null) {
   warn(
    ("Getter is missing for computed property \"" + key + "\"."),
    vm
   );
  }
  if (!isSSR) {
   // create internal watcher for the computed property.
   watchers[key] = new Watcher(
    vm,
    getter || noop,
    noop,
    computedWatcherOptions
   );
  }
  //組件定義的計算屬性已在
  //組件原型。我們只需要定義定義的計算屬性
  //在這里實例化。
  if (!(key in vm)) {
   defineComputed(vm, key, userDef);
  } else {
   if (key in vm.$data) {
    warn(("The computed property \"" + key + "\" is already defined in data."), vm);
   } else if (vm.$options.props && key in vm.$options.props) {
    warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
   }
  }
 }
}
function defineComputed (
 target,
 key,
 userDef
) {
 var shouldCache = !isServerRendering();//true
 if (typeof userDef === 'function') {
  sharedPropertyDefinition.get = shouldCache
   ? createComputedGetter(key)
   : createGetterInvoker(userDef);
  sharedPropertyDefinition.set = noop;
 } else {
  sharedPropertyDefinition.get = userDef.get
   ? shouldCache && userDef.cache !== false
    ? createComputedGetter(key)
    : createGetterInvoker(userDef.get)
   : noop;
  sharedPropertyDefinition.set = userDef.set || noop;
 }
 if (sharedPropertyDefinition.set === noop) {
  sharedPropertyDefinition.set = function () {
   warn(
    ("Computed property \"" + key + "\" was assigned to but it has no setter."),
    this
   );
  };
 }
 Object.defineProperty(target, key, sharedPropertyDefinition);
}
//computed的getter函數(shù),在模板獲取對應(yīng)computed數(shù)據(jù)時會調(diào)用
function createComputedGetter (key) {
 return function computedGetter () {
  var watcher = this._computedWatchers && this._computedWatchers[key];
  if (watcher) {
   if (watcher.dirty) {//true
    watcher.evaluate();//該方法會調(diào)用watcher.get方法,也就是computed對應(yīng)的函數(shù)
   }
   if (Dep.target) {
    watcher.depend();
   }
   return watcher.value
  }
 }
}

通過以上代碼可以看到watch和computed都是通過new Watcher實例實現(xiàn)數(shù)據(jù)的監(jiān)聽的,但是computed的options中l(wèi)azy為true,這個參數(shù)導致它們走的是兩條不同路線。

computed:模板獲取數(shù)據(jù)時,觸發(fā)其getter函數(shù),最終調(diào)用watcher.get,也就是調(diào)用對應(yīng)回調(diào)函數(shù)。

watch:模板獲取數(shù)據(jù)時,觸發(fā)其getter函數(shù),將watcher添加到對應(yīng)的Dep.subs中,在之后setter被調(diào)用時,Dep.notify通知所有watcher進行update,最終調(diào)用watcher.cb,也就是調(diào)用對應(yīng)回調(diào)函數(shù)。

3、Watcher

構(gòu)造函數(shù)在是watch時,會最后調(diào)用this.get,會觸發(fā)屬性的getter函數(shù),將該Watcher添加到Dep的subs中,用于通知數(shù)據(jù)變動時調(diào)用。

調(diào)用Watcher實例的update方法會觸發(fā)其run方法,run方法中會調(diào)用觸發(fā)函數(shù)。其depend方法會調(diào)用new Dep的depend方法,dep的depend會調(diào)用Watcher的addDep方法,最終會把該watcher實例添加到Dep的subs屬性中

/**
  *觀察者解析表達式,收集依賴項,
  *并在表達式值更改時激發(fā)回調(diào)。
  *這用于$watch()api和指令。
  */
 var Watcher = function Watcher (
  vm,
  expOrFn,
  cb,
  options,
  isRenderWatcher
 ) {
  this.vm = vm;
 ......
  this.cb = cb;//觸發(fā)函數(shù)
  this.id = ++uid$2; // uid for batching
  this.active = true;
  this.dirty = this.lazy; // for lazy watchers
 ......
  this.value = this.lazy ? undefined ? this.get();//computed會返回undefined,而watch會執(zhí)行Watcher.get
 };
 /**
  * Scheduler job interface.
  * Will be called by the scheduler.
  * 該方法會執(zhí)行觸發(fā)函數(shù)
  */
 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 {
      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);
    }
   }
  }
 };
 /**
  * Evaluate the getter, and re-collect dependencies.
  */
 Watcher.prototype.get = function get () {
  pushTarget(this);
  var value;
  var vm = this.vm;
  try {
   value = this.getter.call(vm, vm);
  } catch (e) {
   if (this.user) {
    handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
   } else {
    throw e
   }
  } finally {
   // "touch" every property so they are all tracked as
   // dependencies for deep watching
   if (this.deep) {
    traverse(value);
   }
   popTarget();
   this.cleanupDeps();
  }
  return value
 };
 /**
  * Subscriber interface.
  * Will be called when a dependency changes.
  * 在方法中調(diào)用Watcher的run方法
  */
 Watcher.prototype.update = function update () {
  /* istanbul ignore else */
  if (this.lazy) {
   this.dirty = true;
  } else if (this.sync) {
   this.run();
  } else {
   queueWatcher(this);//該方法最終也會調(diào)用run方法
  }
 };
 /**
  * Depend on all deps collected by this watcher.會調(diào)用new Dep的depend方法,dep的depend會調(diào)用Watcher的addDep方法
  */
 Watcher.prototype.depend = function depend () {
  var i = this.deps.length;
  while (i--) {
   this.deps[i].depend();
  }
 };
 /**
  * Add a dependency to this directive.
  */
 Watcher.prototype.addDep = function addDep (dep) {
  var id = dep.id;
  if (!this.newDepIds.has(id)) {
   this.newDepIds.add(id);
   this.newDeps.push(dep);
   if (!this.depIds.has(id)) {
    dep.addSub(this);
   }
  }
 };

總結(jié)

以上所述是小編給大家介紹的vue中watch和computed為什么能監(jiān)聽到數(shù)據(jù)的改變以及不同之處,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!

相關(guān)文章

  • Vue使用JSEncrypt實現(xiàn)rsa加密及掛載方法

    Vue使用JSEncrypt實現(xiàn)rsa加密及掛載方法

    這篇文章主要介紹了Vue使用JSEncrypt實現(xiàn)rsa加密及掛載方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • VUE中filters過濾器的兩種用法實例

    VUE中filters過濾器的兩種用法實例

    vue中過濾器的作用可被用于一些常見的文本格式化,也就是修飾文本,但是文本內(nèi)容不會改變,下面這篇文章主要給大家介紹了關(guān)于VUE中filters過濾器的兩種用法,需要的朋友可以參考下
    2022-04-04
  • vue自定義穿梭框支持遠程滾動加載的實現(xiàn)方法

    vue自定義穿梭框支持遠程滾動加載的實現(xiàn)方法

    這篇文章主要介紹了vue自定義穿梭框支持遠程滾動加載,iview是全局注入,基本使用原先的類名進行二次創(chuàng)建公共組件,修改基礎(chǔ)js實現(xiàn)邏輯,本文結(jié)合實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • Vue路由監(jiān)聽實現(xiàn)同頁面動態(tài)加載的示例

    Vue路由監(jiān)聽實現(xiàn)同頁面動態(tài)加載的示例

    本文主要介紹了Vue基于路由監(jiān)聽實現(xiàn)同頁面動態(tài)加載的示例,重點在于切換路由的時候,重新拉取列表數(shù)據(jù),接下來看看實現(xiàn)方法吧
    2021-05-05
  • 可控制緩存銷毀的?keepAlive?組件實現(xiàn)詳解

    可控制緩存銷毀的?keepAlive?組件實現(xiàn)詳解

    這篇文章主要為大家介紹了可控制緩存銷毀的?keepAlive?組件實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • vue數(shù)組中不滿足條件跳出循環(huán)問題

    vue數(shù)組中不滿足條件跳出循環(huán)問題

    這篇文章主要介紹了vue數(shù)組中不滿足條件跳出循環(huán)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • vue項目實現(xiàn)github在線預覽功能

    vue項目實現(xiàn)github在線預覽功能

    這篇文章主要介紹了vue項目實現(xiàn)github在線預覽功能,本文通過提問兩個問題帶領(lǐng)大家一起學習整個過程,需要的朋友可以參考下
    2018-06-06
  • 使用vue-aplayer插件時出現(xiàn)的問題的解決

    使用vue-aplayer插件時出現(xiàn)的問題的解決

    這篇文章主要介紹了使用vue-aplayer插件時出現(xiàn)的問題的解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 解決vue create 創(chuàng)建項目只有兩個文件問題

    解決vue create 創(chuàng)建項目只有兩個文件問題

    這篇文章主要介紹了解決vue create 創(chuàng)建項目只有兩個文件問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • vue3實現(xiàn)移動端滑動模塊

    vue3實現(xiàn)移動端滑動模塊

    這篇文章主要為大家詳細介紹了vue3實現(xiàn)移動端滑動模塊,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09

最新評論