ref 接收一个内部值并返回响应式且可变的ref对象,ref对象仅有一个.value
属性,指向该内部值。
浏览器控制台设置里,勾选“启动自定义格式化程序”并刷新,控制台就能看格式化后整齐的ref,reactive也是如此。
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 <template> <div>{{person}}</div> <button @click="change">修改</button> <div ref="dom">我是个DOM</div> </template> <script setup lang="ts"> import { ref } from "vue" import type { Ref } from "vue" type P = { name: string } // ref 作深层响应式 // 也可以不写类型定义或泛型,ref会做类型推断 // const person = ref<P>({ name: "yajue" }) // const person: Ref<P> = ref({ name: "yajue" }) const person = ref({ name: "yajue" }) // 不用ref包裹就没有响应式 const person2 = { name: "pinky" } // ref还可以取得DOM元素,变量名需要和元素的ref属性值一致 const dom = ref<HTMLDivElement>() // 在setup语法糖里只能读到undefined,因为在这个阶段,DOM还没有被渲染 console.log(dom.value?.innerText) const change = ()=> { // person.name = "野兽先辈" // ref函数返回一个类,其中有一个属性value,修改或取值时必须带上value person.value.name = "野兽先辈" console.log(person) console.log(dom.value?.innerText) } </script>
isRef 判断是否为ref对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template> <div>{{person}}</div> <button @click="change">修改</button> </template> <script setup lang="ts"> import { ref, isRef } from "vue" const person = ref({ name: "yajue" }) const person2 = { name: "pinky" } const change = ()=> { // 判断对象是不是ref对象 // 实际生产中用得不多,但源码里用得很多 console.log(isRef(person)) // -> true console.log(isRef(person2)) // -> false } </script>
shallowRef 创建一个跟踪.value
变化的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 <template> <div>ref: {{person}}</div> <hr> <div>shallowRef: {{person2}}</div> <hr> <button @click="change">修改</button> </template> <script setup lang="ts"> import { ref, shallowRef } from "vue" type P = { name: string } const person = ref({ name: "yajue" }) // shallowRef 作浅层响应式 const person2 = shallowRef({ name: "MUR" }) const change = ()=> { // 点击后数据改变,但视图不会变化,因为它是浅层响应式 // person2.value.name = "三浦" // 直接从value赋值才会变成响应式 // person2.value = { // name: "三浦" // } // ref和shallowRef不能同时写,因为会影响shallowRef,造成视图的更新 person.value.name = "我是ref" person2.value.name = "我是shallowRef" console.log(person2) } </script>
triggerRef 强制页面视图更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template> <div>ref: {{person}}</div> <hr> <div>shallowRef: {{person2}}</div> <button @click="change">修改</button> <hr> </template> <script setup lang="ts"> import { ref, shallowRef, triggerRef } from "vue" const person = ref({ name: "yajue" }) const person2 = shallowRef({ name: "MUR" }) const change = ()=> { person2.value.name = "我被影响了" // triggerRef强制更新收集的依赖,直接调用它也能更新shallowRef // ref底层更新视图的逻辑中会调用triggerRef函数,所以ref和shallowRef不能同时写在一起 triggerRef(person2) console.log(person2) } </script>
customRef 自定义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 <template> <div>{{obj}}</div> <button @click="change">修改</button> </template> <script setup lang="ts"> import { ref, shallowRef, triggerRef, customRef } from "vue" // 自定义ref,要求返回一个customRef的返回值 function myRef<T>(value: T) { // customRef接收一个回调函数,回调函数接收两个参数 return customRef((track, trigger)=> { // 可以使用自定义ref实现额外的逻辑 let timer // 回调函数要求返回一个对象,对象需要实现get和set方法 return { get() { // track用来收集依赖 track() return value }, set(newVal) { //比如用户输入值时,调用接口并做个防抖 clearTimeout(timer) setTimeout(()=> { console.log("我调用了一个接口") value = newVal timer = null // trigger用来触发依赖更新 trigger() }, 500) } } }) } const obj = myRef<string>("cheriko") const change = ()=> { obj.value = "meruko" console.log(obj) } </script>
源码 在Vue源码(/package/reactivity/src/ref.ts
)中可以看到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 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 export function ref<T extends Ref >(value : T): Texport function ref<T>(value : T): Ref <UnwrapRef <T>>export function ref<T = any >(): Ref <T | undefined >export function ref (value?: unknown ) { return createRef (value, false ) } export function shallowRef<T extends object >( value : T ): T extends Ref ? T : ShallowRef <T> export function shallowRef<T>(value : T): ShallowRef <T>export function shallowRef<T = any >(): ShallowRef <T | undefined >export function shallowRef (value?: unknown ) { return createRef (value, true ) } 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 )) { this ._rawValue = newVal this ._value = useDirectValue ? newVal : toReactive (newVal) triggerRefValue (this , newVal) } } } export function triggerRef (ref: Ref ) { triggerRefValue (ref, __DEV__ ? ref.value : void 0 ) }
这里属于reactive的源码,位于/package/reactivity/src/reactive.ts
。
1 2 3 4 5 6 7 8 9 10 export const toReactive = <T extends unknown >(value : T): T => isObject (value) ? reactive (value) : value