数组双向绑定的简单分享

阅读 467
收藏 11
2018-07-05
原文链接:damobing.com

前言

本文主要是摘录《vuejs权威指南》部分的相关解读,我们都知道vue对数据支持双向绑定,对数组也是支持的,但是其在官网有明确说明,对于数组通过 对象.语法来修改其值得是无法监听到的,可以通过this.$set方法来实现。另外其也推荐了一系列的数组可监听到的方法能够支持双向绑定。

那么本文就从源码角度去帮大家理解分析为什么是这样的。

定位源码位置

分析源码内容

我们将源码复制粘贴下来进行逐行分析:好在这个文件并不是特别复杂。

/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original 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)
    // notify change
    ob.dep.notify()
    return result
  })
})

分析结果

首先我们可以看到官网提到的支持数组双向绑定方法的全部都是因为在vuejs源码中进行了方法的重定义,将Array原型链中的每个方法进行了监听,当其方法执行时,会对其对象进行通知,并返回其正确的结果。并且针对一些特殊的unshift,splice改变了insert字段,增加监听。

关于$set ,$remove

其原理只是vue写的语法糖,这里只拿set方法的源码进行分析查看:
这里可以看到当对象是数组的时候,其set方法是利用数组的splice方法进行数据更改,同时也增加了对新增内容的监听。

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
export function set (target: Array<any> | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

那么我们也可以简化的理解为源码是这样的:

def{arrayProto,'$set',function $set (index,val){
    if(index>=this.length){this.length=Number(index)+1}
    return this.splice(index,1,val)[0]
    }
}
def{arrayProto,'$remove',function $remove (item){
   if(!this.length) return 
   var index =  indexOf(this,item)
   if(index>-1){
        return this.splice(index,1)
   }
}

总结

关于数组中对数据的双向绑定监听就分析到这里了,希望你能进一步vue是如何对数组进行相应的监听和绑定,以及绑定了哪些方法,对于不支持的方法如何进行变通。

拓展:

也希望大家通过本文进一步了解原型链,监听,this的相关概念和数组的基础知识,比如数组中删除某个元素使用splice.

评论