vue 实现个简易版(4)

上一篇文章实现了v-bind, v-on指令以及methods等,这一篇来实现computed和watch;

1. computed

在Vue实例中通过this可以直接访问computed中的计算属性,而且它可以实现缓存,并且只有当依赖的数据变化才会更新;

1.1 ComputedWatcher

需要定义一个ComputedWatcher,它跟Watcher基本相同,只是再设置值的时候,需要跟新缓存的老值

class ComputedWatcher {
    constructor(vm, expr, cb) {
        this.vm = vm
        this.expr = expr
        this.cb = cb
        this.value = this.get()
    }
    get() {
        Dep.target = this
        let oldValue = CompilerUtil.getVal(this.vm, this.expr)
        Dep.target = null
        return oldValue
    }
    update() {
        let newVal = CompilerUtil.getVal(this.vm, this.expr)
        if (newVal !== this.value) {
            this.value = newVal // 跟Watcher不同
            this.cb(newVal)
        }
    }
}

1.2 在取data中值是判断 是否是取computed 中的值

修改CompilerUtil中的取值函数getVal,加入判断

getVal(vm, expr) {
  return expr.split('.').reduce((data, key) => {
      if (key in vm.$computed) { // 1.2 添加
          return vm.$computed[key].call(vm)
      }
      return data[key]
  }, vm.$data)
}

1.3 在设置值前加入依赖,缓存旧值,并在数据更新的时候,更新computed 中的值以及视图

修改CompilerUtil中的取值函数setTextContent,加入判断

setTextContent(node, expr, vm) {
    let content
    let fn = this.updater['textUpdater']
    let k = (/\{\{(.+?)\}\}/g).exec(expr)[1] //1.3 获取{{ }}

    if (k in vm.$computed) {//1.3 
    	// new ComputedWatcher的时候缓存旧值,加入依赖
        let computed = new ComputedWatcher(vm, k, (newVal) => { 
            fn(node, newVal)
        })
        content = computed.value
    } else {
        content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
            new Watcher(vm, args[1], (newVal) => {
                fn(node, newVal)
            })
            return this.getVal(vm, args[1])//school.age
        })
    }

    fn(node, content)
}

至此computed功能基本完成

2. watch

2.1 修改Watcher类的update函数,接收上一次的值oldValue,更新vm.$watch中的函数传递newVal和oldValue

update(oldValue) {
    let newVal = CompilerUtil.getVal(this.vm, this.expr)
    let timer = null
    if (newVal !== this.value) {
        this.cb(newVal,oldValue)
        if (this.expr in this.vm.$watch) {
            vm.$watch[this.expr].call(this.vm, newVal, oldValue)
        }
    }
}

2.2 修改Dep类中的notify函数接收上一次的值oldValue,

notify(oldValue) {
    this.subscribers.forEach(watcher => watcher.update(oldValue));
}

2.3 修改Observer类中的defindReactive函数中的Object.defineProperty的set函数

set: (newValue) => {
    // 当赋的值和老值一样,就不重新赋值
    if (newValue != value) {
        let oldValue = value //2.3 添加
        this.observer(newValue)//新值,做成响应式
        value = newValue
        dep.notify(oldValue) //2.3修改
    }
}

到此为止watch的原理,也基本实现了。

posted @ 2020-09-17 11:10  tang丶有年  阅读(94)  评论(0编辑  收藏  举报