Provide / Inject
این صفحه فرض میکند شما قبلاً مبانی کامپوننتها را خواندهاید. اگر با کامپوننتها آشنایی ندارید، ابتدا آن را بخوانید.
انتقال پراپ به اعماق درخت کامپوننتها | Prop Drilling
معمولاً وقتی میخواهیم دادهای را از والد به فرزند منتقل کنیم، از props استفاده میکنیم. اما تصور کنید موردی داشته باشیم که کامپوننتهای تو در توی زیادی داریم و یک کامپوننت در عمق درخت نیاز به دادهای از یک کامپوننت اجدادی دوردست دارد. در این حالت، با استفاده از props باید همان پراپ را در تمام زنجیره والدین انتقال دهیم:
ملاحظه میکنید اگرچه کامپوننت <Footer>
شاید نیازی به این پراپها ندارد، ولی باز هم باید آنها را تعریف و منتقل کند تا <DeepChild>
بتواند به آنها دسترسی داشته باشد. اگر زنجیره والدینها طولانیتر باشد، کامپوننتهای بیشتری در مسیر تاثیر میگیرند. این موضوع را "props drilling" (انتقال پراپ به اعماق درخت کامپوننتها) مینامند و حتماً کار لذتبخشی نیست.
میتوانیم با استفاده از provide
و inject
این مشکل را حل کنیم. کامپوننت والد میتواند به عنوان یک ارائهدهنده dependencyها برای تمام فرزندانش عمل کند. هر کامپوننتی در درخت فرزندان، صرفنظر از عمق آن، میتواند dependencyهای ارائه شده توسط کامپوننتهای بالاتر در زنجیره والدین را وارد (inject) کند.
Provide | ارائه
برای ارائه دادن داده به فرزندان یک کامپوننت، از تابع provide()
استفاده کنید:
vue
<script setup>
import { provide } from 'vue'
provide(/* key */ 'message', /* value */ 'hello!')
</script>
اگر از <script setup>
استفاده نمیکنید، اطمینان حاصل کنید که provide()
به صورت همگام (synchronous) درون setup()
صدا زده میشود:
js
import { provide } from 'vue'
export default {
setup() {
provide(/* key */ 'message', /* value */ 'hello!')
}
}
تابع provide()
دو آرگومان دریافت میکند. آرگومان اول injection key نام دارد که میتواند یک رشته یا یک Symbol
باشد. injection key توسط کامپوننتهای فرزند برای پیدا کردن value مورد نظر استفاده میشود. یک کامپوننت میتواند provide()
را چندین بار با injection key های متفاوت صدا بزند تا مقادیر مختلفی را ارائه دهد.
آرگومان دوم value ارائه شده است. این value میتواند از هر نوعی باشد، از جمله stateهای reactive مثل refs:
js
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
ارائه مقادیر reactive به کامپوننتهای فرزند این امکان را میدهد تا ارتباط reactive با کامپوننت ارائهدهنده برقرار کنند.
ارائه در سطح اپلیکیشن | App-level Provide
علاوه بر ارائه داده در یک کامپوننت، میتوانیم در سطح اپلیکیشن هم ارائه کنیم:
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* key */ 'message', /* value */ 'hello!')
ارائههای سطح اپلیکیشن برای تمام کامپوننتهای رندر شده در اپلیکیشن در دسترس هستند. این مورد به خصوص زمانی مفید است که پلاگینها را مینویسیم، چرا که پلاگینها معمولاً قادر نیستند از طریق کامپوننتها مقادیری را ارائه کنند.
Inject | تزریق
برای تزریق دادههای ارائه شده توسط یک کامپوننت اجدادی، از تابع inject()
استفاده کنید:
vue
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
اگر مقدار ارائه شده یک ref باشد، بدون تغییر تزریق میشود و به طور خودکار unwrap نمیشود. این امکان اتصال reactive به کامپوننت ارائهدهنده را برای کامپوننت تزریقکننده حفظ میکند. به عبارت دیگر، مقدار به صورت "as-is" و بدون تغییر تزریق میشود.
نمونه کامل از provide و inject با reactive
دوباره تاکید میکنیم اگر از <script setup>
استفاده نمیکنید، inject()
باید فقط به صورت همگام درون setup()
صدا زده شود:
js
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}
Injection Default Values
به طور پیشفرض، inject
فرض میکند injection key در جایی در زنجیره والدین ارائه شده است. در صورتی که کلید ارائه نشده باشد، یک هشدار در زمان اجرا نمایش داده میشود.
اگر میخواهیم یک پراپرتی تزریق شده با optional provider ها کار کند، نیاز است مقدار پیشفرضی را مشابه props تعریف کنیم:
js
// خواهد بود "default value" مقدار `value`
// ارائه نشده باشد "message" اگر هیچ دادهای با کلید
const value = inject('message', 'default value')
در برخی موارد، مقدار پیشفرض نیاز به صدا زدن تابع یا مقداردهی اولیه یک کلاس جدید دارد. برای جلوگیری از محاسبات یا عوارض جانبی غیرضروری در صورتی که مقدار اختیاری استفاده نشود، میتوانیم از factory function برای ساخت مقدار پیشفرض استفاده کنیم:
js
const value = inject('key', () => new ExpensiveClass(), true)
پارامتر سوم نشان میدهد مقدار پیشفرض باید به عنوان factory function در نظر گرفته شود.
کار با Reactive
هنگام استفاده از مقادیر reactive برای provide / inject ، توصیه میشود هر جا امکان دارد تغییرات reactive state را درون provider نگه دارید. این اطمینان میدهد که state ارائه شده و تغییرات احتمالی آن در یک کامپوننت قرار دارند تا نگهداری آنها در آینده آسانتر باشد.
ممکن است مواردی باشد که نیاز داشته باشیم داده را از یک کامپوننت تزریقکننده بهروزرسانی کنیم. در چنین مواردی، توصیه میکنیم تابعی را ارائه کنیم که مسئول تغییر دادن state باشد:
vue
<!-- inside provider component -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
vue
<!-- in injector component -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
در نهایت، میتوانید مقدار ارائه شده را داخل readonly()
قرار دهید تا اطمینان حاصل کنید دادههای انتقال داده شده از طریق provide
توسط کامپوننت تزریقکننده قابل تغییر نیستند.
vue
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>
کار با کلیدهای Symbol
تا اینجا، در مثالها از کلیدهای تزریق رشتهای استفاده کردهایم. اگر در یک برنامه بزرگ با بسیاری ارائهدهندههای وابستگی کار میکنید، یا قصد دارید کامپوننتهایی بنویسید که توسط سایر توسعهدهندگان استفاده خواهند شد، بهتر است از کلیدهای تزریق Symbol برای جلوگیری از برخورد احتمالی استفاده کنید.
توصیه میشود Symbolsها را در یک فایل جداگانه export کنید:
js
// keys.js
export const myInjectionKey = Symbol()
js
// in provider component
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, {
/* data to provide */
})
js
// in injector component
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
See also: Typing Provide / Inject