vue3响应式的实现
Vue 3 中,数据是基于 ES6 Proxy 实现响应式的。
一、Proxy对象
- 作用:用于创建一个对象的代理,实现对其拦截、自定义等操作
- 入参:
- target:被 Proxy 代理的目标对象
- handler:容纳一批特定属性的占位符对象,包含有
Proxy
的各个捕获器(如:handler.defineProperty()
是Object.defineProperty
方法的捕捉器;handler.deleteProperty()
是delete
操作符的捕捉器。;handler.get()
是属性读取操作的捕捉器;handler.set()
是属性设置操作的捕捉器。)
- 简单示例:
const target = {
message1: "hello",
message2: "everyone",
};
const handler = {
get: function(target, prop) {
return prop in target ? target[prop] : 17;
},
set: function(target, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy)
[图片上传失败...(image-5572f3-1681302523903)]
console.log(proxy.message1) // hello
console.log(proxy.age) // 17
proxy.age ='1'
[图片上传失败...(image-b753e2-1681302523903)]
二、Reflect
-
Reflect
并不是一个构造函数,所以不能通过 new对其进行调用,也不能将Reflect
对象作为一个函数来调用。Reflect
的所有属性和方法都是静态的 -
Reflect
是一个内置的对象,它提供拦截 JavaScript 操作的静态方法,与 ES6 Proxy handler 的方法相同 - 若需要在
Proxy
内部调用对象的默认行为,建议使用Reflect
,如某些Object
操作是命令式,比如:name in target
和delete target[name]
,即可以使用Reflect.has(target, name)
和Reflect.deleteProperty(target, name)
,使其变成函数行为
三、vue3的reactive
和ref
ref
ref接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value
。
const test = ref(1)
[图片上传失败...(image-f53b6a-1681302523903)]
const test = ref({a: 1})
[图片上传失败...(image-a2c5b3-1681302523903)]
源码:
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
const toReactive = (value: T): T =>
isObject(value) ? reactive(value) : value
class RefImpl {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
constructor(value: T, public readonly __v_isShallow: boolean) {
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
reactive
reactive返回一个对象的响应式代理,可以将「引用类型」数据转换为「响应式」数据
参数: reactive参数必须是对象或数组
const test = reactive(1)
[图片上传失败...(image-27809a-1681302523903)]
[图片上传失败...(image-eb3aa8-1681302523903)]
const test = reactive({a: 1})
[图片上传失败...(image-d7577c-1681302523903)]
相关代码(摘取于版本3.2.33
):
// 判断是否为对象
const isObject = val => val !== null && typeof val === 'object';
export function reactive(target) {
// 首先先判断是否为对象
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
const handler = {
get(target, key, receiver) {
// ...收集依赖 track
const result = Reflect.get(target, key, receiver)
// 深度监听
if (isObject(result)) {
return reactive(result);
}
return result;
},
set(target, key, value, receiver) {
// 首先先获取旧值
const oldValue = (target as any)[key]
const result = Reflect.set(target, key, value, receiver);
// ...触发依赖更新 trigger
return result
},
// 其他方法
// ...
}
return new Proxy(target, handler)
}
shallowReactive是reactive
的浅层作用形式,因为它创建的树具有不一致的响应行为,需谨慎使用
function createGetter(shallow = false) {
return function get(target, key, receiver) {
// ...一些判断
const res = Reflect.get(target, key, receiver)
if (shallow) {
return res
}
if (isObject(res)) {
return reactive(res)
}
return res
}
}
更多响应式 API
查看Vue文档,总结来看:
reactive
通过Proxy
实现,可以将引用类型值变为响应式,ref
通过监听类的value属性的get
和set
实现,当传入的值为引用类型时,内部还是使用reactive
方法进行处理,可将基本类型和引用类型都变成响应式。
四、vue3的track
和trigger
[图片上传失败...(image-14adf4-1681302523903)]