Vue2 跟Vue3 的响应式系统概念从框架本身来看没有任何的改变。
我们来回顾下 Vue2 说:
当你把一个普通的 JavaScript 对象传入 Vue 实例作为data选项,Vue 将遍历此对象所有的 property,并使用Object.defineProperty把这些 property 全部转为getter/setter。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。
每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
Vue3说:
每个组件实例都有一个相应的侦听器实例,该实例将在组件渲染期间把“触碰”的所有 property 记录为依赖项。之后,当触发依赖项的 setter 时,它会通知侦听器,从而使得组件重新渲染。
将对象作为数据传递给组件实例时,Vue 会将其转换为 Proxy。这个 Proxy 使 Vue 能够在 property 被访问或修改时执行依赖项跟踪和更改通知。每个 property 都被视为一个依赖项。
首次渲染后,组件将跟踪一组依赖列表——即在渲染过程中被访问的 property。反过来,组件就成为了其每个 property 的订阅者。当 Proxy 拦截到 set 操作时,该 property 将通知其所有订阅的组件重新渲染。
虽然概念相似但底层实现方式却有本质的不同, Vue2 采用的是 ES5 Object.defineProperty, Vue3 采用的是 ES6 Proxy。 用法也有差异Vue2 推崇optionsAPI , Vue3 推荐 compositionAPI。
Vue2:
var vm = new Vue({
data:{
count:1
}
});
// data 对象上的 property 转换为响应式 property 。
Vue3:
export default {
setup() {
const book = reactive({ title: 'Vue 3 Guide' })
return {
book
}
}
}
//reactive函数返回对象的响应式副本, 对象上的 property 转换为响应式 property。
关于Vue2 响应式系统的实现可以看这篇文章。Vue3.0 放弃 Object.defineProperty 你了解多少?
接来下进入 Reactive API
我们先来看一下 reactive 函数的具体实现过程:
function reactive(target) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && target["__v_isReadonly" /* IS_READONLY */]) {
return target;
}
return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers);
}
reactive 内部通过 createReactiveObject 函数把 target 变成了一个响应式对象。__v_isReadonly 是 ComputedRefImpl 实例才有的属性,如果属性可读不可写,__v_isReadonly 值为 false。
更多信息请参考__v_isReadonly 与 computed
mutableHandlers、 mutableCollectionHandlers 参数对象
createReactiveObject函数:
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {
if (!isObject(target)) {
{
console.warn(`value cannot be made reactive: ${String(target)}`);
}
return target;
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (target["__v_raw" /* RAW */] &&
!(isReadonly && target["__v_isReactive" /* IS_REACTIVE */])) {
return target;
}
// target already has corresponding Proxy
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target);
if (targetType === 0 /* INVALID */) {
return target;
}
const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
proxyMap.set(target, proxy);
return proxy;
}
老板叫我暂时不能摸鱼... 喜欢就收藏明天写完。