前言
Vue2.0响应式核心是Object.defineProperty,但是它有局限,不能检测到对象属性的新增和删除,也不能检测到对象中属性值为对象的属性的修改,而对于数组类型不能检测到通过push、pop、shift、unshift等会修改数组本身的方法对数组做的更改;而Proxy作为操作对象的代理对象可以弥补Object.defineProperty的缺陷,那么Object.defineProperty和Proxy分别都是如何来创建响应式的呢?
Object.defineProperty实现响应式
第一篇解析:Object.defineProperty实现响应式
<body>
<input id="input">
<div>
2s后自动更新值:<span id="text"></span>
</div>
</body>
<script>
function changeValue(e) {
data.inputValue = e.target.value
}
function defineReactiveValue(obj, key, value) {
Object.defineProperty(obj, key, {
get: () => {
return value
},
set: (val) => {
console.log('响应类型:', obj)
document.querySelector('#input').value = val
document.querySelector('#text').innerHTML = val
}
})
}
let data = {
inputValue: '',
arr: [{
name: 'bb'
}],
obj: {
name: 'yy'
}
};
Object.keys(data).forEach((key, index) => {
defineReactiveValue(data, key, data[key])
})
setTimeout(() => {
console.log('更新基本属性值...')
data.inputValue = '我是被自动更新的值~'
console.log('push更新数组属性值...')
data.arr.push({
name: 'haha'
})
console.log('下标更新数组属性值...')
data.arr[0] = {
name: 'haha'
}
console.log('更新对象属性中的属性值...')
data.obj.name = 'changeObjName'
}, 2000);
//数组类型
let dataArr = [{
name: 'dataArr_bb'
}]
dataArr.forEach((item, index) => {
defineReactiveValue(dataArr, index, item)
})
setTimeout(() => {
console.log('更新数组类型的属性值...')
console.log('更新数组push方法更新...')
dataArr.push({
name: 'haha'
})
console.log('更新数组下标法更新...')
dataArr[0] = {
name: 'haha'
}
}, 4000);
document.querySelector('#input').addEventListener('keyup', changeValue)
</script>
结果:
Proxy实现响应式
第一篇Proxy的解析: Proxy
<body>
<input id="input">
<div>
2s后自动更新值:<span id="text"></span>
</div>
<ul id="list"></ul>
<button id="btn">add</button>
<button id="modify">modify</button>
</body>
<script>
const list = document.getElementById('list');
const btn = document.getElementById('btn');
const modify = document.getElementById('modify');
// 渲染列表
const Render = {
// 初始化
init: function (arr) {
list.innerHTML = '';
const fragment = document.createDocumentFragment();
for (let i = 0; i < arr.length; i++) {
const li = document.createElement('li');
li.textContent = arr[i];
fragment.appendChild(li);
}
list.appendChild(fragment);
},
};
function changeValue(e) {
data.inputValue = e.target.value
}
function defineReactiveValue(target) {
return new Proxy(target, {
get: function (target, propKey, receiver) {
console.log('触发get:', target[propKey])
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
if (propKey === 'length') {
Render.init(target)
}
console.log('触发set,响应类型:', target)
document.querySelector('#input').value = value
document.querySelector('#text').innerHTML = value
return Reflect.set(target, propKey, value, receiver);
}
})
}
let _data = {
inputValue: '',
arr: [{
name: 'bb'
}],
obj: {
name: 'yy'
}
};
let data = defineReactiveValue(_data);
setTimeout(() => {
console.log('更新基本属性值...')
data.inputValue = '我是被自动更新的值~'
console.log('push更新数组属性值...')
data.arr.push({
name: 'haha'
})
console.log('下标更新数组属性值...')
data.arr[0] = {
name: 'ttttt'
}
console.log('更新对象属性中的属性值...')
data.obj.name = 'changeObjName'
console.log('读取值:', data.obj.name)
}, 2000);
// 初始数组
const arr = [1, 2, 3, 4];
// 监听数组
const newArr = new Proxy(arr, {
get: function (target, key, receiver) {
console.log('get:', key);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log('set:', target, key, value, receiver);
if (key === 'length') {
Render.init(target);
}
if (typeof + key === 'number') {
target[key] = value;
Render.init(target);
}
return Reflect.set(target, key, value, receiver);
},
});
window.onload = function () {
Render.init(newArr);
}
// push数字
btn.addEventListener('click', function () {
newArr.push(6);
});
modify.addEventListener('click', function () {
newArr[0] = 8888
});
document.querySelector('#input').addEventListener('keyup', changeValue)
</script>
结果:
Proxy实现响应式的优势
Proxy可以监听数组通过push、pop、shift、unshift等方法做的修改以及下标修改数组的方式修改的值变化。
Proxy不仅仅只能拦截到对象的读取和赋值,还支持对更多对象的操作的拦截, 例如apply、constructor、delete、defineProperty等等。
Proxy作为新标准将受到浏览器厂商重点持续的性能优化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。