响应式原理
- 我们在使用 Vue 时候, 赋值属性获得属性都是直接使用的 Vue 实例
- 我们在设计属性值的时候, 页面的数据更新
1 2 3 4 5 6 7
| Object.defineProperty( 对象, '设置什么属性名', { writeable configurable enumerable: 控制属性是否可枚举, 是不是可以被 for-in 取出来 set() {} 赋值触发 get() {} 取值触发 } )
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function defineReactive( target, key, value, enumerable ) { Object.defineProperty( target, key, { configurable: true, enumerable: !!enumerable,
get () { console.log( `读取 o 的 ${key} 属性` ); return value; }, set ( newVal ) { console.log( `设置 o 的 ${key} 属性为: ${newVal}` ); value = newVal; } } ) }
|
实际开发中对象一般是有多级
1 2 3 4 5 6 7 8 9 10 11
| let o = { list: [ { } ], ads: [ { } ], user: {
} }
|
对于对象可以使用 递归来响应式化, 但是数组我们也需要处理
- push
- pop
- shift
- unshift
- reverse
- sort
- splice
要做什么事情呢?
- 在改变数组的数据的时候, 要发出通知
- Vue 2 中的缺陷, 数组发生变化, 设置 length 没法通知 ( Vue 3 中使用 Proxy 语法 ES6 的语法解决了这个问题 )
- 加入的元素应该变成响应式的
技巧: 如果一个函数已经定义了, 但是我们需要扩展其功能, 我们一般的处理办法:
- 使用一个临时的函数名存储函数
- 重新定义原来的函数
- 定义扩展的功能
- 调用临时的那个函数
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function func() { console.log( '原始的功能' ); } let _tmpFn = func; func = function () { _tmpFn(); console.log( '新的扩展的功能' ); }; func();
|
扩展数组的 push 和 pop 怎么处理呢???
- 直接修改 prototype 不行
- 修改要进行响应式化的数组的原型 ( proto )
1 2
| // 继承关系: arr -> Array.prototype -> Object.prototype -> ... // 继承关系: arr -> 改写的方法 -> Array.prototype -> Object.prototype -> ...
|
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| let ARRAY_METHOD = ['push','pop','shift','unshift','reverse','sort','splice']; let array_methods = Object.create( Array.prototype ); ARRAY_METHOD.forEach( method => { array_methods[ method ] = function () { console.log( '调用的是拦截的 ' + method + ' 方法' ); for( let i = 0; i < arguments.length; i++ ) { reactify( arguments[ i ] ); } let res = Array.prototype[ method ].apply( this, arguments ); return res; } } );
function defineReactive( target, key, value, enumerable ) { let that = this; if ( typeof value === 'object' && value != null && !Array.isArray( value ) ) { reactify( value ); }
Object.defineProperty( target, key, { configurable: true, enumerable: !!enumerable, get () { console.log( `读取 ${key} 属性` ); return value; }, set ( newVal ) { console.log( `设置 ${key} 属性为: ${newVal}` );
value = newVal;
that.mountComponent();
} } ); } function reactify( o, vm ) { let keys = Object.keys( o );
for ( let i = 0; i < keys.length; i++ ) { let key = keys[ i ]; let value = o[ key ]; if ( Array.isArray( value ) ) { value.__proto__ = array_methods; for ( let j = 0; j < value.length; j++ ) { reactify( value[ j ], vm ); } } else { defineReactive.call( vm, o, key, value, true ); } } }
|
来源: 腾讯课堂蒋坤公开课