iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 19
0
Modern Web

技術在走,Vue.js 要有系列 第 19

|D19| 從原始碼看 Vue 響應式原理 (5) - Watcher

  • 分享至 

  • xImage
  •  

在 Vue 實例化的 initState 中, mount 階段透過 mountComponent 執行,這裡把 Watcher 實例化

// src/core/instance/lifecycle.js

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
    // ...
    } else {
        updateComponent = () => {
          vm._update(vm._render(), hydrating)
        }
      }

      new Watcher(vm, updateComponent, noop, {
        before () {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate')
          }
        }
      }, true)
  
    //...
}

watcher 會先執行 this.get()

// src/core/observer/watcher.js

let uid = 0

export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deep: boolean;
  user: boolean;
  computed: boolean;
  sync: boolean;
  dirty: boolean;
  active: boolean;
  dep: Dep;
  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
  ) {
    //...
    /********************************************************
         把傳入的 updateComponent function 賦值給 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.get()
    *********************************************************/
     this.value = this.lazy
      ? undefined
      : this.get()
  }


  get () {
    /*************************************************
      this 就是 Watcher,把自己賦值給 Dep.target 
    **************************************************/
    pushTarget(this)
    let value
    const 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 {
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

  addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      /*************************************************
          把接收的 dep 物件 push 到 Watcher.newDeps 中
      **************************************************/
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        /*************************************************
           調用 dep 的 addSub 傳入一個 Watcher 物件
        **************************************************/
        dep.addSub(this)
      }
    }
  }

  // ...
}

上一篇
|D18| 從原始碼看 Vue 響應式原理 (4) - Dep
下一篇
|D20| 從原始碼看 Vue 響應式原理 (6) - nextTick 異步更新
系列文
技術在走,Vue.js 要有30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言