1. 程式人生 > 實用技巧 >vue 實現個簡易版(4)

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的原理,也基本實現了。