toRef
- 创建一个基于响应式对象对应的
ref
import { reactive, toRef } from 'vue';
const data = reactive({ heroName: "张郃" })
const heroName: any = toRef(data, 'heroName')
const change = () => {
heroName.value = '荀彧'
console.log('修改后heroName.value: ', heroName.value); // 修改后heroName.value: 荀彧
}
<div>
<div>{{ heroName }}</div>
<button @click="change">修改</button>
</div>
- 对非响应式对象使用
toRef
,值虽然会发生改变,但是视图不会更新
import { toRef } from 'vue';
const data = { heroName: "张郃" } // <---------- 普通对象
const heroName: any = toRef(data, 'heroName') // <------ 没啥用
const change = () => {
heroName.value = '荀彧'
console.log('修改后heroName.value: ', heroName.value); // 修改后heroName.value: 荀彧
}
<div>
<div>{{ rData }}</div>
<button @click="change">修改</button>
</div>
toRefs
- 把响应式对象中的所有属性,都变成
ref
import { reactive, toRefs, onMounted } from 'vue'
const data = reactive({ a: "赵云", b: '周泰', c: '许褚' })
const data2 = reactive({ d: '夏侯惇', e: '张辽', f: '孙尚香' })
const { a, b, c } = toRefs(data)
const { d, e, f } = data2
onMounted(() => {
console.group("== 使用 toRefs 方式的 ==");
console.log(a);
console.log(b);
console.log(c);
console.groupEnd();
console.group("== 不使用 toRefs 方式的 ==");
console.log(d);
console.log(e);
console.log(f);
console.groupEnd();
})
toRefs
实际上是把响应式对象中的每一个属性都调用了一遍toRef
toRaw
- 根据响应式对象,返回其普通(原始)对象,失去响应式
import { reactive, toRaw, onMounted } from 'vue'
const data = reactive({ name: "马云禄" })
const rawData = toRaw(data)
onMounted(() => {
console.log(data); // Proxy {name: '马云禄'}
console.log(rawData); // {name: '马云禄'}
console.log(data['__v_raw']); // '__v_raw' 是源码中的内容
})
源码
toRef、toRefs源码地址: \core-main\packages\reactivity\src\ref.ts
toRaw 源码地址: \core-main\packages\reactivity\src\reactive.ts
toRef
export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K
): ToRef<T[K]>
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K,
defaultValue: T[K]
): ToRef<Exclude<T[K], undefined>>
// 上面是函数重载,不用管它
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K,
defaultValue?: T[K]
): ToRef<T[K]> {
const val = object[key] // 接受两个参数,一是响应式对象,二是响应式对象中对应的键
// 是不是 ref 对象, 是 -> 返回我们的响应式对象 不是 -> 变成响应式对象
return isRef(val)
? val
: (new ObjectRefImpl(object, key, defaultValue) as any)
}
ObjectRefImpl
类:
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(
private readonly _object: T,
private readonly _key: K,
private readonly _defaultValue?: T[K]
) {}
get value() {
const val = this._object[this._key]
return val === undefined ? (this._defaultValue as T[K]) : val
}
set value(newVal) {
this._object[this._key] = newVal
}
get dep(): Dep | undefined {
return getDepFromReactive(toRaw(this._object), this._key)
}
}
可以看到 toRef
并没有像 ref
、reactive
那样去调用收集依赖、更新依赖的方法,因此它是对非响应式对象没有任何作用(页面不会更新!)
toRefs
export type ToRefs<T = any> = {
[K in keyof T]: ToRef<T[K]>
}
export function toRefs<T extends object>(object: T): ToRefs<T> {
// 判断是不是代理对象
if (__DEV__ && !isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
// 实际上就是创建了一个对象
const ret: any = isArray(object) ? new Array(object.length) : {}
// 利用 for in 来对每一个属性进行 toRef,实现响应式
for (const key in object) {
ret[key] = toRef(object, key)
}
// 最后返回
return ret
}
toRaw
export function toRaw<T>(observed: T): T {
const raw = observed && (observed as Target)[ReactiveFlags.RAW]
return raw ? toRaw(raw) : observed
}
实际上就是在对象中取属性
[ReactiveFlags.RAw]
是个一个枚举类型
export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
IS_SHALLOW = '__v_isShallow',
RAW = '__v_raw'
}
总结
toRef
- 根据一个代理对象的键,返回对应的
ref
- 对非代理对象使用
toRef
,没有任何效果(视图不会更新) - 当函数参数需要
reactive
代理对象中的某个属性时,可以使用toRef
单独传递
toRefs
- 当我们使用
reactive
去创建代理对象时,如果有需要,可以使用toRefs
把代理对象中所有的属性都变成ref
toRaw
- 把代理对象变为普通(原始)对象,失去响应式