Vue3.0 以前组件实例在初始化的时候会将 data 整个对象变为可观察对象,通过递归的方式给每个 Key 使用Object.defineProperty 加上 getter 和 settter,如果是数组就重写代理数组对象的七个方法。而在 Vue3.0 中,将可响应式对象创建的权利交给了开发者,开发者可以通过暴露的 reactive, compted, effect 方法自定义自己需要响应式能力的数据,实例在初始化时不需要再去递归 data 对象了,从而降低了组件实例化的时间。
Vue3.0 以前生成响应式对象会对对象进行深度遍历,同时为每个 Key 生成一个 def 对象用来保存 Key 的所有依赖项,当 Key 对应的 Value 变化的时候通知依赖项进行 update。但如果这些依赖项在页面整个生命周期内不需要更新的时候,这时 def 对象收集的依赖项不仅没用而且还会占用内存,如果可以在初始化 data 的时候忽略掉这些不会变化的值就好了。Vue3.0 通过暴露的 reactive 方法,开发者可以选择性的创建可观察对象,达到减少依赖项的保存,降低了运行内存的使用。
- Map、Set、WeakSet、WeakMap的监听
前面提到 Proxy 可以代理所有的对象,立马联想到了 ES6 里面新增的集合 Map、Set, 聚合类型的支持得益于 Proxy 和 Reflect。讲真的这之前还真不知道 Proxy 这么刚啥都能代理,二话不说直接动手用 Proxy 代理了一个 map 试试水
- let map = new Map([["name","zhengcaiyun"]])
- let mapProxy = new Proxy(map, {
- get(target, key, receiver) {
- console.log("取值:",key)
- return Reflect.get(target, key, receiver)
- }
- })
- mapProxy.get("name")
- Uncaught TypeError: Method Map.prototype.get called on incompatible receiver [object Object]
一盆凉水泼来,报错了。原来 Map、Set 对象赋值、取值和他们内部的 this 指向有关系,但这里的 this 指向的是其实是 Proxy 对象,所以得这样干
- let map = new Map([['name','wangyangyang']])
- let mapProxy = new Proxy(map, {
- get(target, key, receiver) {
- var value = Reflect.get(...arguments)
- console.log("取值:",...arguments)
- return typeof value == 'function' ? value.bind(target) : value
- }
- })
- mapProxy.get("name")
当获取的是一个函数的时候,通过作用域绑定的方式将原对象绑定到 Map、Set 对象上就好了。
Vue3.0 是如何实现集合类型数据监听的?
眼尖的同学看完上面这段代码会发现一个问题,集合是没有 set 方法,集合赋值用的是 add 操作,那咋办呢?来看看那么 Vue3.0 是怎么处理的,上一段简化后的源码
- function reactive(target: object) {
- return createReactiveObject(
- target,
- rawToReactive,
- reactiveToRaw,
- mutableHandlers,
- mutableCollectionHandlers
- )
- }
-
- function createReactiveObject(
- target: any,
- toProxy: WeakMap<any, any>,
- toRaw: WeakMap<any, any>,
- baseHandlers: ProxyHandler<any>,
- collectionHandlers: ProxyHandler<any>
- ) {
- //collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
- const handlers = collectionTypes.has(target.constructor)
- ? collectionHandlers
- : baseHandlers
- //生成代理对象
- observed = new Proxy(target, handlers)
- toProxy.set(target, observed)
- toRaw.set(observed, target)
- if (!targetMap.has(target)) {
- targetMap.set(target, new Map())
- }
- return observed
- }
(编辑:ASP站长网)
|