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 }
این مطالب را هم ببینید
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>
هشدار با احتیاط استفاده کنید
هنگام استفاده از customRef، باید در مورد مقدار بازگشتی getter آن محتاط باشیم، به ویژه زمانی که هر بار که getter اجرا میشود، تایپ داده آبجکت جدید تولید میکند. این امر بر رابطه بین کامپوننتهای والد و فرزند تأثیر میگذارد، جایی که چنین customRef به عنوان یک prop منتقل شده است.
تابع render کامپوننت والد ممکن است توسط تغییرات در یک حالت reactive دیگر فعال شود. در طول رندر مجدد، مقدار customRef ما مجدداً ارزیابی میشود و یک تایپ داده آبجکت جدید را به عنوان prop به یک کامپوننت فرزند برمیگرداند. این prop با آخرین مقدار آن در کامپوننت فرزند مقایسه میشود و از آنجایی که متفاوت هستند، وابستگیهای reactive مربوط به customRef در کامپوننت فرزند فعال میشوند. در همین حال، وابستگیهای reactive در کامپوننت والد اجرا نمیشوند زیرا setter مربوط به customRef فراخوانی نشده و در نتیجه وابستگیهای آن فعال نشدهاند.
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 اجرا می شود.
اگر این تابع بدون یک دامنه اثر فعال فراخوانی شود، هشدار داده میشود. در نسخه ۳.۵+، این هشدار را میتوان با ارسال مقدار true
به عنوان آرگومان دوم غیرفعال کرد.
تایپ
tsfunction onScopeDispose(fn: () => void, failSilently?: boolean): void