vue2源码学习(二)---监听数组的变化
在监听对象变化的基础上只修改了observe.js,新增了array.js
通过重写数组内的会改变数组本身的方法进行监听数组的变化
//observe.js
import { newArrayProto } from './array' class Observer { constructor (data) { console.log(data,this) // Object.defineProperty只能劫持已经存在的属性(所以vue里定义了$set,$delete) Object.defineProperty(data,'__ob__',{ value:this, enumerable:false//将__ob__变为不可枚举(循环的时候无法获取,避免死循环) }) // data.__ob__ = this //判断是否是数组 if (Array.isArray(data)) { // 重写数组部分方法(保留数组原有的特性) data._proto_ = newArrayProto this.observeArray(data) } else { this.walk(data) } } walk (data) { //重新定义属性 Object.keys(data).forEach(key => defineReactive(data, key, data[key])) } observeArray (data) { data.forEach(item => observe(item)) } } export function defineReactive (target, key, value) {//闭包 observe(value) //对所有的对象进行属性劫持 Object.defineProperty(target, key, { get () {//取值的时候会执行get return value }, set (newV) {//修改的时候会执行set if (newV === value) return observe(newV)//防止新值为对象,所以对新值进行再次代理 value = newV }, }) } export function observe (data) { // 对data进行劫持 if (typeof data !== 'object' || data == null) { return;//只对对象进行劫持 } if(data.__ob__ instanceof Observer){ return } // 如果一个对象被劫持过了,就不需要再劫持了(判断一个对象是否被接吃过,可以增添一个实例,用实例来判断) return new Observer(data) }
//array.js重写数组中的部分方法 let oldArrayProto = Array.prototype export let newArrayProto = Object.create(oldArrayProto) //所有能修改原数组的方法 let methods = [ 'push',//向数组的末尾添加一个或更多元素,并返回新的长度。 'pop',//删除数组的最后一个元素并返回删除的元素。 'shift',// 删除并返回数组的第一个元素。 'unshift',//向数组的开头添加一个或更多元素,并返回新的长度。 'reverse',//反转数组的元素顺序。 'sort',//对数组进行排序(可以穿进去一个函数) 'splice',//splice(n,m)删除数组的第n个到第m-1个(不包含m个)从数组中添加或删除元素。 ] methods.forEach(method => { newArrayProto[method] = function (...args) {//重写数组方法 const result = oldArrayProto[method].call(this, ...args)//内部调用原来的方法,函数的劫持,切片编程 //需要对新增的数据再次进行劫持 let ob = this.__ob__ let inserted; switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break default: break } console.log(inserted) //新增的内容 if (inserted) { // 对新增的内容再次进行劫持 ob.observeArray(inserted) } return result } })