上期简单记录了下
Vue3.0给我们带了哪些变化,本来想打算研究下周边配套插件,结果发现在目前的版本中好像还没法使用Vuex和Vue-Router,至少目前我没找到相关资料。那本期就来探索一下Vue3.0的响应式原理
之前在尤大提出Vue3.0响应式要使用Proxy来重写的时候我就去看过Proxy的相关介绍,但是在平时项目中基本是用不到这个的,所以理解起来肯定有点儿吃力(明明是自己太菜了好吗?),正好这段时间项目时间不紧张,抽空看了下Vue3.0的响应式原理,下面简单记录一下。
我们都知道Vue2.x的响应式是基于Object.defineProperty来实现的,其缺点我们应该也知道,它不能对对象的属性的增删操作做出处理,同时也不能监听数组的下标的变化,我们先来看看2.x中响应式的核心部分:
2.x响应式核心部分
位置:vue/src/core/observer/index.js
可以大致看到2.x的响应式系统是在
get时进行依赖收集,在set时进行消息通知,也就是说在Vue组件初始化时,Vue会对data中的每一个属性进行如上操作,关于Vue2.x源码目前我还只能看懂一些,代码中一层套一层,跟俄罗斯套娃一样,看不了多少就被绕晕了。
相较于2.x,3.0的源码看上去就友好很多,而且3.0的响应式系统是被独立出来的,可以与其他任何框架结合使用,因此代码之间的嵌的就不是很复杂,也不用在多个文件之间来回切换。接下来我们来看看3.0的响应式原理。
在看3.0的响应式原理前,我想我们有必要先了解一下Proxy和Reflect,只有了解了Proxy和Reflect后我们才能更好的理解3.0的响应式原理。
Proxy
ES6中是这样描述
Proxy的:Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(metaprogramming),即对编程语言进行编程。Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
什么意思?就是说我们能够在对一个对象进行操作前能够对该行为进行修改,比如get和set,这个不是跟Object.defineProperty一样的吗?并不是,Object.defineProperty是对对象属性的行为做修改,而Proxy代理的是整个对象,这也是2.0不能检测对象属性的增删的原因,当然我们可以通过其他方法为对象添加属性。Proxy厉害就厉害在这里,当然Proxy支持的不止这两个行为,还有其他11种行为,详细的请参见:ECMAScript 6 入门
Reflect
Reflect在ES6中的概述是这样的:Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新 API。
emmmm,啥子意思?看不懂啊,看了下例子,似乎明白了一些,Reflect中的方法和Proxy中的方法是一一对应的,也就是说Reflect同样也有13种行为,对于get和set会返回默认的行为,看下面例子:
当我们注释到上面的 return Reflect.set(target,key,value)时,控制会打印出:阿强强 Change By Proxy,去掉注释后,控制台会打印出:阿强强,我们明明已经通过target[key] = value+' Change By Proxy'修改了name属性的值,但是使用Reflect.set(target,key,value)后,会返回修改前的值,同样get也是如此,大家可以自行测试。
简单了解了Proxy和Reflect后,接下来我们来看看3.0的响应式原理。
3.0响应式核心部分
位置:@vue/reactivity/dist/reactivity.global.js
这里是我们使用reactive函数创建响应式数据时需要调用的函数,这里还不是最为关键地方,最关键的地方应该handler部分。接下来我们看看 各个handler 。
handler一共有四个,分别为:
| Handler | 类型 |
|---|---|
| mutableHandlers | 可读写 |
| shallowReactiveHandlers | 可读写 |
| readonlyHandlers | 只读 |
| shallowReadonlyHandlers | 只读 |
我们从名字中就能看出几个handler的细微区别,具体有哪些差别大家可查看源码:@vue\reactivity\dist\reactivity.global.js,今天简单查看一下mutableHandlers这个handler中的get和set。 |
Handler 中的 setter 部分
位置:@vue\reactivity\dist\reactivity.global.js
这里可以看出
Vue3.0默认是会对增加对象属性做出响应,这里的trgger暂时还没来的及看,猜测应该是进行依赖收集。
再看看getter的源码:
Handler 中的 getter 部分
位置:@vue/reactivity/dist/reactivity.global.js
在我看到getter之前我一直有个疑问: vue是在什么时候对整对象进行依赖收集的,我们知道vue2.x在应用初始化时对data中的字段递归的进行依赖收集,但是当我在getter中的看到:
return shared.isObject(res)
? isReadonly
? // need to lazy access readonly and reactive here to avoid
// circular dependency
readonly(res)
: reactive(res)
: res;
vue3在getter中对属性值为Object的类型又进行了一次代理,大胆猜测一下:难道vue3依赖收集是实时进行的?好了本期先就探索到这里,文中有不当地方还请各位指正,不胜感激!
转载或摘抄须注明出处。