摘要:对以下API的使用和源码进行解析
- reactive
- ref
- computed
- toRef
- toRefs
- watch[todo]
- watchEffect[todo]
reactive
创建一个响应式对象
const obj = reactive({ count: 0 })
obj.count++
原理: 基于proxy拦截对对象属性的访问和赋值
export function shallowReactive<T extends object>(
target: T,
): ShallowReactive<T> {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers,
shallowReactiveMap,
)
}
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>,
) {
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
)
proxyMap.set(target, proxy)
return proxy
}
ref
接受基本类型和对象, 创建一个响应式对象,
const count = ref(0)
console.log(count.value)
基础类型基于对对象的value属性访问和设置,建立响应式;对象转换为reactive创建响应式对象
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
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)) {
const oldVal = this._rawValue
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, DirtyLevels.Dirty, newVal, oldVal)
}
}
}
computed
可以创建一个只读的ref或者创建一个可写的计算属性 ref
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value)
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
如果computed传入的是函数,就只读;如果是对象就初始化ComputedRefImpl来做响应式
export function computed(getterOrOptions) {
let getter
let setter
if (typeof getterOrOptions === 'function') {
getter = getterOrOptions
setter = () => {
console.warn('Write operation failed: computed value is readonly')
}
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter, typeof getterOrOptions === 'function' || !getterOrOptions.set)
return cRef
}
ComputedRefImpl的实现
class ComputedRefImpl<T> {
private _value!: T;
private _dirty = true;
public dep?: Dep = undefined;
public readonly effect: ReactiveEffect<T>;
constructor(getter: ComputedGetter<T>, private readonly _setter: ComputedSetter<T>) {
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true;
triggerRefValue(this);
}
});
this.effect.computed = this;
this.effect.active = true;
}
get value() {
if (this._dirty) {
this._value = this.effect.run();
this._dirty = false;
}
trackRefValue(this);
return this._value;
}
set value(newValue: T) {
this._setter(newValue);
}
}
toRef
使用
const props = defineProps(/* ... */)
useSomeFeature(toRef(props, 'foo'))
原理
export function toRef(
source: Record<string, any> | MaybeRef,
key?: string,
defaultValue?: unknown,
): Ref {
if (isRef(source)) {
return source
} else if (isFunction(source)) {
return new GetterRefImpl(source) as any
} else if (isObject(source) && arguments.length > 1) {
return propertyToRef(source, key!, defaultValue)
} else {
return ref(source)
}
}
toRefs
使用:将对象每一个属性都转为ref, 可以解构对象并不失去响应式
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
state.foo++
console.log(stateAsRefs.foo.value)
stateAsRefs.foo.value++
console.log(state.foo)
原理
export function toRefs<T extends object>(object: T): ToRefs<T> {
if (__DEV__ && !isProxy(object)) {
warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret: any = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = propertyToRef(object, key)
}
return ret
}
参考
响应式 API:核心 | Vue.js (vuejs.org)