VUE2X部分
1、实现原理
- 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持).
- 数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹).
Object.defineProperty(data,'count',{
get(){};
set(){};
})
2、存在问题
- 新增属性、删除属性、界面不会更新.
- 直接通过下表修改数组、界面不会自动更新.
3、体验下VUE2的响应式
4、响应式原理
1、我们先去控制台,输入person,回车看看我们的源数据是什么,再输入p,回车看看我们的p对象是什么?
2、我们在控制台输入person.name="李四",然后回车看看控制台有什么?
3、我们在控制台输入person.sex="男",然后回车看看控制台有什么?
4、我们在控制台输入delete person.name,然后回车看看控制台有什么?
这个时候,我们修改代码,如下:
这个时候,我们删除的结果是true,但不是响应式的奥,原因就是,我们无法捕获到删除了person对象的name属性,我们可以捕获到name的读取,也能够捕获到name的修改,但是新增和删除属性捕获不到,所以说他的响应式有点小问题,但是,而且还有一点吧,就是Object.defineProperty我被重复执行好多次,显然这样的不是最好的。
VUE3X部分
1、实现原理
- 通过Proxy(代理):拦截对象中任意变化,包括:属性值的读写、属性值的添加、属性值的删除。
- 通过Reflect(反射):被代理对象的属性操作。
- MDN文档中描述Proxy与Reflect:
Proxy: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Reflect: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect 2、初识VUE3的双向绑定(简单的认识)
实际上,我们能够这么丝滑的体验VUE3的响应式,得感谢reactive,有了他的存在,我们才有如此美妙的感觉。
3、VUE3 的响应式原理
- Proxy(代理)
Proxy,看到这个,我们是不是比较眼熟,因为我们之前在写VUE项目配置接口代理(解决Ajax跨域)的时候用过
// 源数据
let person = {
name: '张三',
age: 18
}
const p = new Proxy();
这里的new Proxy()代理的作用就是:可以映射p对person的操作
Proxy的参数:Proxy("目标对象-也就是代码哪个对象的操作")
1、我们先去控制台,输入person,回车看看我们的源数据是什么,再输入p,回车看看我们的p对象是什么?
[[Handler]]:当我们给对象中的属性做任何操作(增删改查)的时候都会去出触发其里面的配置来完成。(不做关注,直接合起来就行)
2、在控制台输入p.name="李四",然后再输入person回车看看控制台有什么?
3、在控制台输入p.sex="男",然后再输入person回车看看控制台有什么?
4、在控制台输入delete p.age,然后再输入person回车看看控制台有什么?
咦,根据上面的操作,我们可以发现,我们增删改查对象的某个值的时候,person都会发生对应的变化,这不就是传说中的响应式么?
这个理解就不对了,因为我们刚开始的时候就说过了,proxy是代理,那我们看到所谓的响应式只是个代理的操作而已,这个时候我们可以更加深刻的来理解下什么叫做代理?代理在这个demo中就是,打在P的身上,疼在person的身上。
接下来,我们再次填充上面的代码块如下:
在上面的代码块中,我们可以看到:VUE3的响应式也有get()和set()方法,那不和VUE2的响应式一样么?然而,事实并非如此,因为我们用的不是Object.defineProperty的get()和set(),毕竟Object.defineProperty的get()和set()稍微有点弱智, 为什么敢说如此狂妄的话呢?那接下来我们就以前验证下VUE3的响应式原理,感受下究竟哪里比VUE2响应式原理牛逼!
1、我们先去控制台,输入person,回车看看我们的源数据是什么,再输入p,回车看看我们的p对象是什么?
2、在控制台输入p.name="李四",然后再输入person回车看看控制台有什么?
3、在控制台输入p.sex="男",然后再输入person回车看看控制台有什么?
4、在控制台输入delete p.age,然后再输入person回车看看控制台有什么?
- Reflect(反射)
let obj={a:1,b:2}
这里有个问题,我们如何拿到obj下面的a的值呢?这个问题是不是有点侮辱人?确实是的,我们有一中都知道的方式
但是,我们还有一种不常用的方式,我们隆重的介绍一下window下的Reflect,
首先,我们觉得直接用obj.XXX增删改查的时候比用ES6 提供的Reflect方便多了,那为什么还有Reflect的出现,而且据可靠消息,ECMA正在将Object下比较常用的API往Reflect聚合。 要是这玩意真的和我们现在看到的一样没用,为什么那些大佬们花时间经理来做这些事呢,这必定有其中的道理,我么一起接着往下研究。
我们按照上述代码,我们属性c不也添加到obj对象中了么?是的,是添加了,我们再接着往下看
咦,这不是报错了么?什么情况?这个报错也就是:不能重复定义一个属性,不和我们想的一样覆盖掉,会报错,而且JS是单线程的,遇到报错不会再去往下执行的。
这个时候,我们发现没报错,且在obj中添加了c为3,但是为什么不是4呢?我们再往下去研究
所以通过上面的代码块,我们可以发现Reflect.defineProperty(...)会给我们返回一个布尔值,但是我们需要考虑一个问题,就是:在正常的代码中,就算返回布尔值,对我们有什么用呢?还不如直接给我抛出错误,我还比较好定位问题在哪里? 其实,这个东西在一个简单的对象中这么简单的应用是体现不出来它的价值的。比如我们此时在封装一个框架,从这个角度上思考,我们希望框架的用户在写错一个东西的时候不影响别的代码的执行,那在没有Reflect的情况下,我们一定会用到try...catch..来 捕获异常,想一下哈,这么写的话是不是满屏幕的代码都是捕获异常?是不是代码很繁琐,也显得代码比较low,所以这个时候我们直接使用Reflect就比较完美的解决这个困惑。
那这个Reflect(反射),在我们VUE3响应式原理中有什么重要的作用呢?那就接着往下看看!
上面的代码块才是实现了VUE3响应式原理的代码雏形。 这个时候,我们可以运行以下的操作,再次体验一下VUE3的响应式!
1、输入person.name="李四",然后再出入person回车看看?再输入P回车看看?
2、输入person.sex="男",然后再出入person回车看看?再输入P回车看看?
3、输入delete person.name,然后再出入person回车看看?再输入P回车看看?