Vue中Watcher和Scheduler的實現(xiàn)原理是什么

這篇文章主要介紹“Vue中Watcher和Scheduler的實現(xiàn)原理是什么”,在日常操作中,相信很多人在Vue中Watcher和Scheduler的實現(xiàn)原理是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Vue中Watcher和Scheduler的實現(xiàn)原理是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:國際域名空間、虛擬空間、營銷軟件、網(wǎng)站建設(shè)、漠河網(wǎng)站維護、網(wǎng)站推廣。

Vue中Watcher和Scheduler的實現(xiàn)原理是什么

Vue通過數(shù)據(jù)偵測機制感知狀態(tài)的變化,上一篇《Vue如何實現(xiàn)數(shù)據(jù)偵測》有提到Watcher對象,當(dāng)數(shù)據(jù)更新有更新,例如當(dāng)執(zhí)行this.title = '監(jiān)聽我變化了沒',在setter函數(shù)調(diào)用dep.notify通知watcher執(zhí)行更新(具體執(zhí)行watcher.update函數(shù))。

那么Vue在何時創(chuàng)建Watcher,如何通過Scheduler來調(diào)度Watcher隊列,watcher的更新最終如何體現(xiàn)到視圖的渲染,本篇內(nèi)容主要圍繞這三個問題來介紹Vue的Watcher實現(xiàn)原理。

Vue中Watcher和Scheduler的實現(xiàn)原理是什么

1.何時創(chuàng)建Watcher

組件從創(chuàng)建到銷毀會經(jīng)歷一系列生命周期,其中我們比較熟悉的有beforeMount、mounted、beforeUpdate、updated, 了解了生命周期,理解Watcher在何時被創(chuàng)建就會容易很多。Vue共三處地方會創(chuàng)建Watcher對象,mount事件、$watch函數(shù)、computed和watch屬性, mount事件創(chuàng)建Watcher用于渲染通知,watch和computed創(chuàng)建的Watcher都用于監(jiān)聽用戶自定義的屬性變化。

1.1 mount事件

文件core/instance/lifecycle.js包含了Vue生命周期相關(guān)的函數(shù),例如$forupdate、$destroy以及實例化Watcher的mountComponent函數(shù),mountComponent函數(shù)在組件掛載完成執(zhí)行$mount時觸發(fā),函數(shù)首先觸發(fā)beforeMount鉤子事件,在實例化Watcher時有傳入before函數(shù),before將觸發(fā)beforeUpdate hook。當(dāng)組件有屬性更新時,watcher在更新(watcher.run)之前會觸發(fā)beforeUpdate事件。isRenderWatcher表明創(chuàng)建的是渲染W(wǎng)atcher,直接掛在vm._watcher屬性上,當(dāng)強制執(zhí)行$forceUpdate刷新渲染,會執(zhí)行vm._watcher.update觸發(fā)渲染過程以及對應(yīng)的update hook。

/**
 * 生命周期mount事件觸發(fā)函數(shù)
 * @param {*} vm 
 * @param {*} el 
 * @param {*} hydrating 
 * @returns 
 */
 export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el

  callHook(vm, 'beforeMount')

  let updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }

  // 實例化Watcher對象,在Watcher構(gòu)造函數(shù)中建立Watcher和vm的關(guān)系
  new Watcher(vm, updateComponent, noop, {
    // 在執(zhí)行wather.run函數(shù)之前觸發(fā)before hook事件
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
    // isRenderWatcher表示用于渲染的Watcher,在執(zhí)行$forceupdate時會手動觸發(fā)watcher.update
  }, true /* isRenderWatcher */)
  
  return vm
}

export default class Watcher {
  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    this.getter = expOrFn
    this.value = this.lazy
      ? undefined
      : this.get()
  }
}

Vue.prototype.$forceUpdate = function () {
  const vm: Component = this
  if (vm._watcher) {
    vm._watcher.update()
  }
}

1.2.$watch函數(shù)

在組件中,除了使用watch、computed方法監(jiān)聽屬性變化,Vue定義了$watch函數(shù)用于監(jiān)聽屬性變化,例如當(dāng)a.b.c嵌套屬性變化,可以$watch來實現(xiàn)監(jiān)聽做后續(xù)處理,$watch相當(dāng)于在組件中直接寫watch屬性的函數(shù)式寫法,可支持在運行時動態(tài)的添加依賴監(jiān)聽,例如Vue源碼中的keep-alive組件在mounted事件中使用$watch監(jiān)聽include、exclude屬性變化。

vm.$watch( expOrFn, callback, [options] )
參數(shù):
    {string | Function} expOrFn
    {Function | Object} callback
    {Object} [options]
    {boolean} deep
    {boolean} immediate
返回值:{Function} unwatch

// 鍵路徑
vm.$watch('a.b.c', function (newVal, oldVal) {
  // 做點什么
})

// keep-alive.js文件
  mounted () {
    this.cacheVNode()
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  }

$watch函數(shù)和mountComponent函數(shù)的區(qū)別是,mountComponent用于渲染監(jiān)聽,會觸發(fā)相關(guān)的hook事件,而$watch的職責(zé)比較專一,就處理expOrFn的監(jiān)聽。另外,$watch的cb參數(shù)可以是函數(shù)、對象或字符串,當(dāng)為字符串時表示定義在Vue對象的函數(shù)名,例如在Vue組件中定義了nameChange函數(shù),那么定義vm.$watch('name', 'nameChange')后,如果name有更新會觸發(fā)Vue實體的nameChange函數(shù)。

// 監(jiān)聽屬性變化
Vue.prototype.$watch = function (
  expOrFn: string | Function,
  cb: any,
  options?: Object
): Function {
  const vm: Component = this
  // cb可能是純JS對象,那么回調(diào)為cb.handler
  if (isPlainObject(cb)) {
    return createWatcher(vm, expOrFn, cb, options)
  }
  const watcher = new Watcher(vm, expOrFn, cb, options)
  
  // 返回watch注銷監(jiān)聽函數(shù)
  return function unwatchFn () {
    watcher.teardown()
  }
}

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  // 當(dāng)執(zhí)行函數(shù)是一個對象的時候, 將 handler 的 handler調(diào)用給執(zhí)行函數(shù)
    // 這里的 options 是 watch 函數(shù)的配置信息
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

1.3.watch和computed屬性

使用Vue開發(fā)組件,這兩個屬性一定不陌生,例如使用watch定義firstName、secondName屬性的監(jiān)聽,使用computed定義fullName屬性監(jiān)聽,當(dāng)firstName和secondName更新時fullName也隨之觸發(fā)更新。

new Vue({
  el: '#app',
  data() {
    return {
        firstName: 'Li',
        secondName: 'Lei'
    }
  },
  watch: {
      secondName: function (newVal, oldVal) {
          console.log('second name changed: ' + newVal)
      }
  },
  computed: {
      fullName: function() {
          return this.firstName + this.secondName
      }
  },
  mounted() {
    this.firstName = 'Han'
    this.secondName = 'MeiMei'
  }
})

當(dāng)我們在watch和computed定義了對屬性的監(jiān)聽,Vue在何時將其轉(zhuǎn)換為Watcher對象執(zhí)行監(jiān)聽?Vue的構(gòu)造函數(shù)會調(diào)用_init(options)執(zhí)行初始化,源碼core/components/instance/init.js文件定義了_init函數(shù),執(zhí)行了一些列初始化操作,例如初始化生命周期、事件、狀態(tài)等,其中initState函數(shù)就包含了watch和computed的初始化。

// core/components/instance/init.js
// Vue構(gòu)造函數(shù)
function Vue (options) {
  this._init(options)
}

// core/components/instance/init.js
Vue.prototype._init = function (options?: Object) {
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // resolve injections before data/props
  initState(vm)
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created')
}

// // core/components/state.js
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  ...
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

1.3.1 computed屬性

initComputed初始化computed屬性,每一個Vue實體都包含_computedWatchers對象用于存儲所有computed屬性的watcher對象。首先遍歷computed對象,為每個key創(chuàng)建一個新的Watcher對象,其lazy屬性為true,表示W(wǎng)atcher會緩存計算值,如果依賴其依賴的屬性(如firstName、secondName)沒有更新,當(dāng)前computed屬性(例如fullName)也不會觸發(fā)更新。computed中定義的屬性可以通過this(例如this.fullName)訪問,defineComputed將所有computed屬性掛載到Vue實體上。

// lazy為true表示需要緩存,一般只有computed屬性才會用到
const computedWatcherOptions = { lazy: true }

function initComputed (vm: Component, computed: Object) {
    const watchers = vm._computedWatchers = Object.create(null)

    for (const key in computed) {
      const userDef = computed[key]
      // 用戶定義的執(zhí)行函數(shù)可能是{ get: function() {} }形式
      const getter = typeof userDef === 'function' ? userDef : userDef.get
      // 為用戶定義的每個computed屬性創(chuàng)建watcher對象
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      )

      // 組件自身的computed屬性已經(jīng)定義在組件原型鏈上,我們只需要定義實例化的computed屬性。
      // 例如我們在computed定義了fullName,defineComputed會將其掛接到Vue對象的屬性上
      if (!(key in vm)) {
        defineComputed(vm, key, userDef)
      }
}

defineComputed函數(shù)將計算屬性轉(zhuǎn)換為{ get, set }形式,但計算屬性不需要set,所以代碼直接為其賦值了noop空函數(shù)。計算屬性的get函數(shù)通過createComputedGetter封裝,首先找到對應(yīng)屬性的watcher對象,如果watcher的dirty為true,表示依賴屬性有更新,需要調(diào)用evaluate函數(shù)重新計算新值。

// 將computed定義的屬性轉(zhuǎn)換為{ get, set }形式并掛接到Vue實體上,這樣就可以通過this.fullName形式調(diào)用
export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = createComputedGetter(key)
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? createComputedGetter
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }

  Object.defineProperty(target, key, sharedPropertyDefinition)
}

// 定義computed的專屬getter函數(shù)
function createComputedGetter (key) {
  return function computedGetter () {
    // _computedWatchers上為每個computed屬性定義了Watcher對象
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      // dirty為true,表示依賴的屬性有變化
      if (watcher.dirty) {
      // 重新計算值
        watcher.evaluate()
      }
      if (Dep.target) {
        // 將Dep.target(watcher)附加到當(dāng)前watcher的依賴中
        watcher.depend()
      }
      return watcher.value
    }
  }
}

如果Dep.target有值,將其他依賴當(dāng)前計算屬性的Watcher(例如使用到fullName的依賴Watcher)附加到當(dāng)前計算屬性所依賴的屬性的dep集合中。如下面的代碼創(chuàng)建了對fullName計算屬性的監(jiān)聽, 我們將其命名為watcher3。那么firstName和secondName的dep對象都會附加上watcher3觀察者,只要其屬性有任何變化,都會觸發(fā)watcher3的update函數(shù),重新讀取fullName屬性值。

vm.$watch('fullName', function (newVal, oldVal) {
  // 做點什么
})

1.3.2 watch屬性

initWatch函數(shù)邏輯相對簡單些,遍歷每個屬性的依賴項,如果依賴項為數(shù)組,則遍歷數(shù)組,為每個依賴項單獨創(chuàng)建Watcher觀察者,createWatcher函數(shù)在前文中有提到,它使用$watch創(chuàng)建新的watcher實體。

// 初始化Watch屬性
function initWatch (vm: Component, watch: Object) {
  for (const key in watch) {
    const handler = watch[key]
    // 如果對應(yīng)屬性key有多個依賴項,則遍歷為每個依賴項創(chuàng)建watcher
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

2.Scheduler調(diào)度處理

Vue在core/observer/scheduler.js文件定義了調(diào)度函數(shù),一共有兩處使用,Watcher對象以及core/vdom/create-component.js文件。watcher對象在執(zhí)行更新時,會被附加到調(diào)度隊列中等待執(zhí)行。create-component.js主要處理渲染過程,使用scheduler的主要作用是觸發(fā)activated hook事件。這里重點闡述Watcher對Scheduler的使用。
當(dāng)執(zhí)行watcher的update函數(shù),除了lazy(計算屬性watcher)、sync(同步watcher),所有watcher都將調(diào)用queueWatcher函數(shù)附加到調(diào)度隊列中。

export default class Watcher {
  /**
 * 通知訂閱,如果依賴項有更新,該函數(shù)會被觸發(fā)
 */
  update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
  }
}

queueWatcher函數(shù)定義如下,函數(shù)的目的是將watcher附加到調(diào)度隊列中,對調(diào)度隊列創(chuàng)建微任務(wù)(microTask),等待執(zhí)行。關(guān)于microTask和macroTask的區(qū)別,看查看參考8“宏任務(wù)macroTask和微任務(wù)microTask的區(qū)別”。如果微任務(wù)flushSchedulerQueue還未執(zhí)行(flushing為false),直接將watcher附加到queue即可。否則,還需判斷當(dāng)前微任務(wù)的執(zhí)行進度,queue會按watcher的id做升序排序,保證先創(chuàng)建的watcher先執(zhí)行。index為微任務(wù)中正在被執(zhí)行的watcher索引,watcher將會插入到大于index且符合id升序排列的位置。最后隊列執(zhí)行函數(shù)flushSchedulerQueue將通過nextTick創(chuàng)建一個微任務(wù)等待執(zhí)行。

/*
* 附加watcher到隊列中,如果有重復(fù)的watcher直接跳過。
* 如果調(diào)度隊列正在執(zhí)行(flushing為true),將watcher放到合適的位置
*/
export function queueWatcher (watcher: Watcher) {
  // 所有watcher都有一個遞增的唯一標(biāo)識,
  const id = watcher.id
  // 如果watcher已經(jīng)在隊列中,不做處理
  if (has[id] == null) {
    has[id] = true
    if (!flushing) {
      // 如果隊列還未執(zhí)行,則直接附加到隊列尾部
      queue.push(watcher)
    } else {
      // 如果正在執(zhí)行,基于id將其附加到合適的位置。
      // index為當(dāng)前正在執(zhí)行的watcher索引,并且index之前的watcher都被執(zhí)行了。
      // 先創(chuàng)建的watcher應(yīng)該被先執(zhí)行,和隊列中的watcher比較id大小,插入到合適的位置。
      let i = queue.length - 1
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      // i的位置,表明 watcher[i - 1].id < watcher[i].id < watcher[i + 1].id
      queue.splice(i + 1, 0, watcher)
    }
    // 如果未排隊,開始排隊,nextick將執(zhí)行調(diào)度隊列。
    if (!waiting) {
      waiting = true
      nextTick(flushSchedulerQueue)
    }
  }
 }

nextTick將會選擇適合當(dāng)前瀏覽器的微任務(wù)執(zhí)行隊列,例如MutationObserver、Promise、setImmediate。flushSchedulerQueue函數(shù)將遍歷所有watcher并執(zhí)行更新,首先需要將queue做升序排序,確保先創(chuàng)建的watcher先被執(zhí)行,例如父組件的watcher優(yōu)先于子組件執(zhí)行。接著遍歷queue隊列,先觸發(fā)watcher的before函數(shù),例如前文中介紹mountComponent函數(shù)在創(chuàng)建watcher時會傳入before事件,觸發(fā)callHook(vm, 'beforeUpdate')。接下來就具體執(zhí)行更新(watcher.run)操作。當(dāng)隊列執(zhí)行完后,調(diào)用resetSchedulerState函數(shù)清空隊列、重置執(zhí)行狀態(tài)。最后callActivatedHooks和callUpdatedHooks將觸發(fā)對應(yīng)的activated、updated hook事件。

/**
 * 遍歷執(zhí)行所有的watchers
 */
 function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true
  let watcher, id

  // 遍歷之前先排序隊列
  // 排序的隊列能確保:
  //    1.父組件先于子組件更新,因為父組件肯定先于子組件創(chuàng)建。
  //    2.組件自定義的watcher將先于渲染watcher執(zhí)行,因為自定義watcher先于渲染watcher創(chuàng)建。
  //    3.如果組件在父組件執(zhí)行wtcher期間destroyed了,它的watcher集合可以直接被跳過。
  queue.sort((a, b) => a.id - b.id)

  // 不要緩存length,因為在遍歷queue執(zhí)行wacher的同時,queue隊列一直在調(diào)整。
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      // 通過before可觸發(fā)hook,例如執(zhí)行beforeUpdated hook
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    // 執(zhí)行watcher的更新
    watcher.run()
  }

  // 由于activatedChildren和queue兩個隊列一直在更新,因為需要拷貝處理
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()
  // 重置掉隊隊列狀態(tài)
  resetSchedulerState()

  // 觸發(fā)activated和updated hooks
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)
}

3.Watcher更新

調(diào)度隊列會執(zhí)行watcher的run函數(shù)觸發(fā)更新,每個watcher有active狀態(tài),表明當(dāng)前watcher是否處于激活狀態(tài),當(dāng)組件執(zhí)行$destroy函數(shù),會調(diào)用watcher的teardown函數(shù)將active設(shè)置為false。在執(zhí)行更新通知回調(diào)cb之前,有三個條件判斷,首先判斷值是否相等,對于簡單值string或number類型的可直接判斷;如果value為對象或需要深度遍歷(deep為true),例如用戶自定義了person屬性,其值為對象{ age: number, sex: number },我們使用$watch('person', cb)監(jiān)聽了person屬性,但當(dāng)person.age發(fā)生變化時,cb不會被執(zhí)行。如果改成$watch('person', cb, { deep: true }),任何嵌套的屬性發(fā)生變化,cb都會被觸發(fā)。滿足三個條件其中之一,cb回調(diào)函數(shù)將被觸發(fā)。

export default class Watcher {
  /**
 * 調(diào)度接口,將被調(diào)度器執(zhí)行
 */
   run () {
    // 僅當(dāng)watcher處于激活狀態(tài),才會執(zhí)行更新通知
    // 當(dāng)組件destroyed時,會調(diào)用watcher的teardown將其重置到非激活狀態(tài)
    if (this.active) {
      // 調(diào)用get獲取值
      const value = this.get()
      if (
        // 如果新計算的值更新了
        value !== this.value ||
        // 如果value為對象或數(shù)組,不管value和this.value相等否,則其深度watchers也應(yīng)該被觸發(fā)
        // 因為其嵌套屬性可能發(fā)生變化了
        isObject(value) ||
        this.deep
      ) {
        const oldValue = this.value
        this.cb.call(this.vm, value, oldValue)
      }
    }
  }
}

this.$watch('person', () => {
  this.message = '年齡為:' + this.person.age
  }, 
  // 當(dāng)deep為true,當(dāng)age更新,回調(diào)會被觸發(fā);如果deep為false,age更新不會觸發(fā)回調(diào)
  { deep: true }
)

run函數(shù)有調(diào)用get獲取最新值,在get函數(shù)中,首先調(diào)用pushTarget函數(shù)將當(dāng)前Watcher附加到全局Dep.target上,然后執(zhí)行g(shù)etter獲取最新值。在finally模塊中,如果deep為true,則調(diào)用traverse遞歸遍歷最新的value,value可能為Object或者Array,所以需要遍歷子屬性并觸發(fā)其getter函數(shù),將其dep屬性附加上Dep.target(當(dāng)前Watcher),這樣任何子屬性的值發(fā)生變化都會通知到當(dāng)前watcher,至于為什么,可以回顧下上篇《Vue如何實現(xiàn)數(shù)據(jù)狀態(tài)的偵測》。

export default class Watcher {
  /**
* 執(zhí)行g(shù)etter,重新收集依賴項
*/
  get () {
    // 將當(dāng)前Watcher附加到全局Dep.target上,并存儲targetStack堆棧中
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      // 執(zhí)行g(shù)etter讀取value
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // 如果deep為true,將遍歷+遞歸value對象
      // 將所有嵌套屬性的dep都附加上當(dāng)前watcher,所有子屬性對應(yīng)的dep都會從push(Dep.target)
      if (this.deep) {
        // 遞歸遍歷所有嵌套屬性,并觸發(fā)其getter,將其對應(yīng)的dep附加當(dāng)前watcher
        traverse(value)
      }
      // 退出堆棧
      popTarget()
      // 清理依賴
      this.cleanupDeps()
    }
    return value
  }
}

在get函數(shù)中為什么要執(zhí)行traverse遞歸遍歷子屬性,我們可以通過實際的例子來說明,例如在data中定義了{ person: { age: 18, sex: 0, addr: { city: '北京', detail: '五道口' } }, Vue會調(diào)用observe將person轉(zhuǎn)換為如下Observer對象,子屬性(如果為對象)也會轉(zhuǎn)換為Observer對象,簡單屬性都會定義get、set函數(shù)。

Vue中Watcher和Scheduler的實現(xiàn)原理是什么

當(dāng)watcher.get執(zhí)行traverse函數(shù)時,會遞歸遍歷子屬性,當(dāng)遍歷到addr屬性時,觸發(fā)get函數(shù),該函數(shù)將調(diào)用其dep.depend將當(dāng)前Watcher附加到依賴項中,這樣我們在執(zhí)行執(zhí)行this.person.age = 18,其set函數(shù)調(diào)用dep.notify觸發(fā)watcher的update函數(shù),實現(xiàn)person對象的監(jiān)聽。

get: function reactiveGetter () {
  const value = getter ? getter.call(obj) : val
  if (Dep.target) {
    dep.depend()
    ...
  }
  return value
}

set: function reactiveSetter (newVal) {
  ...
  dep.notify()
}

到此,關(guān)于“Vue中Watcher和Scheduler的實現(xiàn)原理是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

分享題目:Vue中Watcher和Scheduler的實現(xiàn)原理是什么
網(wǎng)站鏈接:http://www.muchs.cn/article38/ippjsp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、電子商務(wù)靜態(tài)網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站收錄、微信公眾號

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司