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 = newValuedocument.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 = valuep.innerHTML = value;}return Reflect.set(target, key, value, receiver);}})input.addEventListener('keyup', function(e) {newObj.txt = e.target.value;})
检测数组的时候可能多次触发多次get/set,防止多次触发?
判断key是否为当前被代理对象target自身属性,也可以判断旧值和新值是否相等
满足条件,再执行trigger
