Skip to content

تزويد / حقن ​

لقراءة هذه الصفحة يجب عليك أولا الاطلاع على أساسيات المُكونات . ثم العودة إلى هنا.

التمرير العميق للخاصيات ​

عادةً , عندما نحتاج إلي تمرير بيانات من المُكون الأب إلي المُكون الإبن . نستخدم الخاصيات (Props). لكن تخيل اننا لدينا شجرة كبيرة من المُكونات , و المُكون الحفيد يحتاج إلي بيانات من المُكون الجد الأعلي منه . بإستخدام الخاصيات (Props) فقط , سوف نتمكن من تمرير نفس الخاصية إلي سلسلة الآباء كاملةً لكي ننقلها من المُكون الجد إلي المُكون الحفيد

مُخطط التمرير العميق للخاصيات

لاحظ ان علي الرغم من ان المُكون <Footer> لا يحتاج هذه الخاصيات علي الإطلاق , ولكن لا يزال بحاجة إلي تعريفها و تمريرها حتي يتمكن المُكون <DeepChild> من الوصول إليها . إذا كان هناك سلسلة من الآباء أطول مُكونات اكثر سيتم تمرير الخاصيات إليها و هي ليس بحاجة لها . و هذا يسمي "التمرير العميق للخاصيات" وبالتأكيد ليس من الجيد فعله

يمكننا حل مشكلة التمرير العميق للخاصيات (Props Drilling) عن طريق إستخدام التزويد و الحقن provide و inject . ان يكون المُكون الأب هو مُزود التبعية (dependency provider) لكل المُكونات الأحفاد له . يمكن ان يتم حقن inject أي مُكون في شجرة الأحفاد و الأبناء بغض النظر عن مدي عمق هذا المُكون بالتباعيات المقدمة من المُكونات الأعلي (الآباء او الجدود) في هذه السلسلة

مُخطط التزويد / الحقن

التزويد ​

لكي نقوم بتوفير البيانات إلي المُكونات الأحفاد , نستخدم دالة ()provide

vue
<script setup>
import { provide } from 'vue'

provide(/* المفتاح */ 'message', /* القيمة */ 'hello!')
</script>

في حالة عدم إستخدام <script setup> , تأكد من إستدعاء دالة ()provide بشكل متزامن (Synchronously) بداخل دالة ()setup

js
import { provide } from 'vue'

export default {
  setup() {
    provide(/* المفتاح */ 'message', /* القيمة */ 'hello!')
  }
}

تقبل هذه الدالة ()provide وسيطين . يمسي الوسيط الأول بمفتاح الحقن injection key , و الذي يمكن ان يكون في هيئة نصاً String أو رمزً Symbol . يتم إستخدام مفتاح الحقن بواسطة المُكونات الأحفاد للبحث عن القيمة المرغوب الوصول لها . كما يستطيع المُكون الأحادي ان يستدعي الدالة ()provide عدة مرات لمفاتيح حقن مختلفة لتوفير قيم عديدة يمكن حقنها من قبل المُكونات الأحفاد

الوسيط الثاني هو القيمة المُقدمة . يمكن لهذه القيمة ان تكون من اي نوع , تشمل الحالة التفاعلية مثل refs

js
import { ref, provide } from 'vue'

const count = ref(0)
provide('key', count)

يسمح توفير القيم التفاعلية (Reactive Values) للمُكونات الأحفاد بإنشاء اتصال تفاعلي إلي المكون المُقدم للبيانات

لكي تقوم بتزويد المُكونات الأحفاد بالبيانات , أستخدم خاصية provide

js
export default {
  provide: {
    message: 'hello!'
  }
}

كل خاصية في الكائن provide , يتم إستخدام المفتاح الخاص بها بواسطة المُكونات الأبناء أو الأحفاد وذالك لتحديد القيمة الصحيحة للحقن

إذا كنا نريد تقديم بيانات علي مستوي النسخة (Instance) , علي سبيل المثال البيانات الموجودة في دالة ()data , فيجب إستخدام provide علي هيئة دالة :

js
export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    // `this` عند استخدام صيغة الدالة يمكننا استخدام 
    return {
      message: this.message
    }
  }
}

ولكن , لاحظ ان هذا الشكل لا يجعل الحقن تفاعلياً . يتم مناقشة هذا أسفل جعل الحقن تفاعلياً

التزويد علي مستوى التطبيق ​

بالإضافة إلي تزويد البيانات إلي المُكونات , يمكن ايضاً تزويدها علي مستوي التطبيق:

js
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* المفتاح */ 'message', /* القيمة */ 'hello!')

التزويد علي مستوي التطبيق يكون متاح لجميع المُكونات التي تم عرضها (Rendered components) في التطبيق . هذا الشكل مفيد خاصةً في حالة إنشاء إضافات, لأن الإضافات عادةً لن تكون قادرة علي تزويد القيم بإستخدام المُكونات

الحقن ​

لكي نحقن البيانات التي يوفرها أحد مُكونات الأحفاد, نستخدم دالة ()inject

vue
<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

في حالة ان القيمة التي تم تزويدها هي قيمة تفاعلية (Ref) , سوف يتم حقنها كما هي و لم يتم تفكيكها تلقائياً . هذا يسمح للمكون الحاقن بالإحتفاظ بالإتصال التفاعلي بين المُكون Provider المزود

مثال كامل للتزويد + الحقن مع الحالة التفاعلية

مرة أخري , في حالة عدم استخدام <script setup> , يجب إستدعاء دالة ()inject بشكل متزامن بداخل دالة ()setup

js
import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

لكي نحقن البيانات المتوفرة بواسطة المُكونات الأحفاد, نستخدم خاصية inject

js
export default {
  inject: ['message'],
  created() {
    console.log(this.message) // القيمة التي تم حقنها
  }
}

ينفذ الحقن قبل الحالة الخاصة بالمُكون . لذالك يُمكن الوصول إلي الخواص التي تم حقنها دالخل دالة ()data

js
export default {
  inject: ['message'],
  data() {
    return {
      // القيمة الإبتدائية إعتماداُ علي القيمة التي تم حقنها
      fullMessage: this.message
    }
  }
}

مثال كامل على التزويد + الحقن

الاسماء المميزة للحقن ​

في حالة استخدام هيئة المصفوفة لكلمة inject , تكون الخاصيات المُحقنة مُعرضة لإستخدام نفس المفتاح الموجودة في نسخة المُكون (Component Instance) . في المثال السابق , تم تقديم الخاصية بمفتاح يسمي "message" , و تم حقنها ك this.message . المفتاح المحلي هو نفسه مفتاح الحقن .

إذا كنا نريد ان نحقن هذه الخاصية بإستخدام اسم مفتاح مختلف , نحتاج في هذه الحالة إلي إستخدام هيئة الكائن للخاصية inject:

js
export default {
  inject: {
    /* المفتاح المحلي */ localMessage: {
      from: /* مفتاح الحقن */ 'message'
    }
  }
}

هنا, سوف يتعرف المُكون علي خاصية مُقدمة بمفتاح يُسمي "message" , ثم استدعاؤه بإسم this.localMessage.

القيم الإفتراضية للحقن ​

بشكل أفتراضي , يفترض inject ان المفتاح المحقون تم تزويده في مكانً ما في سلسلة الاَباء . لذالك في حالة عدم تزويده سيتم إظهار رسالة تحذير وقت التشغيل

في حالة اننا نريد ان نجعل خاصية الحقن تعمل بشكل اختياري في حالة تم تزويدها أو لا . يجب تعريف قيمة إبتدائية مثل التي في الخاصيات (Props):

js
// 'default value' سوف تكون قيمتها `value`
// "message" في حالة عدم تزويد خاصية بإسم 
const value = inject('message', 'default value')

في بعض الحالات , نريد إنشاء القيمة الإبتدائية من خلال إستدعاء دالة أو إستنساخ من كلاس جديد . لتجنب الحسابات الغير ضرورية أو الأثار الجانبية في حالة عدم استخدام القيمة الإختيارية . يمكن ان نستخدم دالة المصنع (Factory Function) لإنشاء القيمة الإبتدائية:

js
const value = inject('key', () => new ExpensiveClass(), true)

The third parameter indicates the default value should be treated as a factory function.

js
export default {
  // (inject object) هيئة الكائن مطلوبة
  // عند تعريف قيم إفتراضية للحقن
  inject: {
    message: {
      from: 'message', //  اختياري في حالة استخدام نفس اسم مفتاح الحقن
      default: 'default value'
    },
    user: {
      // (non-primitive values) استخدم دالة المصنع للقيم الغير بدائية 
      // لإنشاء , أو التي يجب ان تكون غير متكررة لكل نسخة مُكون
      default: () => ({ name: 'John' })
    }
  }
}

استخدام الصيغ التفاعلية ​

عند إستخدام قيم تزويد / حقن تفاعلية, من الأفضل الإحتفاظ بأي طفرات (Mutations) في الحالات التفاعلية داخل مُزود provider كلما كان ممكناً

هناك العديد من الاوقات نريد تعديل البيانات من المُكون الحاقن (injector component) . في بعض الأحيان , من الأفضل إضافة دالة مسؤولة عن تغيير الحالة

vue
<!-- داخل مُكون التزويد-->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>
vue
<!-- داخل مُكون الحقن -->
<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>

من أجل جعل الحقن مرتبط مع المُكون المُزود بشكل تفاعلي , نحتاج إلي تزويد خاصية محسوبة (computed) بإستخدام دالة ()computed

js
import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    return {
      // بشكل مباشر (computed property) تزويد خاصية محسوبة  
      message: computed(() => this.message)
    }
  }
}

مثال كامل عن تزويد / حقن مع الحالة التفاعلية

تستحدم دالة ()computed عادةً في مُكونات الواجهة التركيبية (Composition Api Components) , ولكن ممكن ايضا لإستكمال حالات إستخدام معينة في واجهات الخيارات (Options Api) . يمكنك معرفة المزيد حول استخدامه من خلال قراءة أساسيات التفاعلية و الخواص المحسوبة مع أفضلية للواجهة التركيبية

استخدام المفاتيح الرموز Symbols Keys ​

حتي الأن , لقد قمنا بإستخدام مفاتيح الحقن النصية في الأمثلة السابقة . إذا كنت تعمل علي تطبيق كبير مع العديد من مُزودي التباعية (dependency providers) , أو انت مسؤول عن مُكونات سيتم إستخدامها من قبل مُطورين أخرين , من الأفضل إستخدام مفاتيح الحقن الرموز (Symbol injection keys) لتجنب الصدامات المحتملة

من الأفضل إستخراج الرموز من ملف مخصوص لها:

js
// keys.js
export const myInjectionKey = Symbol()
js
// في مُكون التزويد
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* البيانات المُراد تزويدها */
})
js
// في مُكون الحقن
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

شاهد أيضاً: الأنواع للتزويد / الحقن

js
// في مُكون التزويد
import { myInjectionKey } from './keys.js'

export default {
  provide() {
    return {
      [myInjectionKey]: {
         /* البيانات المُراد تزويدها */
      }
    }
  }
}
js
// في مُكون الحقن
import { myInjectionKey } from './keys.js'

export default {
  inject: {
    injected: { from: myInjectionKey }
  }
}
تزويد / حقن has loaded