共计 2123 个字符,预计需要花费 6 分钟才能阅读完成。
在 Vue 中应用选项式 API,去申明响应式对象是很简略的。咱们只须要将须要响应式的变量属性放进 data 函数中,并 return 进去。Vue 框架会帮咱们把数据变为响应式,并在模板中可用。如上面所示。
export defaut {
// other
data() {
return {hello: 'world'}
}
//... other code
}
但对于 Composition API,事件就没那么简略了。状态申明必须应用 Ref 和 Reactive 函数来实现,这两个函数的行为表现也不一样。
让咱们讨论一下 Vue 3 中产生了什么变动以及为什么咱们须要两个不同的助手。
Vue3 中的响应式实现
大家都晓得 Vue2 中,是通过 Object.defineProperty 实现的依赖收集和响应式解决。但在 Vue3 应用了 javascript 的 Proxy API 齐全重写了响应式外围。
Proxy 是一种更古代更优雅的 API 能够实现对一个对象的代理和劫持。
你能够通过下边一段代码来理解 Proxy 是如何工作的:
const userInfo = {
name: "sean",
age: 35,
};
const handler = {get(target, property) {if (property === "name") {const name = target[property]
return name.charAt(0).toUpperCase() + name.slice(1);
}
if (property === "age") {return '--'}
return target[property]
},
};
const proxyObj = new Proxy(userInfo, handler);
console.log(proxyObj.name) // "Sotis"
console.log(proxyObj.age) // "--"
handler 内的 get 办法称为 trap,每次拜访对象的属性时都会调用该办法。
同样的情理,咱们很容易就能推断出一个 set 的 trap 办法。
const userInfo = {
name: "Sean",
age: 35,
};
const handler = {set(target, prop, value) {if (prop === "age") {if (!Number.isInteger(value)) {throw new TypeError("age 类型谬误");
}
if (value > 200) {throw new RangeError("额,如同超过了范畴了");
}
}
target[prop] = value;
return true;
},
};
const proxy = new Proxy(userInfo, handler);
proxy.age = 12 // OK
proxy.age = 300 // Error: 额,如同超过了范畴了
这就是 Vue3 的响应式实现的外围原理,当咱们应用 Reactive 工具办法申明响应式属性的时候,框架底层会通过 Proxy 来实现,属性的变更追踪和依赖解决。
function reactive(obj) {
return new Proxy(obj, {get(target, key) {track(target, key)
return target[key]
},
set(target, key, value) {target[key] = value
trigger(target, key)
}
})
}
当然,Vue 框架中的响应式,实现要比这简单的多。框架须要解决很多边界状况。然而外围的原理还是应用了 Proxy。如果你对 Vue 的 Reactive 的具体实现感兴趣,请看如下连贯。
https://github.com/vuejs/core/blob/main/packages/reactivity/src/reactive.ts#L83
下面的代码片段解释了为什么将响应式变量解构或重新分配给局部变量不再具备响应式的个性,因为它不再触发源对象上的 get/set 代理 track。
这看起来是一个让所有变得响应式的完满解决方案。然而有一个问题!依据定义,Proxy 仅实用于简单类型 (对象、数组、映射和汇合)。
想要将原始值 (Boolean、Number、BigInt、String、Symbol、undefined 和 null 等类型的值) 变成响应式数据,就必须对其做一层包裹,也就是咱们接下来要介绍的 ref。
function ref(value) {
const refObject = {get value() {track(refObject, 'value')
return value
},
set value(newValue) {
value = newValue
trigger(refObject, 'value')
}
}
return refObject
}
通过将原始值,进行包装成 Object 咱们实现了,原始值的响应式。
这也就解释了为什么必须在脚本设置中应用的烦人的 .value。
同样,重组或重新分配给局部变量是行不通的。
总结
所以为什么同时须要 Ref 和 Reactive 的答案是:
- 代理。对于简单类型,能够间接应用它们,但对于原始类型数据须要创立代理对象。
心愿理解 Vue 的底层工作原理能够让您更加高效,并打消 ref 和 Reactive 之间的混同。