Skip to content

پراپ‌ها

در این صفحه فرض شده که شما از قبل مبانی کامپوننت‌ها را مطالعه کرده اید. اگر با کامپوننت‌ها آشنایی ندارید، ابتدا آن را بخوانید.

تعریف کردن پراپ‌ها

کامپوننت‌های Vue نیاز به تعریف پراپ‌ها بصورت مشخص دارند تا Vue بداند کدام یک از پراپ‌هایی که از خارج کامپوننت به آن پاس داده شده را بصورت fallthrought attributes (که در بخش مخصوص خودش توضیح داده شده) در نظر بگیرد.

هنگام استفاده از SFCها در <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 است. همان پراپ‌های options API بین دو روش تعریف پراپ مشترک هستند.

پراپ‌ها در option API بصورت 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
  }
}

برای هر پراپرتی در سینتکس تعریف کردن پراپ بصورت آبجکت، کلید ( key ) اسم پراپ خواهد بود، در صورتی که مقدار آن باید بصورت یک تابع سازنده از تایپ مقدار آن باشد. (در مثال بالا کلید title نام پراپ و جنس آن String هست)

این فقط مستندات و اطلاعات کامپوننت شما نیست، اگر دیگر توسعه دهندگان از تایپ‌هایی بجز آن تایپی که در کامپوننت تعریف شده استفاده کنند، در کنسول هشداری در ارتباط با تایپ اشتباه مشاهده می‌شود. درباره جزئیات اعتبارسنجی پراپ ها (Props Validation) در ادامه توضیح داده شده است.

مشاهده کنید: Typing Component Props

اگر همراه با <script setup> از Typescript استفاده می‌کنید، می‌توانید با استفاده از pure type annotations پراپ‌ها را تعریف کنید:

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

اطلاعات بیشتر: Typing Component Props

تجزیه پراپ‌های reactive

سیستم واکنش‌پذیری Vue، استفاده از state را بر اساس دسترسی به پراپرتی‌ها دنبال می‌کند. به عنوان مثال، زمانی که شما به props.foo در یک getter محاسباتی یا یک watcher دسترسی پیدا می‌کنید، پراپرتی foo به‌عنوان یک وابستگی ردیابی می‌شود.

برای مثال، با توجه به کد زیر:

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

watchEffect(() => {
  // قبل از نسخه 3.5 فقط یک‌بار اجرا می‌شود
  // دوباره اجرا می‌شود "foo" در نسخه 3.5+ هنگام تغییر پراپ 
  console.log(foo)
})

در نسخه 3.4 و پایین‌تر، foo یک ثابت واقعی است و هرگز تغییر نمی‌کند. اما در نسخه 3.5 و بالاتر، کامپایلر Vue به‌طور خودکار props.‎ را قبل از دسترسی به متغیرهایی که در یک بلوک کد <script setup> از defineProps تجزیه شده‌اند، اضافه می‌کند. بنابراین کد بالا معادل کد زیر خواهد بود:

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

watchEffect(() => {
  // تبدیل شده است `props.foo` توسط کامپایلر به `foo`
  console.log(props.foo)
})

علاوه بر این، شما می‌توانید از سینتکس مقدار پیش‌فرض جاوا اسکریپت برای اعلام مقادیر پیش‌فرض برای props استفاده کنید. این ویژگی به‌ویژه هنگام استفاده از تعریف props بر اساس تایپ بسیار مفید است:

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

اگر ترجیح می‌دهید که تفاوت بین props تجزیه‌شده و متغیرهای معمولی در IDE خود واضح‌تر باشد، افزونه VSCode در vue تنظیماتی برای فعال کردن نکات درون‌خطی (inlay-hints) برای props تجزیه‌شده ارائه می‌دهد.

پاس دادن Props تجزیه‌شده به توابع

وقتی ما یک prop تجزیه‌شده را به یک تابع ارسال می‌کنیم، مثلاً:

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

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

این به‌درستی کار نخواهد کرد، زیرا معادل watch(props.foo, ...) است - ما در واقع به‌جای یک منبع داده reactive، یک value را به watch ارسال می‌کنیم. در واقع، کامپایلر Vue چنین مواردی را تشخیص می‌دهد و یک هشدار صادر می‌کند.

مشابه نحوه نظارت یک prop معمولی با watch(() => props.foo, ...)، ما می‌توانیم یک prop تجزیه‌شده را نیز با قرار دادن آن در یک getter نظارت کنیم:

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

علاوه بر این، این روش توصیه‌شده زمانی است که نیاز داریم یک prop تجزیه‌شده را به یک تابع خارجی ارسال کنیم و واکنش‌پذیری آن حفظ شود:

js
useComposable(() => foo)

تابع خارجی می‌تواند هنگام نیاز به ردیابی تغییرات پراپ، getter را فراخوانی کند (یا آن را با toValue نرمال‌سازی کند)، مثلاً در یک computed یا watcher.

جزئیات پاس دادن پراپ‌ها

نگارش اسم پراپ‌ها

برای تعریف کردن اسامی پراپ‌های طولانی از نگارش camelCase استفاده می‌کنیم چرا که باعث می‌شود هنگام استفاده کردن از آن ها به عنوان کلید های پراپرتی نیازی به استفاده از quotes نباشد، و به ما اجازه می‌دهد که به صورت مستقیم در template expressions به آنها رفرنس بدهیم چرا که شناسه های معتبر جاوااسکریپت هستند.

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

از نظر فنی، می‌توانید هنگام پاس دادن پراپ به کامپوننت فرزند از نگارش camelCase استفاده کنید ( بجز in-DOM templates ). اگرچه روش معمول استفاده از نگارش kebab-case میباشد که با اتریبیوت‌های HTML از یک نگارش واحد پیروی کنند.

template
<MyComponent greeting-message="hello" />

ما در صورت امکان از نگارش PascalCase برای تگ‌های کامپوننت استفاده می‌کنیم، برای اینکه شناسایی بین المنت‌های بومی HTML ها و کامپوننت‌های Vue راحت‌تر باشد. اگرچه استفاده کردن از نگارش camelCase برای پاس دادن پراپ ها خیلی سودی عَملی ندارد، به همین دلیل تصمیم گرفتیم از روش های معمول زبان ها استفاده کنیم.

پراپ‌های استاتیک و داینامیک

تاکنون پراپ‌های استاتیک را دیده‌ایم، به این شکل:

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

همچنین دیده‌ایم که پراپ‌ها با استفاده از v-bind یا میانبر : به یک مقدار داینامیک نسبت داده شده‌اند، به این شکل:

template
<!-- نسبت دادن به یک متغیر بصورت داینامیک -->
<BlogPost :title="post.title" />

<!-- نسبت دادن به یک عبارت بصورت داینامیک -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

انتقال مقادیر با تایپ‌های متفاوت

در دو مثال بالا، ما دو مقدار با تایپ‌ رشته ( string ) پاس داده ایم، اما می‌توان هر مقدار با هر تایپی به یک پراپ پاس داد.

Number

template
<!-- بگوییم Vue نیاز داریم تا به v-bind اگرچه `42` استاتیک است، به -->
<!-- این یک عبارت جاوااسکریپتی است نه یک رشته                      -->
<BlogPost :likes="42" />

<!-- نسبت دادن به یک متغیر بصورت داینامیک -->
<BlogPost :likes="post.likes" />

Boolean

template
<!-- است `true` نوشتن تنها نام پراپ بدون مقدار به معنی مقدار -->
<BlogPost is-published />

<!-- بگوییم Vue نیاز داریم تا به v-bind استاتیک است، به `false` اگرچه -->
<!-- یک عبارت جاوااسکریپتی است نه یک رشته                             -->
<BlogPost :is-published="false" />

<!-- نسبت دادن به یک متغیر بصورت داینامیک -->
<BlogPost :is-published="post.isPublished" />

Array

template
<!-- بگوییم Vue نیاز داریم تا به v-bind اگرچه یک آرایه استایتک است اما به -->
<!-- یک عبارت جاوااسکریپتی است نه یک رشته                                 -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- نسبت دادن به یک متغیر بصورت داینامیک -->
<BlogPost :comment-ids="post.commentIds" />

Object

template
<!-- بگوییم Vue نیاز داریم تا به v-bind اگرچه یک آبجکت استایتک است اما به -->
<!-- یک عبارت جاوااسکریپتی است نه یک رشته                                 -->
<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: 'My Journey with Vue'
      }
    }
  }
}
js
const post = {
  id: 1,
  title: 'My Journey with Vue'
}

کد زیر:

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

معادل است با:

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

جریان داده یک طرفه

همه پراپ‌ها دارای اتصال one-way-down بین پراپرتی کامپوننت فرزند و والد هستند: وقتی پراپرتی کامپوننت والد آپدیت می‌شود، باعث آپدیت شدن کامپوننت فرزند می‌شود، اما عکس این اتفاق نمی‌افتد. این از آپدیت کردن تصادفی state والد، که باعث می‌شود جریان انتقال داده در برنامه سخت‌تر قابل فهم شود، در کامپوننت فرزند جلوگیری می‌کند.

به علاوه، هربار که کامپوننت والد آپدیت می‌شود، تمام پراپ‌ها در کامپوننت فرزند به آخرین و جدیدترین مقدار آپدیت می‌شوند. این به این معناست که نباید سعی به آپدیت کردن پراپ درون کامپوننت فرزند کرد، اگر این کار را انجام دهید، Vue در کنسول یک هشدار خواهد داد:

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

// ❌ warning, props are readonly!
props.foo = 'bar'
js
export default {
  props: ['foo'],
  created() {
    // ❌ warning, props are readonly!
    this.foo = 'bar'
  }
}

معمولا در دو حالت است که می‌خواهیم یک پراپ را mutate یا مقدار آن را آپدیت کنیم:

  1. از پراپ برای مقدار دهی اولیه استفاده می‌شود؛ کامپوننت فرزند می‌خواهد از پراپ به عنوان داده‌ی محلی خودش استفاده کند. در این حالت بهترین راه این است که یک متغیر محلی تعریف کرده و مقدار اولیه‌اش را به عنوان مقدار اولیه به پراپی که می‌خواهیم اساین کنیم:

    js
    const props = defineProps(['initialCounter'])
    
    // فقط برای مقداردهی اولیه استفاده می‌کند props.initialCounter از counter
    // و از بروزرسانی‌های پراپ در آینده جدا می‌شود
    const counter = ref(props.initialCounter)
    js
    export default {
      props: ['initialCounter'],
      data() {
        return {
          // فقط برای مقداردهی اولیه استفاده می‌کند this.initialCounter از counter
          // و از بروزرسانی‌های پراپ در آینده جدا می‌شود
          counter: this.initialCounter
        }
      }
    }
  2. پراپی که به کامپوننت پاس داده شده است، به خودی خود قابل استفاده نیست و نیازمند اعمال تغییرات است. در این صورت، بهترین کار استفاده کردن از computed است:

    js
    const props = defineProps(['size'])
    
    // بصورت خودکار آپدیت می‌شود props.size با هربار آپدیت شدن computed
    const normalizedSize = computed(() => props.size.trim().toLowerCase())
    js
    export default {
      props: ['size'],
      computed: {
        // با هربار آپدیت شدن پراپ بصورت خودکار آپدیت می‌شود computed
        normalizedSize() {
          return this.size.trim().toLowerCase()
        }
      }
    }

آپدیت کردن پراپ‌های آبجکت / آرایه

وقتی آبجکت یا آرایه به عنوان پراپ پاس داده می‌شوند، درحالی که کامپوننت فرزند نمی‌تواند پراپ‌ها را آپدیت کند، اما می‌تواند پراپرتی‌های آبجکت‌ها یا آرایه‌ها را آپدیت کند. دلیل این موضوع این است که در جاوااسکریپت آبجکت‌ها و آرایه‌ها passed by reference هستند، و به دیلی نامعقولی هزینه‌ی زیادی برای Vue دارد که از چنین آپدیت هایی جلوگیری کند.

اصلی ترین عیب چنین آپدیت‌هایی این است که اجازه می‌دهد دیتا و استیت کامپوننت والد را به گونه‌ای تغییر کند که برای کامپوننت والد مشخص نیست، و به احتمال زیاد باعث سخت‌تر شدن پیگیری و تشخیص جریان داده (data flow) در آینده می‌شود. بهترین راهکار این است که تا حد امکان از آپدیت‌هایی که به این صورت هستند دوری کرد مگر اینکه کامپوننت‌های والد و فرزند نزدیکی زیادی باهم داشته باشند. در اکثر مواقع، کامپوننت فرزند باید یک event را emit کند تا به کامپوننت والد بگوید آپدیت را ممکن سازد.

اعتبارسنجی پراپ‌ها

کامپوننت‌ها می‌توانند نیازمندی‌هایی را برای پراپ ها مشخص کنند، به عنوان مثلا تایپ پراپ که در بالاتر به آن اشاره کردیم. اگر نیازمندی به درستی مشخص نشده باشد Vue در کنسول مرورگر هشدار خواهد داد. هنگامی که یک کامپوننت را توسعه می‌دهیم که تا دیگر توسعه‌دهندگان از آن استفاده کنند، این ویژگی به شدت مفید خواهد بود.

برای مشخص کردن نیازمندی‌های یک پراپ به جای آرایه‌ای از رشته‌ها، می‌توان یک آبجکت با اعتبارسنجی از نیازمندی ها به ماکروی defineProps()‎ آپشن props پاس داد.

js
defineProps({
  // بررسی تایپ ساده
  // `null` و `undefined` اجازه هر تایپی را می دهند
  propA: Number,
  // چندین تایپ ممکن
  propB: [String, Number],
  // تایپ رشته و الزامی
  propC: {
    type: String,
    required: true
  },
  // باشد null استرینگِ الزامی ولی می‌تواند
  propD: {
    type: [String, null],
    required: true
  },
  // تایپ عدد با مقدار پیش فرض
  propE: {
    type: Number,
    default: 100
  },
  // تایپ آبجکت با مقدار پیش فرض
  propF: {
    type: Object,
    // آبجکت یا آرایه با مقدار پیش فرض باید از طریق
    // برگردانده شود factory function
    // پراپ توسط کامپوننت به عنوان آرگومان دریافت می‌شود.
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // تابع اعتبارسنجی سفارشی
  // تمام پراپ‌ها به عنوان آرگومان دوم در 3.4+ پاس داده می‌شوند
  propG: {
    validator(value, props) {
      // باید یکی از سه مقدار زیر باشد value
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // تابع با مقدار پیش فرض
  propH: {
    type: Function,
    // یک تابع سازنده نیست `default` برخلاف آبجکت یا آرایه
    // این خود این تابع به عنوان یک مقدار پیش فرض به شمار می‌رود
    default() {
      return 'Default function'
    }
  }
})

نکته

کد داخل defineProps()‎ به بقیه متغیر های تعریف شده در <script setup> دسترسی ندارد، به این دلیل که این عبارت هنگام کامپایل به یک تابع خارجی منتقل می شود.

js
export default {
  props: {
    // بررسی تایپ ساده
    // `null` و `undefined` اجازه هر تایپی را می دهند
    propA: Number,
    // چندین تایپ ممکن
    propB: [String, Number],
    // تایپ رشته و الزامی
    propC: {
      type: String,
      required: true
    },
    // باشد null استرینگِ الزامی ولی می‌تواند
    propD: {
      type: [String, null],
      required: true
    },
    // تایپ عدد با مقدار پیش فرض
    propE: {
      type: Number,
      default: 100
    },
    // تایپ آبجکت با مقدار پیش فرض
    propF: {
      type: Object,
      // آبجکت یا آرایه با مقدار پیش فرض باید از طریق
      // برگردانده شود factory function
      // پراپ توسط کامپوننت به عنوان آرگومان دریافت می‌شود
      default(rawProps) {
        return { message: 'hello' }
      }
    },
    // تابع اعتبارسنجی سفارشی
    // تمام پراپ‌ها به عنوان آرگومان دوم در 3.4+ پاس داده می‌شوند
    propG: {
      validator(value, props) {
        // باید یکی از سه مقدار زیر باشد value
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // تابع با مقدار پیش فرض
    propH: {
      type: Function,
      // یک تابع سازنده نیست `default` برخلاف آبجکت یا آرایه
      // این خود این تابع به عنوان یک مقدار پیش فرض به شمار می‌رود
      default() {
        return 'Default function'
      }
    }
  }
}

توضیحات بیشتر:

  • بطور پیش فرض، دریافت همه پراپ‌ها اختیاری است، مگر اینکه required: true مشخص شده باشد.

  • پراپی که تایپ آن boolean نباشد، در صورت عدم داشتن مقدار، مقدار undefiend خواهند داشت.

  • عدم مقداردهی پراپ با تایپ boolean مقدار false را خواهد داشت. می‌توان این تنظیمات را با مقداردهی default تغییر داد. برای مثال: default: undefined تا رفتاری مانند تایپ‌های non-Boolean داشته باشد.

  • اگر مقدار پراپ undefiend باشد و مقدار default مشخص شده باشد، از آن استفاده خواهد شد. این مورد شامل شرایطی است که پراپ مقدار دهی نشده است یا مقدار آن undefined پاس داده شده است.

هنگامی که اعتبار سنجی prop ناموفق باشد، Vue یک هشدار کنسول (در صورت بودن در محیط توسعه) تولید می‌کند.

اگر از Type-based props declarations استفاده می‌کنید، Vue سعی می‌کند تا به بهترین شکل ممکن type annotaions را با معادل runtime prop declarations :معادل سازی کند. برای مثال defineProps<{ msg: string }>‎ بعد از کامپایل به { msg: { type: String, required: true }} تبدیل می‌شود.

توجه

توجه داشته باشید که props قبل از ایجاد یک نمونه از کامپوننت تایید می‌شود، بنابراین ویژگی‌های نمونه کامپوننت (مانند "data"، "computed" و غیره) در توابع "default" یا "validator" در دسترس نخواهند بود.

Runtime Type Checks

تایپ می‌تواند یکی از تایپ‌های زیر باشد:

  • 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

اگر به تایپ نیاز دارید اما متغیر قابل null شدن است، می‌توانید از دستور آرایه استفاده کنید که شامل null می‌شود:

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

توجه داشته باشید که اگر type فقط null باشد بدون استفاده از دستور آرایه، هر تایپی را مجاز می‌کند.

تبدیل تایپ داده منطقی (Boolean casting)

پراپ‌ها با تایپ داده منطقی (boolean) قانون تبدیل خاصی دارند تا بتوانند مدل رفتاری اتریبیوت‌های بومی مرورگر را تقلید کنند.

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

کامپوننت به این صورت قابل استفاده است:

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

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

هنگامی که یک برای یک پراپ چندین نوع تعریف کرده‌ایم، قوانین تبدیل نوع داده برای Boolean هم اعمال خواهد شد. اگرچه هنگام استفاده همزمان از دو نوع منطقی (boolean) و رشته (string) یک نکته وجود دارد، قانون تبدیل نوع داده منطقی فقط زمانی اعمال می شود که ابتدا نوع داده منطقی (boolean) قبل از رشته (string) ذکر شود.

js
// true تبدیل می‌شود به disabled
defineProps({
  disabled: [Boolean, Number]
})

// true تبدیل می‌شود به disabled
defineProps({
  disabled: [Boolean, String]
})

// true تبدیل می‌شود به disabled
defineProps({
  disabled: [Number, Boolean]
})

// به یک رشته خالی تبدیل می‌شود disabled
defineProps({
  disabled: [String, Boolean]
})
js
// true تبدیل می‌شود به disabled
export default {
  props: {
    disabled: [Boolean, Number]
  }
}

// true تبدیل می‌شود به disabled
export default {
  props: {
    disabled: [Boolean, String]
  }
}

// true تبدیل می‌شود به disabled
export default {
  props: {
    disabled: [Number, Boolean]
  }
}

// disabled will be parsed as an empty string (disabled="")
export default {
  props: {
    disabled: [String, Boolean]
  }
}
پراپ‌ها has loaded