在vue中v-model是通过Object.defineProperty来实现的,如下:
const data = {};
let name = '张三';
// 对对象定义属性
// 需要3个参数
// 参数1:对象本身
// 参数2:定义的属性
// 参数3:定义属性的选项
Object.defineProperty(data, 'name', {
get(){
console.log('get执行了');
return name;
},
set(newVal){
console.log('set执行了:', newVal);
name = newVal;
}
})
console.log(data.name);
data.name = '李四';
console.log(data.name);
上面是最简单的一种体现方法,通过对属性进行新增(Vue.set)和(Vue.delete)不能做到完全响应式。
复杂的对象,需要自己实现深度监听,进行递归监听。需要一次性监听到底的。
数量多的情况下,会导致页面卡死。 解决:vue3.0 采用 proxy polyfill
promise babel polyfill
function Promise(){
}
Promise()
对数组没有办法进行监听。需要进行额外的处理,所以数组主要是通过给数据重新定义原型,再创建新的原型,再通过循环遍历给它添加数组的一些方法
定义一个对象,封装一个函数监听对象属性
const data = {
name: 'zhangsan',
age: 20,
info: {
height: 180
},
list: ['a', 'b', 'c']
}
// 监听对象属性的方法
function observer(target){
// 判断监听数据的类型
if(typeof target !== 'object' || target === null){
// 不是对象或者数组就被过滤了
return target;
}
if(Array.isArray(target)){
target.__proto__ = arrProto;
return;
}
for( let key in target ){
defineReactive(target, key, target[key]);
}
}
// 监听数据的变化
observer(data);
/* data.age = {
value: 20
}
定义原型重新创建原型调用Object.defineProperty方法并监听
function updateView(){
console.log('视图更新了....');
}
// 重新定义数组的原型
const oldArrayProperty = Array.prototype;
// 创建新的对象,原型指向的是oldArrayProperty
// 在这个对象上扩展方法不会影响Array.prototype
const arrProto = Object.create(oldArrayProperty);
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
// arrProto.push
// arrProto.pop
// arrProto.shift
// arrProto.unshift
// arrProto.splice
// arrProto.sort
// arrProto.reverse
methods.forEach(methodName=>{
arrProto[methodName] = function(){
oldArrayProperty[methodName].call(this, ...arguments);
// 更新dom
updateView();
}
})
// 对每一个属性进行观测
function defineReactive(target, key, value){
// 深度监听
observer(value);
Object.defineProperty(target, key, {
get(){
console.log('get执行了');
return value;
},
set(newValue){
console.log(key, 'set执行了');
// 对新设置的属性值还需要进行深度监听
observer(newValue);
value = newValue;
// 更新视图
updateView();
}
})
}
给data改变属性验证是否发生改变
// 设置
data.info.height = 190;
data.age.value = 30; //
data.info.address = '深圳';//Vue.set
delete data.name;//Vue.delete
// data.list.push('hello');
// data.list[1] = 'xx';