Skip to content

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

@buppt

Description

@buppt

写在前面

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

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

正文

上一篇我们实现了双向绑定,篇幅原因没有处理数组。我们知道 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()
    }
  }
})

本篇代码:https://github.com/buppt/YourVue/tree/master/oldSrc/3.array_observe

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions