“别再更新了,实在是学不动了”这句话道出了多少前端开发者的心声,”不幸”的是 Vue 的作者在国庆区间发布了 Vue3.0 的 pre-Aplha 版本,这意味着 Vue3.0 快要和我们见面了。既来之则安之,扶我。..
“别再更新了,实在是学不动了”这句话道出了多少前端开发者的心声,”不幸”的是 Vue 的作者在国庆区间发布了 Vue3.0 的 pre-Aplha 版本,这意味着 Vue3.0 快要和我们见面了。既来之则安之,扶我起来我要开始讲了。Vue3.0 为了达到更快、更小、更易于维护、更贴近原生、对开发者更友好的目的,在很多方面进行了重构:
使用 Typescript
放弃 class 采用 function-based API
重构 complier
重构 virtual DOM
新的响应式机制
今天咱就聊聊重构后的响应式数据。
一、Vue3.0响应式数据
1.1 尝鲜
重构后的 Vue3.0 和之前在写法上有很大的差别,早前在网络上对于 Vue3.0 这种激进式的重构方式发起了一场讨论,见仁见智。不多说先看看 Vue3.0 在写法上激进到什么程度。
1 | <!DOCTYPE html> |
reactive(value) 创建可观察的变量,参数可以是 JS 原始类型、引用、纯对象、类实例、数组、集合(Map|Set)。
effect(fn) effect 意思是副作用,此方法默认会先执行一次。如果 fn 中有依赖的可观察属性变化时,会再次触发此回调函数
computed(()=>expression) 创建一个计算值,
computed 实现也是于 effect 来实现的,特点是 computed 中的函数不会立即执行,多次取值是有缓存机制的, expression 不应该有任何副作用,而仅仅是返回一个值。当这个 expression 依赖的可观察属性变化时,这个表达式会重新计算。1 |
|
},
1 | get(){ |
}
})
1 | let index = arr[1]; |
1 | 没毛病,一切变化都在人家的掌握中。上面这段代码,有没有人没看懂,我假装你们都不懂,贴张图 |
}
})
1 | mapProxy.get("name") |
`一盆凉水泼来,报错了。原来```text
Map、Set
let map = new Map([['name','wangyangyang']])
var value = Reflect.get(...arguments)
console.log("取值:",...arguments)
return typeof value == 'function' ? value.bind(target) : value
}
})
`当获取的是一个函数的时候,通过作用域绑定的方式将原对象绑定到```text
``` 对象上就好了。
Vue3.0 是如何实现集合类型数据监听的?
眼尖的同学看完上面这段代码会发现一个问题,集合是没有 set 方法,集合赋值用的是 add 操作,那咋办呢?来看看那么 Vue3.0 是怎么处理的,上一段简化后的源码
```javascript
function 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
}
`根据 target 类型适配不同的 handler,如果是集合 (```text
``` )就使用 collectionHandlers,是其他类型就使用 baseHandlers。接下来看看 collectionHandlers
```xml
export const mutableCollectionHandlers: ProxyHandler<any> = {
get: createInstrumentationGetter(mutableInstrumentations)
}
export const readonlyCollectionHandlers: ProxyHandler<any> = {
get: createInstrumentationGetter(readonlyInstrumentations)
}
`没有意外只有 get,骚就骚在这儿:```java
// 可变数据插桩对象,以及一系列相应的插桩方法
const mutableInstrumentations: any = {
get(key: any) {
return get(this, key, toReactive)
},
get size() {
return size(this)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false)
}
// 迭代器相关的方法
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
iteratorMethods.forEach(method => {
mutableInstrumentations[method] = createIterableMethod(method, false)
readonlyInstrumentations[method] = createIterableMethod(method, true)
})
// 创建getter的函数
function createInstrumentationGetter(instrumentations: any) {
return function getInstrumented(
key: string | symbol,
receiver: any
) {
target =
hasOwn(instrumentations, key) && key in target ? instrumentations : target
}
`由于 Proxy 的 traps 跟```text
Map|Set
``` 集合的原生方法不一致,因此无法通过 Proxy 劫持 set,所以作者在在这里进行了"偷梁换柱",这里新创建了一个和集合对象具有相同属性和方法的普通对象,在集合对象 get 操作时将 target 对象换成新创建的普通对象。这样,当调用 get 操作时 Reflect 反射到这个新对象上,当调用 set 方法时就直接调用新对象上可以触发响应的方法,是不是很巧妙?所以多看源码好处多多,可以多学学人家的骚操作。
#### 1.3 IE 怎么办
这是个实在不想提但又绕不开的话题,IE 在前端开发者眼里和魔鬼没什么区别。在 Vue3.0 之前,响应式数据的实现是依赖 ES5 的 Object.defineProperty,因此只要支持 ES5 的浏览器都支持 Vue,也就是说 Vue2.x 能支持到 IE9。Vue3.0 依赖的是 Proxy 和 Reflect 这一对出生新时代的 CP,且无法被转译成 ES5,或者通过 Polyfill 提供兼容,这就尴尬了。开发者技术前线获悉的信息,官方在发布最终版本之前会做到兼容 IE11,至于更低版本的 IE 那就只有送上一曲凉凉了。
其实也不用太纠结IE的问题,因为连微软自己都已经放弃治疗 IE 拥抱 Chromium 了,我们又何必纠结呢?
#### 1.4 结语
在使用开源框架时不要忘了,我们之所以能免费试用他,靠的维护者投入的大量精力。希望我们多去发现它带来的优点和作者想通过它传递的编程思想。最后期待 Vue3.0 正式版本的早日到来。
本文标题: 聊聊Vue30响应式数
发布时间: 2021年02月11日 00:00
最后更新: 2025年12月30日 08:54
原始链接: https://haoxiang.eu.org/be423f1a/
版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!

