Reactivity API: هسته
همچنین ببینید
برای درک بهتر API های Reactivity، توصیه میشود فصلهای زیر در راهنما را بخوانید:
- مبانی Reactivity (با اولویت APIای که روی Composition API تنظیم شده است)
- Reactivity به صورت عمیق
ref()
یک مقدار داخلی میگیرد و یک ref Object قابل تغییر (mutable) و reactive برمیگرداند که یک پراپرتی تکی به شکل .value
دارد که به همان مقدار داخلی اشاره میکند.
تایپ (Type)
tsfunction ref<T>(value: T): Ref<UnwrapRef<T>> interface Ref<T> { value: T }
جزئیات
ref object گفته شده قابل تغییر است - یعنی شما میتوانید به
.value
مقدار جدید نسبت دهید. همچنین reactive است یعنی هر عملیات خواندن که مربوط به.value
است رهگیری (tracked) میشود.اگر یک آبجکت به عنوان یک مقدار از ref در نظر گرفته شود، آن آبجکت با reactive() عمیقا reactive میشود. این جمله همچنین به این معنی است که اگر آن آبجکت ref های تو در تو داشته باشد آنها عمیقا از پوشش خارج میشوند (unwrapped).
برای جلوگیری از تبدیل عمیق (deep conversion) میتوانید به جای ref از
shallowRef()
استفاده کنید.مثال
jsconst count = ref(0) console.log(count.value) // 0 count.value = 1 console.log(count.value) // 1
همچنین ببینید
computed()
یک getter function میگیرد و به ازای مقدار بازگشت داده شده از getter یک آبجکت ref که readonly و reactive است را برمیگرداند. همچنین میتواند یک آبجکت با تابعهای get
و set
برای ایجاد یک آبجکت ref که writable است را بگیرد.
تایپ (Type)
ts// read-only function computed<T>( getter: (oldValue: T | undefined) => T, // see "Computed Debugging" link below debuggerOptions?: DebuggerOptions ): Readonly<Ref<Readonly<T>>> // writable function computed<T>( options: { get: (oldValue: T | undefined) => T set: (value: T) => void }, debuggerOptions?: DebuggerOptions ): Ref<T>
مثال
ایجاد کردن یک computed ref که readonly است:
jsconst count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // error
ایجاد کردن یک computed ref که writable است:
jsconst count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0
دیباگ کردن:
jsconst plusOne = computed(() => count.value + 1, { onTrack(e) { debugger }, onTrigger(e) { debugger } })
همچنین ببینید
reactive()
یک پروکسی reactive از آبجکت را برمیگرداند.
تایپ (Type)
tsfunction reactive<T extends object>(target: T): UnwrapNestedRefs<T>
جزئیات
تبدیل reactive عمیق است: روی تمام پراپرتیهای تو در تو اثر میگذارد. همچنین یک آبجکت reactive در عین حالی که reactivity حفظ میکند به شکل عمیق هر پراپرتیای که refs است را unwrapped میکند.
همچنین لازم به ذکر است که وقتی ref به عنوان یک المنت از یک آرایهی reactive یا یک تایپ مجموعهی بومی (native collection) مانند
Map
قابل دسترسی است هیچ ref unwrapping صورت نمیگیرد.برای جلوگیری از تبدیل عمیق و برای نگهداشتن reactivity تنها در سطح root به جای reactive از shallowReactive() استفاده کنید.
آبجکت بازگشت داده شده و آبجکتهای تو در توی آن بهوسیلهی ES Proxy دربرگرفته میشوند (wrapped) و با آبجکتهای اصلی یکی نیستند. توصیه میشود که منحصرا با reactive proxy کار کنید و از اعتماد کردن به آبجکت اصلی خودداری کنید.
مثال
ایجاد کردن یک آبجکت reactive:
jsconst obj = reactive({ count: 0 }) obj.count++
unwrap کردنِ ref:
tsconst count = ref(1) const obj = reactive({ count }) // ref will be unwrapped console.log(obj.count === count.value) // true // it will update `obj.count` count.value++ console.log(count.value) // 2 console.log(obj.count) // 2 // it will also update `count` ref obj.count++ console.log(obj.count) // 3 console.log(count.value) // 3
توجه داشته باشید که refها زمانی که به عنوان آرایه یا المنتهای یک collection در دسترساند unwrapped نیستند:
jsconst books = reactive([ref('Vue 3 Guide')]) // need .value here console.log(books[0].value) const map = reactive(new Map([['count', ref(0)]])) // need .value here console.log(map.get('count').value)
هنگامی که یک ref را به یک پراپرتی
reactive
اختصاص میدهیم (assigning), آن ref به صورت خودکار unwrapp میشود:tsconst count = ref(1) const obj = reactive({}) obj.count = count console.log(obj.count) // 1 console.log(obj.count === count.value) // true
همچنین ببینید
readonly()
یک آبجکت (reactive یا معمولی) یا یک ref را میگیرد و یک پروکسی readonly را به نسخه اصلی برمیگرداند.
تایپ (Type)
tsfunction readonly<T extends object>( target: T ): DeepReadonly<UnwrapNestedRefs<T>>
جزئیات
یک پروکسی readonly عمیق است: هر پراپرتی تو در تویی که دسترسی داشته باشید نیز readonly خواهد بود. علاوه بر این readonly رفتاری مشابه
reactive()
در ref-unwrapping دارد، به استثنای مقادیر unwrapp شده که همچنان readonly ساخته خواهند شد.جهت اجتناب از تبدیل عمیق، به جای readonly از shallowReadonly() استفاده کنید.
مثال
jsconst original = reactive({ count: 0 }) const copy = readonly(original) watchEffect(() => { // works for reactivity tracking console.log(copy.count) }) // mutating original will trigger watchers relying on the copy original.count++ // mutating the copy will fail and result in a warning copy.count++ // warning!
watchEffect()
یک تابع را در حالی که وابستگیهای آن را به صورت reactive رهگیری (tracking) میکند، بلافاصله اجرا میکند و هر زمان که وابستگیها (dependencies) تغییر کردند، دوباره آن را اجرا میکند.
تایپ (Type)
tsfunction watchEffect( effect: (onCleanup: OnCleanup) => void, options?: WatchEffectOptions ): WatchHandle type OnCleanup = (cleanupFn: () => void) => void interface WatchEffectOptions { flush?: 'pre' | 'post' | 'sync' // default: 'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void } interface WatchHandle { (): void // callable, same as `stop` pause: () => void resume: () => void stop: () => void }
جزئیات
اولین آرگومان، تابع effect است که باید اجرا شود. تابع effect یک تابع دریافت میکند که میتواند برای ثبت یک cleanup callback استفاده شود. این cleanup callback درست قبل از دفعهی بعدی که effect قرار است دوباره اجرا شود، صدا زده میشود، و میتواند برای پاکسازی (cleanup) عوارض جانبی غیر معتبر (invalidated side effects) استفاده شود؛ به عنوان مثال یک ریکوئست async در حال انتظار (pending async request) (مثال زیر را ببینید).
دومین آرگومان، یک options object اختیاری است که میتواند برای تنظیمکردن عوارض زمانبندی اجرا (flush timing) یا دیباگ کردنِ effect's dependencies استفاده شود.
به صورت پیشفرض، ناظران درست قبل از رندر شدن کامپوننت اجرا میشوند. قرار دادن
flush: 'post'
ناظر را تا بعد از رندر شدن کامپوننت به تعویق میاندازد. برای اطلاعات بیشتر زمانبندی اجرای callback را ببینید. در موارد نادری، ممکن است بهکار انداختن (trigger) یک ناظر بلافاصله وقتی که reactive dependency تغییر میکند الزامی باشد، مثلا برای باطلکردن یک کش. این امکان با استفاده ازflush: 'sync'
میسر است. هر چند که این تنظیم باید با احتیاط استفاده شود، چرا که میتواند باعث مشکلاتی در پرفورمنس و data consistency [اگر چندین پراپرتی همزمان آپدیت شوند] شود.مقداری که برگشت داده میشود یک تابع هندل کننده است که میتواند برای متوقف کردن effect از اجرای دوباره صدا زده شود.
مثال
jsconst count = ref(0) watchEffect(() => console.log(count.value)) // -> logs 0 count.value++ // -> logs 1
متوقف کردن ناظر:
jsconst stop = watchEffect(() => {}) // when the watcher is no longer needed: stop()
مکث / از سرگیری ناظر:
jsconst { stop, pause, resume } = watchEffect(() => {}) // temporarily pause the watcher pause() // resume later resume() // stop stop()
پاکسازی اثرات جانبی:
jswatchEffect(async (onCleanup) => { const { response, cancel } = doAsyncWork(newId) // `cancel` will be called if `id` changes, cancelling // the previous request if it hasn't completed yet onCleanup(cancel) data.value = await response })
پاکسازی اثرات جانبی در 3.5+:
jsimport { onWatcherCleanup } from 'vue' watchEffect(async () => { const { response, cancel } = doAsyncWork(newId) // `cancel` will be called if `id` changes, cancelling // the previous request if it hasn't completed yet onWatcherCleanup(cancel) data.value = await response })
آپشنها:
jswatchEffect(() => {}, { flush: 'post', onTrack(e) { debugger }, onTrigger(e) { debugger } })
همچنین ببینید
watchPostEffect()
نام مستعاری از watchEffect()
با آپشن flush: 'post'
.
watchSyncEffect()
نام مستعاری از watchEffect()
با آپشن flush: 'sync'
.
watch()
یک یا چند دادهی reactive را نظارت میکند و هر زمان که منابع (sources) تغییر کردند یک callback function را اجرا میکند.
تایپ (Type)
ts// watching single source function watch<T>( source: WatchSource<T>, callback: WatchCallback<T>, options?: WatchOptions ): WatchHandle // watching multiple sources function watch<T>( sources: WatchSource<T>[], callback: WatchCallback<T[]>, options?: WatchOptions ): WatchHandle type WatchCallback<T> = ( value: T, oldValue: T, onCleanup: (cleanupFn: () => void) => void ) => void type WatchSource<T> = | Ref<T> // ref | (() => T) // getter | (T extends object ? T : never) // reactive object interface WatchOptions extends WatchEffectOptions { immediate?: boolean // default: false deep?: boolean | number // default: false flush?: 'pre' | 'post' | 'sync' // default: 'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void once?: boolean // default: false (3.4+) } interface WatchHandle { (): void // callable, same as `stop` pause: () => void resume: () => void stop: () => void }
تایپها به جهت خوانایی سادهسازی شدهاند.
جزئیات
watch()
به صورت پیشفرض تنبل (lazy) است به این معنی که callback تنها زمانی که منبع مورد نظارت تغییر کرده باشد فراخوانی میشود.اولین آرگومان، منبعِ watcher است. منبع (source) میتواند یکی از موارد زیر باشد:
- یک تابع getter که یک مقدار را برمیگرداند
- یک ref
- یک آبجکت reactive
- ... یا آرایهای از موارد بالا.
دومین آرگومان callback است که با تغییر منبع فراخوانی میشود. این callback سه آرگومان میگیرد: مقدار جدید، مقدار قدیمی، و یک تابع برای ثبت یک کالبک پاکسازِ عوارض جانبی (side effect cleanup callback). این cleanup callback درست پیش از آنکه تاثیرات دوباره اجرا شود فراخوانی میگردد و میتواند برای پاکسازیِ عوارض جانبی غیرمعتبر (invalidated side effects) استفاده شود به عنوان مثال یک ریکوئست async در حال انتظار (pending async request).
زمانی که چندین منبع نظارت میشوند، این callback دو آرایه که شامل مقادیر جدید / قدیم مربوط به آرایه منبع میباشند را دریافت میکند.
سومین آرگومان اختیاری یک options object است که گزینههای زیر را پشتیبانی میکند:
immediate
: کالبک (callback) را درست در زمان ایجاد ناظر بهکار میاندازد. در اولین فراخوانی مقدار قدیمیundefined
خواهد بود.deep
: اگر منبع، یک آبجکت باشد باالاجبار منبع را به شکل عمیق پیمایش میکند تا callback در تغییرات عمیق منبع نیز اجرا شود. در 3.5+، همچنین می تواند عددی باشد که حداکثر عمق پیمایش را نشان می دهد. ناظران عمیق را ببینید.flush
: زمانبندی اجرای callback را تنظیم میکند. ببینید زمانبندی اجرای Callback وwatchEffect()
.onTrack / onTrigger
: وابستگیهای ناظر را دیباگ میکند. ببینید دیباگ کردن ناظر.once
: (+3.4) تنها یک بار کالبک را اجرا کند. ناظر پس از اولین اجرای کالبک به طور خودکار متوقف میشود.
watch()
در مقایسه باwatchEffect()
به ما اجازه میدهد که:- به شکل تنبلانه (lazily) با عارضهجانبی (side effect) برخورد کنیم؛
- در مورد اینکه کدام state باید ناظر را برای اجرای مجدد به کار بیاندازد جزئیتر باشیم؛
- به هر دو مقدار فعلی و قبلی از state نظارت شده دسترسی داشته باشیم.
مثال
نظارت کردن یک getter:
jsconst state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } )
نظارت کردن یک ref:
jsconst count = ref(0) watch(count, (count, prevCount) => { /* ... */ })
هنگامی که چندین منبع را نظارت میکنیم، callback آرایههایی از مقادیر جدید / قدیم که مرتبط با آرایه منبع هستند را دریافت میکند:
jswatch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })
زمانی که از یک منبع getter استفاده میکنیم، ناظر تنها زمانی فعال میشود که مقدار برگشت داده شده از getter تغییر کرده باشد . اگر میخواهید که callback حتی زمانی که تغییرات عمیق صورت میگیرد نیز فعال شود باید صراحتا با حالت عمیق
{ deep: true }
ناظر را مجبور به این کار کنید. لازم به ذکر است که در حالت عمیق، اگر callback با یک تغییر عمیق (deep mutation) به کار افتاده باشد (triggered) مقدار جدید و قدیمی آبجکتهایی مشابه خواهند بود:jsconst state = reactive({ count: 0 }) watch( () => state, (newValue, oldValue) => { // newValue === oldValue }, { deep: true } )
زمانی که مستقیما یک آبجکت reactive را نظارت میکنیم، نظارت به شکل خودبخود در حالت عمیق میباشد:
jsconst state = reactive({ count: 0 }) watch(state, () => { /* triggers on deep mutation to state */ })
watch()
همان گزینههای دیباگ کردن و زمانبندی اجرا را باwatchEffect()
به اشتراک میگذارد:jswatch(source, callback, { flush: 'post', onTrack(e) { debugger }, onTrigger(e) { debugger } })
متوقف کردن ناظر:
jsconst stop = watch(source, callback) // when the watcher is no longer needed: stop()
مکث / از سرگیری ناظر:
jsconst { stop, pause, resume } = watch(() => {}) // temporarily pause the watcher pause() // resume later resume() // stop stop()
پاکسازِ عوارض جانبی (Side effect cleanup):
jswatch(id, async (newId, oldId, onCleanup) => { const { response, cancel } = doAsyncWork(newId) // `cancel` will be called if `id` changes, cancelling // the previous request if it hasn't completed yet onCleanup(cancel) data.value = await response })
پاکسازی اثرات جانبی در 3.5+:
jsimport { onWatcherCleanup } from 'vue' watch(id, async (newId) => { const { response, cancel } = doAsyncWork(newId) onWatcherCleanup(cancel) data.value = await response })
همچنین ببینید
onWatcherCleanup()
یک تابع پاکسازی (cleanup function) را ثبت کنید که زمانی که watcher فعلی در آستانه اجرای مجدد است، اجرا شود. این تابع فقط در حین اجرای همزمان یک watchEffect
یا تابع بازگشتی (callback) برای watch قابل فراخوانی است (یعنی نمیتوان آن را بعد از یک عبارت await
در یک تابع غیرهمزمان فراخوانی کرد).
تایپ
tsfunction onWatcherCleanup( cleanupFn: () => void, failSilently?: boolean ): void
مثال
tsimport { watch, onWatcherCleanup } from 'vue' watch(id, (newId) => { const { response, cancel } = doAsyncWork(newId) // `cancel` will be called if `id` changes, cancelling // the previous request if it hasn't completed yet onWatcherCleanup(cancel) })