vue2.X实现原理
vue初始化的时候,会用Object.definePropery重新定义data中的所有属性,当页面使用对应属性的时候
watcher进行依赖收集
如果属性发生变化会通知依赖进行更新操作-发布订阅
通过 Object.defineProperty
实现的
<body>
<div id="app">
<input type="text" id="txt">
<p id="show-txt"></p>
</div>
<script>
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show-txt').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
</body>
关于defineProperty的缺点很明显:
- Object.defineProperty监听的是对象的属性,如果对象比较复杂,需要逐个深层遍历他的属性来实现监听,耗费性能
- Object.defineProperty无法监听数组的变化,使Vue不得不对数组做了额外的hack。
vue3.0实现原理
改用proxy替代Object.definePropery,它可以监听对象和数组的变化,有13种种拦截方法
// 语法
let p = new Proxy(target, handler);
这里重点说一下
handler
:handler
本身就是ES6所新设计的一个对象.它的作用就是用来自定义代理对象的各种可代理操作。它本身一共有13中方法,每种方法都可以代理一种操作,常用的几种方法如下:
// 在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, "foo", {}) 时。
handler.defineProperty()
// 在判断代理对象是否拥有某个属性时触发该操作,比如在执行 "foo" in proxy 时。
handler.has()
// 在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。
handler.get()
// 在给代理对象的某个属性赋值时触发该操作,比如在执行 proxy.foo = 1 时。
handler.set()
// 在删除代理对象的某个属性时触发该操作,比如在执行 delete proxy.foo 时。
handler.deleteProperty()
// 在获取代理对象的所有属性键时触发该操作,比如在执行 Object.getOwnPropertyNames(proxy) 时。
handler.ownKeys()
// 在调用一个目标对象为函数的代理对象时触发该操作,比如在执行 proxy() 时。
handler.apply()
// 在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy() 时。
handler.construct()
与其说vue3.0的新特性,倒不如说是Proxy的优点:
- 直接监听对象
- 可以监听数组变化
- 多种拦截方式更加强大
同样的功能,那么proxy如何实现呢?
let input = document.getElementById('txt')
let p = document.getElementById('show-txt')
const obj = {}
const newObj = new Proxy(obj, {
get: function (target, key, receiver) {
console.log(target, key, receiver, 'get');
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(target, key, value, receiver, 'set');
if (key === "txt") {
input.value = value
p.innerHTML = value;
}
return Reflect.set(target, key, value, receiver);
}
})
input.addEventListener('keyup', function(e) {
newObj.txt = e.target.value;
})
检测数组的时候可能多次触发多次get/set,防止多次触发?
判断key是否为当前被代理对象target自身属性,也可以判断旧值和新值是否相等
满足条件,再执行trigger