依赖追踪

前三章主要讲解的是响应式系统,vue侦测数据的变化,当数据变化时,会通知视图进行响应的更新。

侦测一个对象的变化 Object.defineProperty(后面会使用ES6的Proxy)

Object.defineProperty(obj,key,{
    // 这里都有固定的含义 javascript中自己查找含义
    // enumerable: false,
    // writable: true,
    // configurable: true,
    // 每当从obj的key中读取数据时,get函数被触发,修改key数据时,set被触发
    get() {
        console.log(`getting key "${key}": ${oldvalue}`)
        return oldvalue
    },
    set(newvalue) {
        console.log(`setting key "${key}" to: ${newvalue}`)
        oldvalue = newvalue
    }
})

先收集依赖,把用到数据的地方收集起来,然后等属性发生变化时,把之前收集好的依赖循环触发一遍,即getter中收集依赖,setter中触发依赖

个人感觉书籍上讲解的很麻烦,还是尤雨溪作者本人讲解的简易版本比较清楚

getter和setter函数重写

<!-- practice 1try to complete getter and setter -->
  <script>
    // 这里判断是否是对象数据
    function isObject (obj) &#123;
      return typeof obj === 'Object' 
      && !Array.isArray(obj) 
      && obj !== null
      && obj !== undefined
    &#125;
    function convert (obj) &#123;
      // Implement this!

      if(!isObject(obj)) &#123;
        throw new TypeError()
      &#125;

      Object.keys(obj).forEach( key => &#123;
        let oldvalue = obj.key
        Object.defineProperty(obj,key,&#123;
          // 这里都有固定的含义 javascript中自己查找含义
          // enumerable: false,
          // writable: true,
          // configurable: true,
          get() &#123;
            console.log(`getting key "$&#123;key&#125;": $&#123;oldvalue&#125;`)
            return oldvalue
          &#125;,
          set(newvalue) &#123;
            console.log(`setting key "$&#123;key&#125;" to: $&#123;newvalue&#125;`)
            oldvalue = newvalue
          &#125;
        &#125;)
      &#125;)
    &#125;
  </script>

dependency-tracking

<!-- practice 2:try to complete class Dep and function autorun -->
  <!-- 
    they realize the function of dependency tracking 跟踪依赖 
    how to realize it:
    1class Dep has two function: -depend -notify
    2if you call function dep.notity,then autorun will execute.
    3、autorun will execute the function dep.depend,then it will console.log "updatesd"
  -->
  <script>
    window.Dep = class Dep &#123;
      constructor() &#123;
        this.subscriber = new Set()
      &#125;
      // Implement this
      depend() &#123;
        // whole point:通过这个activeUpdate确保在update执行的时候,
        // 我们能够通过depend(依赖类)访问到update
        if(activeUpdate) &#123;
          // now we get the current update function that's being executed
          // then we need to register the current active update as a subscriber
          this.subscriber.add(activeUpdate)
        &#125;
      &#125;
      notify() &#123;
        // run all subscribers
        this.subscriber.forEach(sub => sub())
      &#125;
    &#125;
    
    let activeUpdate;

    function autorun (update) &#123;
      // 这里使用一个包装函数 在执行update的时候 activeUpdate肯定是不为空的
      // 因此可以通过activeUpdate的状态看是否在执行更新函数 
      // 从而实现自动化更新
      function wrappedUpdate() &#123;
        activeUpdate = wrappedUpdate;
        update();
        activeUpdate = null
      &#125;
      wrappedUpdate()
    &#125;

    autorun( () => &#123;
      dep.depend()
    &#125;)
  </script>

mini-observer

  • observe() converts修改 the properties in the received object and make them reactive. For each converted property, it gets assigned a Dep instance which keeps track of a list of subscribing update functions, and triggers them to re-run when its setter is invoked.
  • autorun() takes an update function and re-runs it when properties that the update function subscribes to have been mutated修改. An update function is said to be “subscribing” to a property if it relies on that property during its evaluation.
<!-- practice 3: miniwacther -->
  <!-- 
    in this practice, you need to connect practice1 and practice2, when 
    a object execute setter or getter, autorun should register the update
    through dep.depend,then execute dep.notify to achive the update.
  -->
  <script>
    function isObject (obj) &#123;
        return typeof obj === 'object'
          && !Array.isArray(obj)
          && obj !== null
          && obj !== undefined
    &#125;
    
    function observe (obj) &#123;
      if (!isObject(obj)) &#123;
        throw new TypeError()
      &#125;
    
      Object.keys(obj).forEach(key => &#123;
        let internalValue = obj[key]
        // 每个对象的每个属性都新建了一个dep类来保存它的依赖
        let dep = new Dep()
        Object.defineProperty(obj, key, &#123;
          get () &#123;
            // 收集依赖
            dep.depend()
            return internalValue
          &#125;,
          set (newValue) &#123;
            const isChanged = internalValue !== newValue
            if (isChanged) &#123;
              internalValue = newValue
              // 修改依赖
              dep.notify()
            &#125;
          &#125;
        &#125;)
      &#125;)
    &#125;
    
    window.Dep = class Dep &#123;
      constructor () &#123;
        this.subscribers = new Set()
      &#125;
    
      depend () &#123;
        if (activeUpdate) &#123;
          // register the current active update as a subscriber
          this.subscribers.add(activeUpdate)
        &#125;
      &#125;
    
      notify () &#123;
        // run all subscriber functions
        this.subscribers.forEach(subscriber => subscriber())
      &#125;
    &#125;
    
    let activeUpdate
    
    function autorun (update) &#123;
      function wrappedUpdate () &#123;
        activeUpdate = wrappedUpdate
        update()
        activeUpdate = null
      &#125;
      wrappedUpdate()
    &#125;
    </script>