Reactivity API: مفاهیم پیشرفته
shallowRef
نسخه سطحی یا Shallow از ref
.
تایپ
tsfunction shallowRef<T>(value: T): ShallowRef<T> interface ShallowRef<T> { value: T }
جزئیات
برخلاف ref
، مقدار درونی یک shallowRef
همانطور که هست ذخیره و بازیابی می شود و عمیقا reactive نمیشود. فقط دسترسی مقدار «value» از آن reactive خواهد بود.
shallowRef
معمولاً برای بهینهسازی عملکرد داده ساختار های بزرگ و یا ادغام با سیستمهای مدیریت state خارجی، استفاده میشود.
مثال
jsconst state = shallowRef({ count: 1 }) // باعث بروز تغییر نمی شود state.value.count = 2 // باعث بروز تغییر می شود state.value = { count: 2 }
تنها زمانی بروز تغییر اتفاق می افتد که reference مقدار
value
تغییر کرده باشد.این مطالب را هم ببینید
triggerRef
این تابع Effect هایی را که به یک shallow ref وابسه هستند، به صورت اجباری، اجرا می کند. از این تابع معمولا زمانی استفاده می شود که بر روی value داخلی یک shallowRef
تغییرات عمیق (تغییراتی که reference را تغییر نمی دهند) انجام شده باشد.
تایپ
tsfunction triggerRef(ref: ShallowRef): void
مثال
jsconst shallow = shallowRef({ greet: 'Hello, world' }) // Logs "Hello, world" once for the first run-through watchEffect(() => { console.log(shallow.value.greet) }) // This won't trigger the effect because the ref is shallow shallow.value.greet = 'Hello, universe' // Logs "Hello, universe" triggerRef(shallow)
customRef
این تابع، یک ref سفارشی با امکان کنترل صریح ردیابی وابستگی (Dependency-Tracking) و زمان بروزرسانی، ایجاد میکند.
تایپ
tsfunction customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }
جزئیات
customRef
انتظار یک تابع Factory دارد. این تابع Factory، توابع track
و trigger
را به عنوان آرگومان دریافت کرده و باید یک شی را با متدهای get
و set
برگرداند.
به طور کلی، track
باید در داخل get
، و trigger
باید در داخل set
فراخوانی شود. با این حال، کنترل اینکه چه زمانی باید آنها را فراخوانی کرد یا اینکه آیا اصلاً نیازی به فراخوانی آنها است یا خیر، با شماست.
- مثال
ایجاد یک ref تاخیر خورده (Debounced) که فقط پس از یک بازه زمانی مشخص از آخرین فراخوانی set
، مقدار را بهروزرسانی میکند:
js
import { customRef } from 'vue'
export function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
استفاده از آن در کامپوننت:
vue
<script setup>
import { useDebouncedRef } from './debouncedRef'
const text = useDebouncedRef('hello')
</script>
<template>
<input v-model="text" />
</template>
shallowReactive
نسخه سطحی یا Shallow از reactive
.
تایپ
tsfunction shallowReactive<T extends object>(target: T): T
جزئیات
برخلاف reactive
، فقط ویژگی های سطح اول یا ریشه، به شکل reactive هستند. مقادیر ویژگیها همانطور که هست ذخیره و بازیابی می شود - این همچنین به این معنی است که ویژگیهای تعریف شده با ref
بهطور خودکار Unwrap نمیشوند.
با احتیاط استفاده کنید
داده ساختار های Shallow، صرفا باید برای State های سطح اول یا ریشه استفاده شوند. از Nest کردن آن در یک شی که به شکل عمیق Reactive است (همانند ref)، پرهیز کنید، چراکه با این کار، درخت Dependency Tracking نا پایدار شده و باعث ایجاد مشکل در فهم و یا Debug کد می شود.
مثال
jsconst state = shallowReactive({ foo: 1, nested: { bar: 2 } }) // mutating state's own properties is reactive state.foo++ // ...but does not convert nested objects isReactive(state.nested) // false // NOT reactive state.nested.bar++
shallowReadonly
نسخه سطحی یا Shallow از readonly
.
تایپ
tsfunction shallowReadonly<T extends object>(target: T): Readonly<T>
جزئیات
برخلاف readonly
، فقط ویژگی های سطح اول یا ریشه، به شکل readonly هستند. مقادیر ویژگیها همانطور که هست ذخیره و بازیابی می شود - این همچنین به این معنی است که ویژگیهای تعریف شده با ref
بهطور خودکار Unwrap نمیشوند.
با احتیاط استفاده کنید
داده ساختار های Shallow، صرفا باید برای State های سطح اول یا ریشه استفاده شوند. از Nest کردن آن در یک شی که به شکل عمیق Reactive است (همانند ref)، پرهیز کنید، چراکه با این کار، درخت Dependency Tracking نا پایدار شده و باعث ایجاد مشکل در فهم و یا Debug کد می شود.
مثال
jsconst state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // mutating state's own properties will fail state.foo++ // ...but works on nested objects isReadonly(state.nested) // false // works state.nested.bar++
toRaw
شیء خام و اصلی یک پروکسی ایجاد شده از Vue را برمی گرداند.
تایپ
tsfunction toRaw<T>(proxy: T): T
جزئیات
toRaw
می تواند شی اصلی که به کمک یکی از توابع reactive
, readonly
, shallowReactive
و یا shallowReadonly
ایجاد شده است، برگرداند.
این یک ترفند است که میتواند برای خواندن موقت بدون نیاز به دسترسی به Proxy / سربار Dependency Tracking و یا نوشتن بدون ایجاد Reactive Effect استفاده شود. اینکه یک ارجاع مدارم به خروجی این تابع داشته باشید، توصیه نمی شود. با احتیاط استفاده کنید.
مثال
jsconst foo = {} const reactiveFoo = reactive(foo) console.log(toRaw(reactiveFoo) === foo) // true
markRaw
یک شی را طوری علامت گذاری می کند که هرگز به Proxy تبدیل نشود و به عنوان خروجی، خود شی را برمی گرداند.
تایپ
tsfunction markRaw<T extends object>(value: T): T
مثال
jsconst foo = markRaw({}) console.log(isReactive(reactive(foo))) // false // also works when nested inside other reactive objects const bar = reactive({ foo }) console.log(isReactive(bar.foo)) // false
با احتیاط استفاده کنید
markRaw و APIهای سطحی مانند shallowReactive به شما این امکان را می دهند که به طور انتخابی یا Opt-in از تبدیل عمیق reactive/readonly، که به صورت پیش فرض انجام می شود، خودداری کنید و اشیاء Raw و غیر Proxy شده را در گراف State خود قرار کنید. دلیل استفاده از این API ها می تواند موارد زیر باشد:
برخی مقادیر، نیازی به Reactive شدن ندارند، برای مثال، یک شی پیچیده از کلاس کتابخانه Third-Party و یا یک شی کامپوننت Vue.
Skip کردن تبدیل Proxy، میتواند هنگام رندر فهرستهای بزرگ با منابع داده Immutable و یا غیر قابل تغییر، عملکرد را بهبود بخشد.
این API ها پیشرفته در نظر گرفته می شوند، چراکه Skip کردن Reactivity صرفا در سطح ریشه است، بنابراین اگر یک شی تودرتو که با markRaw علامت گذاری نشده را در یک شی Reactive تنظیم کنید و سپس دوباره به آن دسترسی پیدا کنید، نسخه Proxy شده آن را دریافت می کنید. این می تواند منجر به خطرات هویتی شود - یعنی انجام عملیاتی که بر هویت شی متکی است، اما از هر دو نسخه Raw و Proxy یک شی استفاده می کند:
jsconst foo = markRaw({ nested: {} }) const bar = reactive({ // although `foo` is marked as raw, foo.nested is not. nested: foo.nested }) console.log(foo.nested === bar.nested) // false
خطرات هویتی به طور کلی نادر هستند. با این حال، برای استفاده صحیح از این APIها و همچنین اجتناب از خطرات هویتی، نیاز به درک کاملی از نحوه عملکرد سیستم واکنش پذیری وجود دارد.
effectScope
یک شی Effect Scope ایجاد می کند که می تواند Effect های Reactivity (همانند computed
و یا watch
) ایجاد شده در خود را نگه دارد تا این Effect ها با هم حذف شوند. برای موارد استفاده دقیق از این API، لطفاً به RFC مربوطه آن مراجعه کنید .
تایپ
tsfunction effectScope(detached?: boolean): EffectScope interface EffectScope { run<T>(fn: () => T): T | undefined // undefined if scope is inactive stop(): void }
مثال
jsconst scope = effectScope() scope.run(() => { const doubled = computed(() => counter.value * 2) watch(doubled, () => console.log(doubled.value)) watchEffect(() => console.log('Count: ', doubled.value)) }) // to dispose all effects in the scope scope.stop()
getCurrentScope
effect scope فعال را (در صورتی که اصلا وجود داشته باشد) بر می گرداند.
تایپ
tsfunction getCurrentScope(): EffectScope | undefined
onScopeDispose
ثبت یک Callback بر روی رویداد پاک شدن effect scope فعال. این Callback زمانی فراخوانی می شود که Effect Scope متوقف شده باشد.
این تابع می تواند به عنوان جایگزین onUnmounted
، در توابع Composition استفاده شود با این تفاوت که وابستگی به خود کامپوننت نخواهد داشت. عدم این وابستی بدین خاطر است که تابع setup
از کامپوننت Vue، خود در یک Effect Scope اجرا می شود.
تایپ
tsfunction onScopeDispose(fn: () => void): void