Skip to content

الخاصيات

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

التصريح بالخاصيات

المكونات في Vue تتطلب التصريح الواضح بخاصياتها لكي تعرف Vue أيُّ خاصيات خارجية ممررة إلى المكون يجب أن تُعامل كسمات مستترة (التي ستُناقش في قسمها المخصص).

في المكونات أحادية الملف التي تستخدم <script setup>، يمكن التصريح بالخاصيات باستخدام التعليمة العامة defineProps():

vue
<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>

في المكونات التي لا تستعمل <script setup> ، يمكن التصريح بالخاصيات باستخدام خيار props:

js
export default {
  props: ['foo'],
  setup(props) {
    // setup() تتلقى الخاصيات كأول وسيط
    console.log(props.foo)
  }
}

تجدر الملاحظة أن الوسيط المرسل إلى ()defineProps هو نفس القيمة المقدمة لخيار props: نفس واجهة الخيارات للخاصيات مشتركة بين الأسلوبين.

الخاصيات يُصرّح بها باستخدام خيار props:

js
export default {
  props: ['foo'],
  created() {
    // الخاصيات معروضة على `this`
    console.log(this.foo)
  }
}

بالإضافة إلى التصريح بالخاصيات باستخدام مصفوفة من السلاسل النصية، يمكننا أيضا استخدام الصيغة الكائنية:

js
export default {
  props: {
    title: String,
    likes: Number
  }
}
js
// في <script setup>
defineProps({
  title: String,
  likes: Number
})
js
// في الملفات التي لا تستخدم <script setup>
export default {
  props: {
    title: String,
    likes: Number
  }
}

في كل خاصية في صيغة التصريح الكائنية، يكون المفتاح هو اسم الخاصية، بينما يجب أن تكون القيمة دالة بانية للنوع المتوقع.

هذا لا يعني فقط توثيق مكونك، بل سيُحذر المطورين الآخرين الذين يستخدمون مكونك داخل وحدة التحكم للمتصفح في حال تم تمرير قيمة ذات نوع خاطئ. سنتحدث بتفصيل أكثر عن التحقق من صحة الخاصيات في وقت لاحق داخل هذه الصفحة.

اطلع أيضا على : إضافة الأنواع للمكونات

إذا كنت تستخدم TypeScript مع <script setup>، فمن الممكن أيضا التصريح بالخاصيات باستخدام توصيفات النوع البحت:

vue
<script setup lang="ts">
defineProps<{
  title?: string
  likes?: number
}>()
</script>

تفاصيل أكثر عن: إضافة الأنواع للمكونات

Reactive Props Destructure

Vue's reactivity system tracks state usage based on property access. E.g. when you access props.foo in a computed getter or a watcher, the foo prop gets tracked as a dependency.

So, given the following code:

js
const { foo } = defineProps(['foo'])

watchEffect(() => {
  // runs only once before 3.5
  // re-runs when the "foo" prop changes in 3.5+
  console.log(foo)
})

In version 3.4 and below, foo is an actual constant and will never change. In version 3.5 and above, Vue's compiler automatically prepends props. when code in the same <script setup> block accesses variables destructured from defineProps. Therefore the code above becomes equivalent to the following:

js
const props = defineProps(['foo'])

watchEffect(() => {
  // `foo` transformed to `props.foo` by the compiler
  console.log(props.foo)
})

In addition, you can use JavaScript's native default value syntax to declare default values for the props. This is particularly useful when using the type-based props declaration:

ts
const { foo = 'hello' } = defineProps<{ foo?: string }>()

If you prefer to have more visual distinction between destructured props and normal variables in your IDE, Vue's VSCode extension provides a setting to enable inlay-hints for destructured props.

Passing Destructured Props into Functions

When we pass a destructured prop into a function, e.g.:

js
const { foo } = defineProps(['foo'])

watch(foo, /* ... */)

This will not work as expected because it is equivalent to watch(props.foo, ...) - we are passing a value instead of a reactive data source to watch. In fact, Vue's compiler will catch such cases and throw a warning.

Similar to how we can watch a normal prop with watch(() => props.foo, ...), we can watch a destructured prop also by wrapping it in a getter:

js
watch(() => foo, /* ... */)

In addition, this is the recommended approach when we need to pass a destructured prop into an external function while retaining reactivity:

js
useComposable(() => foo)

The external function can call the getter (or normalize it with toValue) when it needs to track changes of the provided prop, e.g. in a computed or watcher getter.

تفاصيل تمرير الخاصيات

طريقة تسمية الخاصية

نصرح بأسماء الخاصيات الطويلة باستخدام نمط سنام الجمل camelCase لتجنب استخدام علامات التنصيص عند استخدامها كمفاتيح الخاصية، ويسمح لنا بإيرادها مباشرة في تعبيرات القالب لأنها مُعرِّفات JavaScript صالحة:

js
defineProps({
  greetingMessage: String
})
js
export default {
  props: {
    greetingMessage: String
  }
}
template
<span>{{ greetingMessage }}</span>

تقنيا، يمكنك أيضا استخدام camelCase عند تمرير الخاصيات إلى مكون ابن (باستثناء قوالب DOM). ومع ذلك، فإن المصطلح عليه هو استخدام kebab-case في جميع الحالات لتتوافق مع نمط كتابة سمات HTML:

template
<MyComponent greeting-message="مرحبا" />

نستخدم نمط باسكال PascalCase لوسوم المكونات عند الإمكان لأنه يحسن قابلية القراءة للقوالب بتمييز مكونات Vue من العناصر الأصلية. ومع ذلك، لا يوجد فائدة عملية كبيرة في استخدام نمط سنام الجمل camelCase عند تمرير الخاصيات، لذا نختار أن نتبع اصطلاحات كل لغة.

الخصائص الثابتة والمتغيرة

إلى حد الآن، لقد رأيت خاصيات مُمرَّرة كقيم ثابتة، مثل :

template
<BlogPost title="My journey with Vue" />

كما رأيت أيضا خاصيات مُعيَّنة بشكل ديناميكي باستخدام v-bind أو اختصارها :، مثل :

template
<!-- تعيين القيمة ديناميكيا من متغير -->
<BlogPost :title="post.title" />

<!-- تعيين القيمة ديناميكيا من تعبير معقد -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

تمرير أنواع مختلفة من القيم

في المثالين أعلاه، مررنا قيم نصية، ولكن يمكن تمرير أي نوع من القيم إلى الخاصية.

الأرقام

template
<!-- على الرغم من أن `42` ثابت، فإننا نحتاج إلى  v-bind  لإخبار  Vue
  بأن  هذا تعبير  JavaScript  بدلاً من سلسلة نصية. -->
<BlogPost :likes="42" />

<!-- تعيين القيمة ديناميكيا من متغير -->
<BlogPost :likes="post.likes" />

القيم المنطقية

template
<!--  تضمين الخاصية بدون قيمة سيعني أن أنها
`true`. -->
<BlogPost is-published />
<!-- على الرغم من أن `42` ثابت، فإننا نحتاج إلى  v-bind  لإخبار  Vue
  بأن  هذا تعبير  JavaScript  بدلاً من سلسلة نصية. -->
<BlogPost :is-published="false" />

<!-- تعيين القيمة ديناميكيا من متغير -->
<BlogPost :is-published="post.isPublished" />

المصفوفات

template
<!-- على الرغم من أن `42` ثابت، فإننا نحتاج إلى  v-bind  لإخبار  Vue
  بأن  هذا تعبير  JavaScript  بدلاً من سلسلة نصية. -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- تعيين القيمة ديناميكيا من متغير -->
<BlogPost :comment-ids="post.commentIds" />

الكائنات

template
<!-- على الرغم من أن `42` ثابت، فإننا نحتاج إلى  v-bind  لإخبار  Vue
  بأن  هذا تعبير  JavaScript  بدلاً من سلسلة نصية. -->
<BlogPost
  :author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
  }"
 />

<!-- تعيين القيمة ديناميكيا من متغير -->
<BlogPost :author="post.author" />

ربط عدة خصائص باستخدام كائن

إذا كنت تريد تمرير جميع حقول الكائن كخاصيات مكون، يمكنك استخدام v-bind بدون وسيط (v-bind بدلاً من prop-name:). على سبيل المثال، لنعتبر الكائن post كما يلي:

js
export default {
  data() {
    return {
      post: {
        id: 1,
        title: 'رحلتي مع Vue'
      }
    }
  }
}
js
const post = {
  id: 1,
  title: 'رحلتي مع Vue'
}

القالب الموالي:

template
<BlogPost v-bind="post" />

سيتم تمرير كل الخصائص الموجودة في الكائن post كخصائص للمكون BlogPost. لذا، سيكون الناتج مثل الشيفرة التالية:

template
<BlogPost :id="post.id" :title="post.title" />

تدفق البيانات في اتجاه واحد

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

بالإضافة إلى ذلك، سيتم تحديث كل الخاصيات في المكون الابن مع أحدث قيمة، وهذا يعني أنه لا ينبغي محاولة تغيير الخاصية داخل المكون الابن. إذا قمت بذلك، ستحذرك Vue في وحدة التحكم:

js
const props = defineProps(['foo'])

// ❌ تحذير، الخاصيات قابلة للقراءة فقط!
props.foo = 'bar'
js
export default {
  props: ['foo'],
  created() {
    // ❌ تحذير، الخاصيات قابلة للقراءة فقط!
    this.foo = 'bar'
  }
}

يوجد عادة حالتان بحيث تشجعان على تغيير الخاصية:

  1. الخاصية تستخدم لتمرير قيمة أولية؛ والمكون الابن يريد استخدامها كخاصية بيانات محلية بعد ذلك. في هذه الحالة، فإنه من الأفضل تعريف خاصية بيانات محلية تستخدم الخاصية كقيمتها الأولية:

    js
    const props = defineProps(['initialCounter'])
    
    // counter يستخدم props.initialCounter
    // كقيمة أولية فقط؛ وهو مفصول عن تحديثات الخاصية المستقبلية.
    const counter = ref(props.initialCounter)
    js
    export default {
      props: ['initialCounter'],
      data() {
        return {
            // counter يستخدم props.initialCounter
           // كقيمة أولية فقط؛ وهو مفصول عن تحديثات الخاصية المستقبلية.
          counter: this.initialCounter
        }
      }
    }
  2. الخاصية تمرر كقيمة خام تحتاج إلى تحويل. في هذه الحالة، فإنه من الأفضل تعريف خاصية محسوبة باستخدام قيمة الخاصية:

    js
    const props = defineProps(['size'])
    
    // خاصية محسوبة تُحدث تلقائيًا عند تغيير الخاصية
    const normalizedSize = computed(() => props.size.trim().toLowerCase())
    js
    export default {
      props: ['size'],
      computed: {
        // خاصية محسوبة تُحدث تلقائيًا عند تغيير الخاصية
        normalizedSize() {
          return this.size.trim().toLowerCase()
        }
      }
    }

تغيير خاصيات الكائن / المصفوفة

عندما تُمرر الكائنات والمصفوفات كخاصيات، فإن المكون الابن لن يتمكن من تغيير ربط الخاصية، ولكن سيتمكن من تغيير خصائص الكائن أو المصفوفة المتداخلة. هذا لأنه في JavaScript يتم تمرير الكائنات والمصفوفات بالمرجع و ليس بالقيمة، و هو أمر مكلف تقنيا على Vue لمنع مثل هذه التغييرات.

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

التحقق من صحة الخاصية

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

لتحديد متطلبات التحقق من صحة الخاصية، يمكنك إضافة كائن مع متطلبات التحقق إلى التعليمة العامة ()definePropsخيار props، بدلاً من مصفوفة السلاسل النصية. على سبيل المثال:

js
defineProps({
  // التحقق من النوع الأساسي  (ستسمح القيم
  // `null` و `undefined` بأي نوع)
  propA: Number,
  // أنواع متعددة ممكنة
  propB: [String, Number],
  // سلسلة نصية مطلوبة
  propC: {
    type: String,
    required: true
  },
  // Required but nullable string
  propD: {
    type: [String, null],
    required: true
  },
  // Number with a default value
  propE: {
    type: Number,
    default: 100
  },
  // Object with a default value
  propF: {
    type: Object,
    // يجب أن تُعيد القيم الافتراضية للكائن أو المصفوفة من دالة مصنعة.
    // تتلقى الدالة الخاصة بالخاصية الخام 
    // التي تم استلامها من قبل المكون كوسيط.
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // Custom validator function
  // full props passed as 2nd argument in 3.4+
  propG: {
    validator(value, props) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // Function with a default value
  propH: {
    type: Function,
    // Unlike object or array default, this is not a factory
    // function - this is a function to serve as a default value
    default() {
      return 'الدالة الافتراضية'
    }
  }
})

ملاحظة

لا يمكن للشيفرة داخل وسيط ()defineProps الوصول إلى المتغيرات الأخرى المعرفة في <script setup>، لأن العبارة كاملة تُنقل إلى نطاق دالة خارجية عند التصريف.

js
export default {
  props: {
  // التحقق من النوع الأساسي  (ستسمح القيم
  // `null` و `undefined` بأي نوع)
    propA: Number,
    // أنواع متعددة ممكنة
    propB: [String, Number],
    // سلسلة نصية مطلوبة
    propC: {
      type: String,
      required: true
    },
    // Required but nullable string
    propD: {
      type: [String, null],
      required: true
    },
    // Number with a default value
    propE: {
      type: Number,
      default: 100
    },
    // Object with a default value
    propF: {
      type: Object,
      // يجب أن تُعيد القيم الافتراضية للكائن أو المصفوفة من دالة مصنعة.
      // تتلقى الدالة الخاصة بالخاصية الخام 
      // التي تم استلامها من قبل المكون كوسيط.
      default(rawProps) {
        return { message: 'hello' }
      }
    },
    // Custom validator function
    // full props passed as 2nd argument in 3.4+
    propG: {
      validator(value, props) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // Function with a default value
    propH: {
      type: Function,
      // Unlike object or array default, this is not a factory
      // function - this is a function to serve as a default value
      default() {
        return 'الدالة الافتراضية'
      }
    }
  }
}

تفاصيل إضافية:

  • كل الخاصيات اختيارية افتراضيًا، ما لم يتم تحديد required: true.

  • الخاصية الاختيارية الغائبة إن لم تكن ذات قيمة منطقية Boolean ستكون بقيمة undefined.

  • ستُحوّل الخاصيات المنطقية Boolean الغائبة إلى false . يمكنك تغيير ذلك عن طريق تعيين قيمة افتراضية لها (default) - أي: default: undefined للتصرف كخاصية غير منطقية .

  • إذا حُددت القيمة الافتراضية default، ستُستخدم إذا كانت القيمة المعطاة للخاصية هيundefined - وهذا يتضمن الحالتين عندما تكون الخاصية غير موجودة أو تم تمرير قيمة undefined بشكل صريح.

    عند فشل التحقق من صحة الخاصية، ستُنتج Vue تحذيرًا في وحدة التحكم (إذا كنت تستخدم الإصدار التطويري).

إذا كنت تستخدم الخاصيات المصرحة استنادا على النوع , ستحاول Vue أن تصرف توصيفات النوع إلى تصريحات بالخاصيات وقت التشغيل. على سبيل المثال، ستُصرف <defineProps<{ msg: string } إلى { msg: { type: String, required: true }}.

Note

Note that props are validated before a component instance is created, so instance properties (e.g. data, computed, etc.) will not be available inside default or validator functions.

التحقق من النوع في وقت التشغيل

يمكن أن يكون النوع (type) واحدًا من الدوال البانية الأصلية التالية:

  • String (سلسلة نصية)
  • Number (رقم)
  • Boolean (منطقي)
  • Array (مصفوفة)
  • Object (كائن)
  • Date (تاريخ)
  • Function (دالة)
  • Symbol (رمز)
  • Error (خطأ)

بالإضافة إلى ذلك ، يمكن أن يكون type أيضًا صنفًا مخصصًا أو دالة بانية وسيُجرى التحقق من النوع باستخدام instanceof . على سبيل المثال ،ليكن الصنف التالي:

js
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

الذي يمكنك استخدامه كنوع للخاصية:

js
defineProps({
  author: Person
})
js
export default {
  props: {
    author: Person
  }
}

ستستخدم Vue الشيفرة instanceof Person للتحقق من صحة القيمة الموجودة في خاصية author هل هي نسخة من الصنف Person أم لا.

Nullable Type

If the type is required but nullable, you can use the array syntax that includes null:

js
defineProps({
  id: {
    type: [String, null],
    required: true
  }
})
js
export default {
  props: {
    id: {
      type: [String, null],
      required: true
    }
  }
}

Note that if type is just null without using the array syntax, it will allow any type.

تحويل القيم المنطقية

الخاصيات من نوع Boolean لها قواعد تحويل خاصة لتتماشى مع سلوك السمات المنطقية الأصلية. ليكن المكون <MyComponent> بالتصريح التالي:

js
defineProps({
  disabled: Boolean
})
js
export default {
  props: {
    disabled: Boolean
  }
}

يمكن استخدام المكون بالشكل التالي:

template
<!-- ما يعادل :disabled="true" -->
<MyComponent disabled />

<!-- ما يعادل :disabled="false" -->
<MyComponent />

عندما يصرح بخاصية ما للسماح بتمرير أنواع متعددة، على سبيل المثال

js
// disabled ستحول إلى true
defineProps({
  disabled: [Boolean, Number]
})
  
// disabled ستحول إلى true
defineProps({
  disabled: [Boolean, String]
})
  
// disabled ستحول إلى true
defineProps({
  disabled: [Number, Boolean]
})
  
// disabled ستحول إلى سلسلة نصية فارغة (disabled="")
defineProps({
  disabled: [String, Boolean]
})
js
// disabled ستحول إلى true
export default {
  props: {
    disabled: [Boolean, Number]
  }
}
  
// disabled ستحول إلى true
export default {
  props: {
    disabled: [Boolean, String]
  }
}
  
// disabled ستحول إلى true
export default {
  props: {
    disabled: [Number, Boolean]
  }
}
  
// disabled ستحول إلى سلسلة نصية فارغة (disabled="")
export default {
  props: {
    disabled: [String, Boolean]
  }
}

ستُطبق قواعد التحويل للخاصيات ذات النوع المنطقي Boolean بغض النظر عن ترتيب ظهور النوع.

الخاصيات has loaded