国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

關于Vue源碼vm.$watch()內部原理詳解

2019-11-19 11:42:01
字體:
來源:轉載
供稿:網友

關于vm.$watch()詳細用法可以見官網

大致用法如下:

<script>  const app = new Vue({    el: "#app",    data: {      a: {        b: {          c: 'c'        }      }    },    mounted () {      this.$watch(function () {        return this.a.b.c      }, this.handle, {        deep: true,        immediate: true // 默認會初始化執行一次handle      })    },    methods: {      handle (newVal, oldVal) {        console.log(this.a)        console.log(newVal, oldVal)      },      changeValue () {        this.a.b.c = 'change'      }    }  })</script>

可以看到data屬性整個a對象被Observe, 只要被Observe就會有一個__ob__標示(即Observe實例), 可以看到__ob__里面有dep,前面講過依賴(dep)都是存在Observe實例里面, subs存儲的就是對應屬性的依賴(Watcher)。 好了回到正文, vm.$watch()在源碼內部如果實現的。

內部實現原理

// 判斷是否是對象export function isPlainObject (obj: any): boolean { return _toString.call(obj) === '[object Object]'}

源碼位置: vue/src/core/instance/state.js

// $watch 方法允許我們觀察數據對象的某個屬性,當屬性變化時執行回調// 接受三個參數: expOrFn(要觀測的屬性), cb, options(可選的配置對象)// cb即可以是一個回調函數, 也可以是一個純對象(這個對象要包含handle屬性。)// options: {deep, immediate}, deep指的是深度觀測, immediate立即執行回掉// $watch()本質還是創建一個Watcher實例對象。Vue.prototype.$watch = function (  expOrFn: string | Function,  cb: any,  options?: Object ): Function {  // vm指向當前Vue實例對象  const vm: Component = this  if (isPlainObject(cb)) {   // 如果cb是一個純對象   return createWatcher(vm, expOrFn, cb, options)  }  // 獲取options  options = options || {}  // 設置user: true, 標示這個是由用戶自己創建的。  options.user = true  // 創建一個Watcher實例  const watcher = new Watcher(vm, expOrFn, cb, options)  if (options.immediate) {   // 如果immediate為真, 馬上執行一次回調。   try {    // 此時只有新值, 沒有舊值, 在上面截圖可以看到undefined。    // 至于這個新值為什么通過watcher.value, 看下面我貼的代碼    cb.call(vm, watcher.value)   } catch (error) {    handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)   }  }  // 返回一個函數,這個函數的執行會解除當前觀察者對屬性的觀察  return function unwatchFn () {   // 執行teardown()   watcher.teardown()  } }

關于watcher.js。

源碼路徑: vue/src/core/observer/watcher.js

export default class Watcher { vm: Component; expression: string; cb: Function; id: number; deep: boolean; user: boolean; lazy: boolean; sync: boolean; dirty: boolean; active: boolean; deps: Array<Dep>; newDeps: Array<Dep>; depIds: SimpleSet; newDepIds: SimpleSet; before: ?Function; getter: Function; value: any; constructor (  vm: Component, // 組件實例對象  expOrFn: string | Function, // 要觀察的表達式  cb: Function, // 當觀察的表達式值變化時候執行的回調  options?: ?Object, // 給當前觀察者對象的選項  isRenderWatcher?: boolean // 標識該觀察者實例是否是渲染函數的觀察者 ) {  // 每一個觀察者實例對象都有一個 vm 實例屬性,該屬性指明了這個觀察者是屬于哪一個組件的  this.vm = vm  if (isRenderWatcher) {   // 只有在 mountComponent 函數中創建渲染函數觀察者時這個參數為真   // 組件實例的 _watcher 屬性的值引用著該組件的渲染函數觀察者   vm._watcher = this  }  vm._watchers.push(this)  // options  // deep: 當前觀察者實例對象是否是深度觀測  // 平時在使用 Vue 的 watch 選項或者 vm.$watch 函數去觀測某個數據時,  // 可以通過設置 deep 選項的值為 true 來深度觀測該數據。  // user: 用來標識當前觀察者實例對象是 開發者定義的 還是 內部定義的  // 無論是 Vue 的 watch 選項還是 vm.$watch 函數,他們的實現都是通過實例化 Watcher 類完成的  // sync: 告訴觀察者當數據變化時是否同步求值并執行回調  // before: 可以理解為 Watcher 實例的鉤子,當數據變化之后,觸發更新之前,  // 調用在創建渲染函數的觀察者實例對象時傳遞的 before 選項。  if (options) {   this.deep = !!options.deep   this.user = !!options.user   this.lazy = !!options.lazy   this.sync = !!options.sync   this.before = options.before  } else {   this.deep = this.user = this.lazy = this.sync = false  }  // cb: 回調  this.cb = cb  this.id = ++uid // uid for batching  this.active = true  // 避免收集重復依賴,且移除無用依賴  this.dirty = this.lazy // for lazy watchers  this.deps = []  this.newDeps = []  this.depIds = new Set()  this.newDepIds = new Set()  this.expression = process.env.NODE_ENV !== 'production'   ? expOrFn.toString()   : ''  // 檢測了 expOrFn 的類型  // this.getter 函數終將會是一個函數  if (typeof expOrFn === 'function') {   this.getter = expOrFn  } else {   this.getter = parsePath(expOrFn)   if (!this.getter) {    this.getter = noop    process.env.NODE_ENV !== 'production' && warn(     `Failed watching path: "${expOrFn}" ` +     'Watcher only accepts simple dot-delimited paths. ' +     'For full control, use a function instead.',     vm    )   }  }  // 求值  this.value = this.lazy   ? undefined   : this.get() } /**  * 求值: 收集依賴  * 求值的目的有兩個  * 第一個是能夠觸發訪問器屬性的 get 攔截器函數  * 第二個是能夠獲得被觀察目標的值  */ get () {  // 推送當前Watcher實例到Dep.target  pushTarget(this)  let value  // 緩存vm  const vm = this.vm  try {   // 獲取value   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 } /**  * 記錄自己都訂閱過哪些Dep  */ addDep (dep: Dep) {  const id = dep.id  // newDepIds: 避免在一次求值的過程中收集重復的依賴  if (!this.newDepIds.has(id)) {   this.newDepIds.add(id) // 記錄當前watch訂閱這個dep   this.newDeps.push(dep) // 記錄自己訂閱了哪些dep   if (!this.depIds.has(id)) {    // 把自己訂閱到dep    dep.addSub(this)   }  } } /**  * Clean up for dependency collection.  */ cleanupDeps () {  let i = this.deps.length  while (i--) {   const dep = this.deps[i]   if (!this.newDepIds.has(dep.id)) {    dep.removeSub(this)   }  }  //newDepIds 屬性和 newDeps 屬性被清空  // 并且在被清空之前把值分別賦給了 depIds 屬性和 deps 屬性  // 這兩個屬性將會用在下一次求值時避免依賴的重復收集。  let tmp = this.depIds  this.depIds = this.newDepIds  this.newDepIds = tmp  this.newDepIds.clear()  tmp = this.deps  this.deps = this.newDeps  this.newDeps = tmp  this.newDeps.length = 0 } /**  * Subscriber interface.  * Will be called when a dependency changes.  */ update () {  /* istanbul ignore else */  if (this.lazy) {   this.dirty = true  } else if (this.sync) {   // 指定同步更新   this.run()  } else {   // 異步更新隊列   queueWatcher(this)  } } /**  * Scheduler job interface.  * Will be called by the scheduler.  */ run () {  if (this.active) {   const value = this.get()   // 對比新值 value 和舊值 this.value 是否相等   // 是對象的話即使值不變(引用不變)也需要執行回調   // 深度觀測也要執行   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    const oldValue = this.value    this.value = value    if (this.user) {     // 意味著這個觀察者是開發者定義的,所謂開發者定義的是指那些通過 watch 選項或 $watch 函數定義的觀察者     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 value of the watcher.  * This only gets called for lazy watchers.  */ evaluate () {  this.value = this.get()  this.dirty = false } /**  * Depend on all deps collected by this watcher.  */ depend () {  let i = this.deps.length  while (i--) {   this.deps[i].depend()  } } /**  * 把Watcher實例從從當前正在觀測的狀態的依賴列表中移除  */ teardown () {  if (this.active) {   // 該觀察者是否激活狀態    if (!this.vm._isBeingDestroyed) {    // _isBeingDestroyed一個標識,為真說明該組件實例已經被銷毀了,為假說明該組件還沒有被銷毀    // 將當前觀察者實例從組件實例對象的 vm._watchers 數組中移除    remove(this.vm._watchers, this)   }   // 當一個屬性與一個觀察者建立聯系之后,屬性的 Dep 實例對象會收集到該觀察者對象   let i = this.deps.length   while (i--) {    this.deps[i].removeSub(this)   }   // 非激活狀態   this.active = false  } }}
export const unicodeRegExp = /a-zA-Z/u00B7/u00C0-/u00D6/u00D8-/u00F6/u00F8-/u037D/u037F-/u1FFF/u200C-/u200D/u203F-/u2040/u2070-/u218F/u2C00-/u2FEF/u3001-/uD7FF/uF900-/uFDCF/uFDF0-/uFFFD/const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_//d]`)// path為keypath(屬性路徑) 處理'a.b.c'(即vm.a.b.c) => a[b[c]]export function parsePath (path: string): any { if (bailRE.test(path)) {  return } const segments = path.split('.') return function (obj) {  for (let i = 0; i < segments.length; i++) {   if (!obj) return   obj = obj[segments[i]]  }  return obj }}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 长子县| 兰坪| 南乐县| 孟连| 来凤县| 潼南县| 泌阳县| 大埔区| 西乌珠穆沁旗| 赞皇县| 天台县| 都匀市| 保德县| 浮山县| 黄梅县| 泗水县| 赣州市| 乌苏市| 乐安县| 西乡县| 甘南县| 沂源县| 桃江县| 新龙县| 石柱| 陵水| 板桥市| 西贡区| 伊宁县| 隆昌县| 蓬莱市| 五原县| 西安市| 榕江县| 临朐县| 鄱阳县| 浦城县| 临高县| 聂拉木县| 三亚市| 盐边县|