toRef
基于响应式对象上的一个属性创建一个对应的ref,这样创建的ref与其源属性保持同步。
改变源属性的值将更新ref的值,反之亦然。
如果对象是非响应式的,则不产生效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <template> <div>{{ person }}</div> <div>toRef: {{ like }}</div> <hr> <div>{{ person2 }}</div> <div>toRef: {{ like2 }}</div> <hr> <div> <button @click="change">修改</button> </div> </template>
<script setup lang="ts"> import { toRef, reactive } from 'vue'
const person = { name: "yajue", age: 24, like: "ramen" } // 非响应式对象经过toRef处理 // 参数1为对象,2为对象中的一个key const like = toRef(person, "like")
const person2 = reactive({ name: "yajue", age: 24, like: "ramen" }) // 响应式对象经过toRef处理 const like2 = toRef(person2, "like")
const change = ()=> { // 如果对象不是响应式,则值改变不会造成视图的改变 // person.like = "rape" // console.log(person)
// toRef作用于非响应式,然并卵,对象本身变化了,但视图不变化 like.value = "rape" console.log(person, like)
// toRef作用于响应式,对象和视图都变化了 like2.value = "rape" console.log(person2, like2)
// 应用场景:只需要响应式对象上的某属性,并且希望它的改变能导致对象和视图改变 } </script>
|
toRefs
根据一个响应式对象创建普通对象,对象的每个属性都是指向原对象属性的ref,主要是方便解构使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <template> <div>{{ person }}</div> <hr> <div>{{ name }} - {{ age }} - {{ like }}</div> <hr> <div> <button @click="change">修改</button> </div> </template>
<script setup lang="ts"> import { toRef, reactive, toRefs } from 'vue'
const person = reactive({ name: "yajue", age: 24, like: "ramen" })
// toRefs可以把对象的每一个属性都变成ref, // const toRefs = <T extends object>(object: T)=> { // const map: any = {} // for(let key in object) { // map[key] = toRef(object, key) // } // return map // }
// reactive一旦被解构,响应式就不存在了(变成普通值) // let {name, age, like} = person // 如果想要从响应式对象上解构响应式值,需要toRefs const {name, age, like} = toRefs(person)
const change = ()=> { console.log(name, age, like) // name = "yjsp" // age = 114 // like = "rape" name.value = "yjsp" age.value = 114 like.value = "rape" } </script>
|
toRaw
返回响应式对象的原始对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div> <button @click="look">查看</button> </div> </template>
<script setup lang="ts"> import { reactive, toRaw } from 'vue'
const person = reactive<any>({ name: "yajue", age: 24, like: "ramen" })
const look = ()=> { // toRaw能把响应式对象变成普通对象 console.log(person, toRaw(person)) // 本质上就是这样(__v_raw是一个隐藏属性) console.log(person, person["__v_raw"]) } </script>
|
源码
在Vue源码(/package/reactivity/src/ref.ts
)中可以看到toRef和toRefs的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| export function toRef<T>( value: T ): T extends () => infer R ? Readonly<Ref<R>> : T extends Ref ? T : Ref<UnwrapRef<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( 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) } }
function propertyToRef(source: object, key: string, defaultValue?: unknown) { const val = (source as any)[key] return isRef(val) ? val : (new ObjectRefImpl( source as Record<string, any>, key, defaultValue ) as any) }
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) } }
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 (const key in object) { ret[key] = propertyToRef(object, key) } return ret }
|
在Vue源码(/package/reactivity/src/reactive.ts
)中可以看到toRaw的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13
| export function toRaw<T>(observed: T): T { const raw = observed && (observed as Target)[ReactiveFlags.RAW] return raw ? toRaw(raw) : observed }
export const enum ReactiveFlags { SKIP = '__v_skip', IS_REACTIVE = '__v_isReactive', IS_READONLY = '__v_isReadonly', IS_SHALLOW = '__v_isShallow', RAW = '__v_raw' }
|