اصول پایه Component

اصول آموزش vuejs

تاریخ : جمعه 19 مهر 1398

مثال پایه (Base Example)

در این قسمت یک مثال از یک کامپوننت را مشاهده می کنید:

// Define a new component called button-counter
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked met {{ count }} times.</button>'
})

کامپوننت ها با نام خود قابلیت استفاده مجدد در نمونه های vue را دارند. در این مثال <button-counter> .می توانیم از این کامپوننت به عنوان یک عنصر سفارشی در نمونه ایجاد شده Vue استفاده کنیم :

<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })

از آنجا که کامپوننت ها قابلیت استفاده مجدد در نمونه های Vue را دارا می باشند، آنها تعدادی خاصیت را در یک new Vue همانند data, computed, watch, methods و lifecycle hooks را می پذیرند.


استقاده مجدد از کامپوننت ها (Reusing Components)

کامپوننت ها را می توانید هربار که بخواهید استفاده نمایید :

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

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

خاصیت data باید بصورت تابع پیاده سازی شود (data Must Be a Function)

وقتی ما عنصر <button-counter> را تعریف کردیم ، ممکن است متوجه شده باشید که داده ها به طور مستقیم یک شی را ارائه نمی دهند ، مانند این:

data: {
  count: 0
}

در عوض ، خاصیت data یک کامپوننت باید یک تابع باشد ، به طوری که هر نمونه می تواند یک نسخه مستقل از شی داده داده شده را حفظ کند:

data: function () {
  return {
    count: 0
  }
}

اگر Vue این قانون را نداشت ، کلیک کردن بر روی یک دکمه می توانست بر داده های سایر موارد دیگر تأثیر بگذارد.


سازماندهی کامپوننت ها (Organizing Components)

معمولا برنامه ها به صورت قطعات و اجزای تو در تو و درختی پیاده سازی می شوند :

ساختار app

به عنوان مثال ، شما ممکن است کامپوننت هایی برای header ، sidebar و همچنین نمایش محتوا داشته باشید که معمولاً خود شامل اجزای دیگری همانند لینک ها، پست های وبلاگ و غیره است.

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

Vue.component('my-component-name', {
  // ... options ...
})

کامپوننت هایی که بصورت سراسری ایجاد می شوند، می توانند در قالب نمونه های اصلی (new Vue) ایجاد شده و حتی در داخل همه subcomponents مورد استفاده قرار گیرند.


انتقال داده به کامپوننت ها با استفاده از Props(Passing Data to Child Components with Props)

در ابتدا ، ما به ایجاد یک کامپوننت برای نمایش پست های وبلاگ اشاره کردیم. جهت انتقال داده هایی همانند عنوان و محتوای پست به کامپوننت، می توانیم از Props استفاده نماییم.

Props ویژگی های سفارشی هستند که می توانید روی یک کامپوننت ثبت نمایید.

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

یک کامپوننت می تواند دارای تعداد زیادی props باشد و مقادیر می توانند به props انتقال یابند. در قالب بالا ، خواهید دید که ما می توانیم دقیقاً به این مقادیر همانند data دسترسی داشته باشیم.

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

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

همچنین چنانچه مجموعه داده هایی بدین صورت داشته باشیم:

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})

سپس جهت رند کامپوننت برای هر یک از آنها بدین صورت پیاده سازی می کنیم:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>

در مثال بالا مشاهده کردید که می توانیم از v-bind جهت انتقال مقادیر بصورت داینامیک استفاده کنیم. این امر به ویژه هنگامی مفید است که شما محتوای دقیقی که می خواهید ارائه دهید را نمی دانید ، مانند هنگام ارسال پیامک از یک API.


بررسی قالب های چند خطی و المان ریشه (A Single Root Element)

هنگام ساخت کامپوننت <blog-post> در مثال قبل، قالب شما حاوی مواردی غیر از عنوان همانند محتوا ، تاریخ ایجاد پست و ... می باشد :

<h3>{{ title }}</h3>
<div v-html="content"></div>

اگر این مورد را در الگوی خود امتحان کنید ، Vue خطایی با متن هر مؤلفه باید یک عنصر ریشه واحد داشته باشد را نمایش خواهد داد. می توانید با قرار دادن قالب خود در یک المان والد مشکل را رفع نمایید :

<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>

با بزرگ شدن کامپوننت ، به احتمال زیاد به غیر از عنوان و محتوای یک پست به نمایش عناصر دیگری همچون تاریخ انتشار ، نظرات و .. نیز احتیاج خواهیم داشت. تعریف prop برای هر قطعه از اطلاعات مرتبط می تواند بسیار آزار دهنده باشد:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
  v-bind:content="post.content"
  v-bind:publishedAt="post.publishedAt"
  v-bind:comments="post.comments"
></blog-post>

بنابراین می توانیم کامپوننت بالا را مجددا دوباره سازی کرده تا تنها نیاز به تعریف یک prop داشته باشیم :

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})
مثال فوق و برخی مثال ها در آینده از template literal جاوا اسکریپت جهت خواندن قالب های چند خطی استفاده می نمایند که توسط Internet Explorer (IE) پشتیبانی نمی شوند ، چنانچه بخواهیم در IE پشتیبانی شود از newline escapes استفاده نمایید.


اجرای رویدادهای کامپوننت های فرزند (Listening to Child Components Events)

هنگامی که کامپوننت <blog-post> را توسعه می دهیم ، برخی از ویژگیها ممکن است نیاز به برقراری ارتباط با والدین داشته باشند. به عنوان مثال ، ممکن است تصمیم بگیریم قابلیت بزرگتر کردن متن پست های وبلاگ را اضافه نماییم :

می توانیم با اضافه کردن خاصیت postFontSize به data در والد، این عمل را انجام نماییم :

new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* ... */],
    postFontSize: 1
  }
})
<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
    ></blog-post>
  </div>
</div>

اکنون بیایید یک دکمه برای بزرگنمایی متن درست قبل از محتوای هر پست اضافه کنیم:

Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button>
        Enlarge text
      </button>
      <div v-html="post.content"></div>
    </div>
  `
})

مشکل این است که این دکمه کاری نمی کند:

<button>
  Enlarge text
</button>

زمانیکه روی دکمه کلیک می کنیم ، باید به والدین ارتباط برقرار کرده تا متن تمام پست ها را بزرگتر کند. خوشبختانه ، نمونه های Vue سیستم رویدادهای سفارشی را برای حل این مشکل ارائه می دهند. کامپوننت والد می تواند با استفاده از دایرکتیو v-on انتخاب نماید که در بین رویدادها در نمونه کامپوننت فرزند به کدامیک گوش دهد. دقیقاً همانطور که با یک رویداد محلی DOM انجام می دهیم:

<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>

سپس کامپوننت فرزند می تواند با فرخوانی متد emit$، نام رویداد را منتقل می کند:

<button v-on:click="$emit('enlarge-text')">
  Enlarge text
</button>

کامپوننت والد با استفاده از v-on :large-text = "postFontSize + = 0.1" این رویداد را دریافت کرده و مقدار postFontSize را به روز می کنند.

انتشار یک مقدار با رویداد (Emitting a Value With an Event)

انتشار مقادیر خاص با یک رویداد گاهی اوقات مفید است. به عنوان مثال ، ممکن است بخواهیم کامپوننت <blog-post> مسئولیت این را داشته باشد که متن را تا چه اندازه بزرگنمایی کند. در این موارد ، ما می توانیم از دومین پارامتر متد emit$ استفاده نماییم:

<button v-on:click="$emit('enlarge-text', 0.1)">
  Enlarge text
</button>

زمانیکه ما به رویداد در والد گوش فرا می دهیم ، می توانیم به مقدار رویداد با استفاده از event$ دسترسی داشته باشیم :

<blog-post
  ...
  v-on:enlarge-text="postFontSize += $event"
></blog-post>

یا اگر کنترل کننده رویداد یک متد باشد:

<blog-post
  ...
  v-on:enlarge-text="onEnlargeText"
></blog-post>

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

new Vue({
        el: '#blog-posts-events-demo',
        data: {
            posts: [/* ... */],
            postFontSize : 1
        },
        methods: {
            onEnlargeText: function (enlargeAmount) {
                this.postFontSize += enlargeAmount
            }
        }
    })


استفاده از v-model (Using v-model on Components)

رویدادهای سفارشی همچنین می توانند در ایجاد ورودی های سفارشی که با v-model کار می کنند استفاده شوند. به خاطر دارید که :

<input v-model="searchText">

همان کاری را انجام می دهد که:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

در صورت استفاده از v-model در کامپوننت چنین عمل می کند :

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

جهت اجرای کار input باید چنین عمل کند :

  • مقید کردن مقدار خاصیت به مقدار prop
  • در ورودی ، رویداد ورودی سفارشی خود را با مقدار جدید منتشر کنید

در عمل چنین خواهیم داشت :

Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})

اکنون v-model باید با این کامپوننت کاملاً کار کند:

<custom-input v-model="searchText"></custom-input>


انتشار محتوا با Slots (Content Distribution with Slots)

درست همانند عناصر HTML ، بسیار مفید است که بتوانید محتوای را به یک کامپوننت منتقل کنید ، مانند زیر:

<alert-box>
  Something bad happened.
</alert-box>

که ممکن است چیزی مانند زیر باشد:

خوشبختانه ، این کار توسط عنصر <slot> سفارشی Vue انجام شده است:

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

همانطور که در بالا مشاهده میکنید ، ما فقط slot را جایی که می خواهیم آن را استفاده کنیم قرار می دهیم و تمام !

Something bad happened.


نکاتی در عملیات تجزیه قالب (DOM Template Parsing Caveats)

برخی از عناصر HTML مانند <ul> ، <ol> ، <table> و <select> محدودیت هایی در مورد عناصر موجود در داخل آنها دارند و برخی از عناصر مانند <li> ، <tr> و <option> فقط می توانند در داخل بعضی از عناصر دیگر ظاهر شوندد.

این مسئله هنگام استفاده از کامپوننت هایی با چنین عناصری محدودیت هایی به دنبال خواهد داشت. مثلا:

<table>
  <blog-post-row></blog-post-row>
</table>

کامپوننت سفارشی <blog-post-row> به عنوان محتوای نامعتبر حذف می شود و باعث ایجاد خطا در خروجی نهایی ارائه شده می شود. خوشبختانه ، ویژگی ویژه is یک راه حل ارائه می دهد:

<table>
  <tr is="blog-post-row"></tr>
</table>

لازم به ذکر است در صورت استفاده از قالب های رشته از یکی از منابع زیر این محدودیت اعمال نمی شود:

  • String templates (e.g. template: '...')
  • Single-file (.vue) components
  • <script type="text/x-template">

منابع مورد مطالعه جهت جمع آوری این مطلب:
https://vuejs.org/v2/guide/components.html


نظرات