Vue.js: provide / inject

آخر تحديث: 2026-08-26

provide وinject هما واجهات برمجة التطبيقات (APIs) للتواصل عبر المستويات في Vue 3 — حيث توفر المكونات السلفية البيانات، ويمكن لأي مكون تابع (مهما كان عمق التسلسل) إدخالها واستخدامها. وهذا يحل مشكلة «prop drilling».

يدعم provide في Vue 3 البيانات التفاعلية (انظر / reactive)، وستصبح المكونات الفرعية تفاعلية تلقائيًا بعد إدخالها. سيساعدك هذا الدرس على إتقان هذه الآلية وخمس حالات استخدام عملية.

1. ما ستتعلمه



2. كابوس حفر الدعامة بلون «اللون المميز» عبر 5 مستويات

(1) نقطة الضعف: التطبيق → التخطيط → العنوان → قائمة المستخدم → الزر، تمرير عبر 5 طبقات

كان من الضروري أن تدعم لوحة التحكم الإدارية الخاصة بـ«أليس» إمكانية تبديل القوالب:

VUE
<!-- ❌ The "Broken" Version: 5-Layer prop drilling -->
<!-- App.vue -->
<Layout :theme="theme" :user="user" :locale="locale">
  <router-view />
</Layout>

<!-- Layout.vue -->
<Header :theme="theme" :user="user" :locale="locale" />

<!-- Header.vue -->
<UserMenu :theme="theme" :user="user" :locale="locale" />

<!-- UserMenu.vue -->
<ThemeButton :theme="theme" />
<LocaleSelector :locale="locale" />

يضيف مدير المنتج تشارلي 3 إعدادات أخرى:

«أليس، نحتاج إلى إضافة إعدادات لتطيي الشريط الجانبي، وإعدادات الإشعارات، وعلامات الميزات. وهذا يعني 8 خصائص يجب تمريرها عبر 5 مكونات. هذا الأمر يصعب صيانته.»

(2) حل «توفير/إدخال» في Vue: يتم الإدخال في العناصر السلفية؛ وتتلقى العناصر التابعة ذلك

VUE
<!-- App.vue Ancestors -->
<script setup>
import { ref, provide } from 'vue'

const theme = ref('light')
const user = ref({ name: 'Alice' })
const locale = ref('zh-CN')

// ✅ Once provide,All descendants may inject
provide('theme', theme)
provide('user', user)
provide('locale', locale)
</script>

<!-- UserMenu.vue Any descendant at any depth -->
<script setup>
import { inject } from 'vue'

// ✅ Get it directly, No props passthrough needed
const theme = inject('theme')
const user = inject('user')
const locale = inject('locale')
</script>

<template>
  <button :classe="theme">Toggle {{ user.name }}</button>
  <p>{{ locale }}</p>
</template>

5 مستويات من التداخلسطر واحد من provide + سطر واحد من inject.

(3) الإيرادات

بعد التزويد / الحقن:



3. القواعد الأساسية لـ provide و inject

(1) إدخال البيانات

VUE
<!-- App.vue Ancestor Components -->
<script setup>
import { ref, provide } from 'vue'

const theme = ref('light')
const user = { name: 'Alice' }

// 1. Inject a static value
provide('appName', 'My Admin')

// 2. Injecting Responsive Data(ref)
provide('theme', theme)

// 3. Injecting Responsive Data(reactive)
provide('user', user)

// 4. Injection Methods
provide('updateUser', (newUser) => {
  user.value = newUser
})
</script>

(2) يقوم «inject» باستقبال البيانات

VUE
<!-- Child.vue Any descendant component -->
<script setup>
import { inject } from 'vue'

// 1. Basic Reception
const theme = inject('theme')

// 2. With default values(My ancestors are no longer with us provide When to Use)
const theme = inject('theme', 'light')

// 3. Include factory function defaults
const theme = inject('theme', () => 'light')

// 4. Type Assertion(TypeScript)
const theme = inject<string>('theme', 'light')
</script>

(3) مثال كامل

VUE
<!-- Ancestor.vue -->
<script setup>
import { ref, provide } from 'vue'

const count = ref(0)
const user = reactive({ name: 'Alice', age: 25 })

provide('count', count)        // ref
provide('user', user)          // reactive
provide('config', { theme: 'dark' })  // Object
provide('reset', () => { count.value = 0 })  // Methods
</script>

<!-- DeepChild.vue(Any depth) -->
<script setup>
import { inject } from 'vue'

const count = inject('count')        // Responsive
const user = inject('user')          // Responsive
const config = inject('config')      // Ordinary Object
const reset = inject('reset')        // Function

// Edit
function increment() {
  count.value++  // ✅ Ancestor components will also respond
}
</script>
▶ جرّب الكود

4. نوع «Symbol» كمفتاح إدخال

(1) لماذا نستخدم Symbol؟

المفاتيح النصية عرضة للتعارضات (فوجود عدة عبارات provide تحمل نفس المفتاح سيؤدي إلى استبدال بعضها البعض). أما الرموز فهي فريدة، مما يمنع حدوث التعارضات.

VUE
<!-- keys.js - Centrally manage all provide key -->
<script>
export const THEME_KEY = Symbol('theme')
export const USER_KEY = Symbol('user')
export const LOCALE_KEY = Symbol('locale')
export const CONFIG_KEY = Symbol('config')
</script>
VUE
<!-- Ancestor.vue Ancestor Components -->
<script setup>
import { provide, ref } from 'vue'
import { THEME_KEY, USER_KEY, LOCALE_KEY } from './keys'

provide(THEME_KEY, ref('light'))
provide(USER_KEY, ref({ name: 'Alice' }))
provide(LOCALE_KEY, ref('zh-CN'))
</script>
VUE
<!-- DeepChild.vue Any descendants -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY } from './keys'

// ✅ Type Safety:Must use Symbol talent inject
const theme = inject(THEME_KEY)
const user = inject(USER_KEY)
</script>

(2) 5 مزايا رئيسية

الميزة الوصف
التفرد الرمز فريد على مستوى العالم؛ ولا تحدث أي تعارضات
أمان الأنواع يمكن لـ TypeScript استنتاج
الإدارة المركزية يتم تخزين جميع المفاتيح في ملف واحد
ملائم لإعادة الهيكلة تعديل مفتاح ما لا يؤثر على المفاتيح الأخرى
سهولة القراءة يمكنك معرفة الغرض منه بمجرد النظر إلى اسم المفتاح


5. شرح مفصل لـ provide التفاعلي

(1) 3 أنواع من أوضاع "التوفير"

VUE
<!-- Ancestor.vue -->
<script setup>
import { ref, reactive, provide } from 'vue'

// 1. Inject ref(Responsive)
const count = ref(0)
provide('count', count)

// 2. Inject reactive(Responsive)
const user = reactive({ name: 'Alice' })
provide('user', user)

// 3. Injecting Read-Only Objects(Non-responsive)
const config = { theme: 'dark' }
provide('config', config)
</script>

(2) طريقتان لإجراء التغييرات

VUE
<!-- Method 1:Directly Modify Ancestors(Recommendations) -->
<!-- Ancestor.vue -->
<script setup>
import { ref, provide } from 'vue'

const theme = ref('light')
provide('theme', theme)

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
}
</script>

<!-- Method 2:Through their descendants inject Edit Citation -->
<!-- DeepChild.vue -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme')

function toggleTheme() {
  theme.value = 'dark'  // ✅ The change is ref,Both the ancestors and other descendants responded
}
</script>

(3) 5 سيناريوهات استجابة رئيسية

السيناريو الاستخدام
تغيير السمة الرجوع + توفير
تسجيل دخول المستخدم reactive + provide
الإعدادات المحلية (i18n) المرجع + التوفير + طريقة التبديل
التحميل العالمي ref(false) + توفير
إشعارات الرسائل العالمية تفاعلية + توفير


6. 5 سيناريوهات رئيسية من واقع الحياة

(1) المشهد الأول: تغيير الموضوع

JS
// keys.js
export const THEME_KEY = Symbol('theme')
VUE
<!-- App.vue Ancestors -->
<script setup>
import { ref, provide } from 'vue'
import { THEME_KEY } from './keys'

const theme = ref('light')
provide(THEME_KEY, theme)
</script>

<!-- ThemeButton.vue Descendants -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY } from './keys'

const theme = inject(THEME_KEY)
</script>

<template>
  <button @click="theme.value = theme === 'light' ? 'dark' : 'light'">
    {{ theme === 'light' ? '🌞' : '🌙' }}
  </button>
</template>

(2) السيناريو 2: مصادقة المستخدم

JS
// keys.js
export const AUTH_KEY = Symbol('auth')
VUE
<!-- App.vue -->
<script setup>
import { reactive, provide } from 'vue'
import { AUTH_KEY } from './keys'

const auth = reactive({
  user: null,
  isLoggedIn: false,
  login(credentials) { /* API call */ },
  logout() { this.user = null; this.isLoggedIn = false }
})
provide(AUTH_KEY, auth)
</script>

<!-- UserMenu.vue Descendants -->
<script setup>
import { inject } from 'vue'
import { AUTH_KEY } from './keys'

const auth = inject(AUTH_KEY)
</script>

<template>
  <div v-if="auth.isLoggedIn">{{ auth.user.name }}</div>
  <button v-else @click="auth.login(creds)">Login</button>
</template>

(3) السيناريو 3: i18n

JS
// keys.js
export const I18N_KEY = Symbol('i18n')
VUE
<!-- App.vue -->
<script setup>
import { ref, provide, computed } from 'vue'
import { I18N_KEY } from './keys'

const messages = {
  'zh-CN': { hello: 'Hello', welcome: 'Welcome' },
  'en-US': { hello: 'Hello', welcome: 'Welcome' }
}

const locale = ref('zh-CN')
const t = computed(() => (key) => messages[locale.value][key])

provide(I18N_KEY, { locale, t })
</script>

<!-- Hello.vue Descendants -->
<script setup>
import { inject } from 'vue'
import { I18N_KEY } from './keys'

const { t } = inject(I18N_KEY)
</script>

<template>
  <h1>{{ t('hello') }}, {{ t('welcome') }}</h1>
</template>

(4) السيناريو 4: التحميل العالمي

VUE
<!-- App.vue -->
<script setup>
import { ref, provide } from 'vue'

const loading = ref(false)
provide('loading', loading)
</script>

<!-- AnyComponent.vue Descendants -->
<script setup>
import { inject } from 'vue'
const loading = inject('loading')

async function fetchData() {
  loading.value = true
  await fetch('/api/data')
  loading.value = false
}
</script>

<template>
  <button @click="fetchData">Refresh</button>
</template>

(5) السيناريو 5: الموضوع + بيانات الأعمال

VUE
<!-- App.vue -->
<script setup>
import { ref, reactive, provide } from 'vue'

const appState = reactive({
  theme: 'light',
  user: { name: 'Alice' },
  permissions: ['read', 'write'],
  config: { sidebar: true }
})
provide('appState', appState)
</script>


7. التوفير / الإدخال مقابل الأساليب الأخرى

(1) مقارنة بين 5 طرق للتواصل

الجانب props/emit provide/inject ناقل الأحداث Pinia mitt
مراسلات بين الأب والابن ⭐⭐⭐⭐⭐ - - - -
عبر المستويات ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Brothers Communications ❌ (يتطلب ترحيلًا من الوالدين) ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
متجاوب ❌ (يدوي)
أمان الأنواع ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
أدوات المطورين ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
منحنى التعلم ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐

(2) 5 سيناريوهات رئيسية للاختيار

السيناريو الحل الموصى به
التواصل بين الوالدين والأبناء props/emit
الstatus العامة المشتركة بين المستويات (السمة/المصادقة/i18n) توفير/إدراج
الstatus الشاملة في التطبيقات المتوسطة إلى الكبيرة Pinia (المرحلة 4.2)
ناقل الأحداث البسيط mitt / tiny-emitter
إدارة الحالات التجارية المعقدة Pinia + Vue Router


8. مثال كامل: تبديل السمات في النظام الخلفي لموقع التجارة الإلكترونية

▶ مثال: 1. الإدارة المركزية للمفاتيح

JS
// src/keys.js
export const THEME_KEY = Symbol('theme')
export const USER_KEY = Symbol('user')
export const LOCALE_KEY = Symbol('locale')
export const CART_KEY = Symbol('cart')
▶ جرّب الكود

▶ مثال: 2. App.vue — مرة واحدة provide

VUE
<!-- src/App.vue -->
<script setup>
import { ref, reactive, provide, readonly } from 'vue'
import { THEME_KEY, USER_KEY, LOCALE_KEY, CART_KEY } from './keys'

// Topic
const theme = ref('light')
function setTheme(newTheme) {
  theme.value = newTheme
  localStorage.setItem('theme', newTheme)
}

// User
const user = reactive({
  id: 1,
  name: 'Alice',
  role: 'admin'
})

// Internationalization
const locale = ref('zh-CN')
const messages = {
  'zh-CN': { home: 'Home', cart: 'Shopping Cart' },
  'en-US': { home: 'Home', cart: 'Cart' }
}
const t = (key) => messages[locale.value][key]

// Shopping Cart
const cart = reactive({ item: [], total: 0 })

// Disposable provide 4 Global state
provide(THEME_KEY, { theme, setTheme })
provide(USER_KEY, { user })
provide(LOCALE_KEY, { locale, t })
provide(CART_KEY, { cart })
</script>

<template>
  <router-view />
</template>
▶ جرّب الكود

▶ مثال: 3. الاستخدام من قبل أي سليل (5 طرق للاستخدام)

VUE
<!-- DeepChild.vue Any depth -->
<script setup>
import { inject } from 'vue'
import { THEME_KEY, USER_KEY, LOCALE_KEY, CART_KEY } from './keys'

// 1. Deconstruction
const { theme, setTheme } = inject(THEME_KEY)
const { user } = inject(USER_KEY)
const { locale, t } = inject(LOCALE_KEY)
const { cart } = inject(CART_KEY)
</script>

<template>
  <div :class="['app', theme]">
    <p>{{ user.name }} ({{ user.role }})</p>
    <p>{{ t('home') }} | {{ t('cart') }}</p>
    <p>{{ cart.items.length }} items, ${{ cart.total }}</p>
    
    <button @click="setTheme('dark')">Dark Mode</button>
  </div>
</template>
▶ جرّب الكود

▶ مثال: 4. توضيح المزايا الخمس الرئيسية لمفاتيح إدخال الرموز

JS
// src/keys.js
export const THEME_KEY = Symbol('theme')  // The Only One
export const ANOTHER_THEME_KEY = Symbol('theme')  // Also the only one(Different Symbol)
▶ جرّب الكود
VUE
<!-- App.vue -->
<script setup>
import { provide, ref } from 'vue'
import { THEME_KEY, ANOTHER_THEME_KEY } from './keys'

const theme1 = ref('light')
const theme2 = ref('dark')

// ✅ There will be no conflict:Different Symbol Even if the names are the same
provide(THEME_KEY, theme1)
provide(ANOTHER_THEME_KEY, theme2)
</script>

▶ مثال: 5. مرجع سريع لـ 5 أخطاء شائعة

الخطأ الأعراض الحل
تعارضات مفاتيح السلاسل الكتابة فوق استخدام الرمز
مصدر غير سلف غير محدد التحقق من التسلسل الهرمي للمكونات
تعديل قيمة "للقراءة فقط" تحذير تعديل السلف؛ أما الأحفاد فهي للقراءة فقط
تجاهل القيمة الافتراضية غير محدد inject(key, defaultValue)
عدد كبير من عبارات provide صعوبة في الصيانة مركزية في keys.js

▶ مثال: 6. 5 مقارنات رئيسية في الأداء

الوضع الأداء قابلية التطبيق
provide('key', value) ⭐⭐⭐⭐⭐ قيمة ثابتة
provide('key', ref) ⭐⭐⭐⭐⭐ التقييم التفاعلي
provide('key', reactive) ⭐⭐⭐⭐ كائنات معقدة
provide('key', computed) ⭐⭐⭐⭐⭐ القيمة المشتقة
provide('key', function) ⭐⭐⭐⭐ كيفية الاستخدام


❓ أسئلة شائعة

س أيهما يجب أن أستخدم، provide أم props؟
ج استخدم props للاتصال المباشر بين المكونات الأصلية والتابعة (صريح وآمن من حيث النوع). استخدم provide للتواصل عبر المستويات (3 مستويات أو أكثر) لتجنب «prop drilling». استخدم Pinia (المرحلة 4.2) للstatus العامة في التطبيقات الكبيرة.
س هل البيانات المقدمة تفاعلية؟
ج هذا يعتمد على نوع البيانات المقدمة. provide('key', ref) تفاعلية، provide('key', {a: 1}) غير تفاعلية (كائن عادي). نوصي باستخدام provide ref أو provide reactive.
س هل يجب أن يكون provide في المستوى الأعلى من setup؟
ج نعم. يجب استدعاء provide في المستوى الأعلى من <script setup> (لا يُسمح باستدعائه داخل خطافات دورة الحياة)؛ وإلا، فلن يسري مفعول provide.
س هل يمكنني تعديل قيمة provide بعد inject؟
ج يمكنك تعديل ref.value، لكن يُنصح بالقيام بذلك فقط في المكونات السلفية (باستخدام طريقة provide). فقد يؤدي تعديل ref مباشرةً إلى عدم وضوح تدفق البيانات.
س أين يتم تعريف مفتاح الرموز؟
ج في ملف منفصل src/keys.js، الذي يقوم بتصدير جميع الرموز. ويتم استيراد جميع المكونات المستخدمة من هذا الملف.
س كيف أختار بين provide/inject وPinia؟
ج بالنسبة للمشاريع الصغيرة (أقل من 5 حالات عامة)، يكفي استخدام provide/inject. أما بالنسبة للمشاريع المتوسطة إلى الكبيرة (10 حالات عامة أو أكثر)، فيُفضل استخدام Pinia، لأنها توفر دعمًا أفضل لأدوات التطوير (DevTools).
س ماذا يحدث عند إلغاء ربط مكون يحتوي على provide؟
ج يتم أيضًا إلغاء ربط جميع المكونات التابعة له (دورة حياة شجرة مكونات Vue). وسيقوم inject بالبحث مرة أخرى عن المكونات السلفية في الاتجاه الصاعد.

📖 ملخص


📝 تمارين

  1. أسئلة أساسية (مستوى الصعوبة: ⭐)

    تنفيذ أداة بسيطة لتبديل السمات:

    • ملف keys.js يحدد THEME_KEY
    • يوفر ملف App.vue السمة (ref) + طريقة setTheme
    • ThemeButton.vue: إدراج السمة + تعيين السمة
    • انقر على الزر للتبديل بين الوضع الفاتح والوضع الداكن
  2. مسائل متقدمة (مستوى الصعوبة: ⭐⭐)

    تنفيذ مصادقة المستخدم (توفير/إدخال):

    • حقن AUTH_KEY: طرق user و login و logout
    • يقوم ملف App.vue بتعيين المتغير user على القيمة null
    • في ملف LoginPage.vue، استدعِ login(creds) لمحاكاة عملية تسجيل الدخول
    • يعرض ملف Header.vue واجهة مستخدم مختلفة بناءً على قيمة isLoggedIn
  3. مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)

    تنفيذ نظام شامل لإدارة الstatus العامة للعمليات الخلفية للتجارة الإلكترونية:

    1. 4 مفاتيح: THEME / USER / CART / LOCALE
    2. يوفر ملف App.vue 4 حالات عامة في آن واحد
    3. استخدام 5 مكونات على مستويات مختلفة (الرأس / الشريط الجانبي / قائمة المنتجات / سلة التسوق / التذييل)
    4. التواصل بين المكونات: عند النقر على العنوان، يتم تسجيل الخروج → يتم تحديث الشريط الجانبي تلقائيًا
    5. تبديل السمة + تبديل الترجمة (i18n) + إضافة العناصر إلى سلة التسوق وحذفها منها
    6. استخدام TypeScript للتصنيف القوي
    7. 5 توفير الأنماط (القيمة/المرجعية/التفاعلية/المحسوبة/الوظيفية)
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%