从零写一个 Vue(三)数组监听

970 阅读2分钟

写在前面

本篇是从零实现 vue2 系列第三篇,为 YourVue 添加数组监听。

文章会最先更新在公众号:BUPPT。代码仓库:github.com/buppt/YourV…

正文

上一篇我们实现了双向绑定,篇幅原因没有处理数组。我们知道 vue 是通过重写了几个数组的方法实现的数组监听,先在 Observer 中添加几行代码。

class Observer{
    constructor(value) {
        this.value = value
        this.dep = new Dep()
        def(value, '__ob__', this)
        if(Array.isArray(value)){
            value.__proto__ = arrayMethods
            this.observeArray(value)
        }else{
            this.walk(value);
        }
    }
    observeArray(value){
        value.forEach(item => {
            observe(item)
        })
    }
}

首先把 observer 实例赋值给__ob__参数,以便后用。

修改数组的__proto__指向一个新的原型,下面看一下新的原型 arrayMethods。

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
methodsToPatch.forEach(function (method) {
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    ob.dep.notify()
    return result
  })
})

可以发现 Vue 对 push、pop、shift、unshift、splice、sort、reverse 七个方法进行了拓展,执行这七个方法的时候会触发 dep.notify(),就是上一篇中的执行所有订阅 watcher 的更新函数。如果是 push、unshift 和 splice 还会对增加到数组中的数据进行 observe。

代码没有直接修改 Array.prototype,而是将 arrayMenthods 赋值给被观测数组的 __proto__,这样不会污染全局 Array。

这样我们就可以在 main.js 中定义一个数组,调用 push、pop 等方法,也会触发更新。

new YourVue({
  el: '#app',
  data: {
      count: 0,
      message: 'message',
      items: [1,2,3,0,5]
  },
  template: `
      <div>
          array: {{items}}
          <div>{{count}}</div>
          <button @click="addCount">addCount</button>
          <h4>{{message}}</h4>
          <button @click="decCount">decCount</button>
      </div>
  `,
  methods:{
    addCount(){
        this.count += 1
        this.items.push(this.count)
    },
    decCount(){
        this.count -= 1
        this.items.pop()
    }
  }
})

本篇代码:github.com/buppt/YourV…

至此双向绑定就已经完成啦,下篇实现虚拟 dom。求关注~ 求 star~