深入Vue-Reactivity


Reactivity

1.1 Getters and Setters

目标

实现一个 convert 函数:

  • 接收一个 Object 作为参数

  • 使用 object.defineProperty 将对象的属性就地转换为 getter/setter
    mdn

  • 转换后的对象应保留原始行为,但同时记录所有的 get/set 操作。

测试用例:

const obj = { foo: 123 };
convert(obj);

obj.foo; // should log: 'getting key "foo": 123'
obj.foo = 234; // should log: 'setting key "foo" to: 234'
obj.foo; // should log: 'getting key "foo": 234'

测试实现是否正确, 运行:

npm test -- -t 1.1

实现


function isObject(obj) {
    return (
      typeof obj === "object" &&
      !Array.isArray(obj) &&
      obj !== null &&
      obj !== undefined
    );
  }

function convert(obj) {
  if (!isObject(obj)) {
    throw new TypeError();
  }

  Object.keys(obj).forEach(key => {
    let internalValue = obj[key];
    Object.defineProperty(obj, key, {
      get() {
        console.log(`getting key "${key}": ${internalValue}`);
        return internalValue;
      },
      set(newValue) {
        console.log(`setting key "${key}" to: ${newValue}`);
        internalValue = newValue;
      }
    });
  });
}

1.2 Dependency Tracking

目标

  • Create a Dep class with two methods: depend and notify.
  • Create an autorun function that takes an updater function.
  • Inside the updater function, you can explicitly depend on an instance of Dep by calling dep.depend()
  • Later, you can trigger the updater function to run again by calling dep.notify().

The full usage should look like this:

const dep = new Dep()

autorun(() => {
  dep.depend()
  console.log('updated')
})
// should log: "updated"

dep.notify()
// should log: "updated"

To test if your implementation is correct, run:

npm test -- -t 1.2

实现

window.Dep = class Dep {
    constructor(){
        this.subscribers = new Set()
    }
    depend(){
        if(activeUpdate){
            this.subscribers.add(activeUpdate)
        }
    }
    notify(){
        this.subscribers.forEach(sub=>sub())
    }
}

let activeUpdate

function autorun(update){
    function wrappedUpdate(){
        activeUpdate = wrappedUpdate
        update()
        activeUpdate = null
    }
    wrappedUpdate()
}

1.3 Mini Observer

目标

Combine the previous two functions, renaming convert() to observe() and keeping autorun():

  • 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.

They should support the following usage:

const state = {
  count: 0
}

observe(state)

autorun(() => {
  console.log(state.count)
})
// should immediately log "count is: 0"

state.count++
// should log "count is: 1"

To test if your implementation is correct, run:

npm test -- -t 1.3

实现

function isObject(obj){
  return (
    typeof obj === 'object' &&
    !Array.isArray(obj) &&
    obj !== null &&
    obj !== undefined
  )
}
function observe(obj){
  if(!isObject(obj)){
    throw new TypeError()
  }
  Object.keys(obj).forEach(key=>{
    let internalValue = obj[key]
    let dep = new Dep()
    Object.defineProperty(obj,key,{
      get(){
        dep.depend()
        return internalValue
      },
      set(newValue){
        internalValue = newValue
        dep.notify()
      }
    })
  })
}
class Dep{
  constructor(){
    this.subscribers = new Set()
  }
  depend(){
    if(activeUpdate){
      this.subscribers.add(activeUpdate)
    }
  }
  notify(){
    this.subscribers.forEach(sub=>sub())
  }
}

let activeUpdate
function autorun(update){
  function wrappedUpdate(){
    activeUpdate = wrappedUpdate
    update()
    activeUpdate = null
  }
  wrappedUpdate()
}

以上就实现了一个简单的观察者


文章作者: 沐雪
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 沐雪 !
评论
 上一篇
深入Vue-Plugin 深入Vue-Plugin
Plugin2.1 Simple PluginGoalCreate a plugin that teaches Vue components to handle a custom “rules”option. The “rules” opt
2019-04-10 沐雪
下一篇 
深入Vue-开篇 深入Vue-开篇
主要内容 Reactivity Writing Plugins Render Functions State Management Routing Form Validation i18n document.queryS
2019-04-09 沐雪
  目录