کامپوننت ها در VueJS

کامپوننت ها در VueJS

در این مقاله به بررسی استفاده از کامپوننت ها می‌پردازیم. ابتدا یک فایل HTML خالی مانند کد زیر ایجاد کرده و سعی می‌کنیم مرحله به مرحله با اِعمال تغییرات روی آن، به هدف خود برسیم.

<html>

<head>
  <title>use of components</title>
</head>

<body>
  <div id="app">

  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    const app = new Vue({
      el: '#app',
      data: {

      },
      methods: {

      }
    });
  </script>
</body>

</html>

کامپوننت ها بخشی مهمی از فریمورک Vue.js هستند، آن‌ها بلوک‌های شما برای ساختن برنامه‌های واکنش پذیر هستند که خوش بختانه برای ما، یادگیری و استفاده از آن‌ها بسیار ساده هستند!
یک کامپوننت را به عنوان هر عنصری در صفحه وب یا برنامه خود در نظر بگیرید که می خواهید یک یا چند بار از آن استفاده کنید. کامپوننت می‌تواند به اندازه ی یک دکمه، یک ورودی یا به اندازه ی کل نوار منوی شما و یا حتی کل نمای یک صفحه باشد.
مزیت ایجاد یک کامپوننت این است که شما منطق شیوه نمایش آن (HTML / CSS) و شیوه تعامل آن با کاربر (فقط JS) را یک بار می‌نویسید و سپس فقط از آن در سراسر برنامه خود و به هر تعداد که لازم داشتید، استفاده می‌کنید.
طبق معمول، ما با ساده‌تر ین مثال ممکن، یعنی یک دکمه شروع می‌کنیم. بیایید ابتدا طرح اولیه یا تمپلیت (template) این کامپوننت را تعریف کنیم و فرض کنید که اسم آن را awesome-button گذاشتیم. برای تعریف آن از Vue.component همانند کد زیر استفاده می‌کنیم:

Vue.component('awesome-button', {
      template: `<button @click="clickHandler">Click me for some awesomeness</button>`,
      methods: {
        clickHandler() {
          alert('Hello Vue.js');
        }
      }
    });


Vue از طریق CDN، که قبلاْ اضافه کرده‌ایم در دسترس ما قرار گرفته است، این CDN شامل کامپوننت هایی نیز می‌شود که ما در اینجا فراخوانی می‌کنیم. این موضوع به ما این امکان را می‌دهد که یک کامپوننت جدید را ایجاد کنیم. اولین پارامتری که برای کامپوننت موردنظر تنظیم می‌کنیم یک رشته است که اسم کامپوننت ما خواهد بود.
پارامتر دوم یک object ورودی JavaScript است، که در واقع نوعی object پیکربندی است که برای نمونه ی اصلی Vue استفاده می‌شود. این موضوع به این معنی است که یک template تمام ویژگی‌های یک نمونه Vue را داراست. به عبارتی هر کامپوننتی می‌تواند یک نمونه Vue شناخته شود.
در مثال بالا، درون template باید اِلِمان موردنظر را قرار داد که در اینجا یک button است. دقت شود که مقدار template را به صورت یک رشته تعریف کردیم که درون علامت `` (back-tick) قرار گرفته است. 
اگر همین حالا فایل HTML خود را در صفحه مرورگر باز کنید اتفاقی نمی‌افتد. یادتان باشد که کاری که تا الان انجام دادیم فقط ساخت یک template بود که آن را در صفحه خود ارائه ندادیم اما در ادامه شیوه استفاده از آن را خواهید فهمید. برای انجام این کار کافی است که به ابتدای تگ <div id=”app”></div> رفته و با توجه به اسمی که برای کامپوننت موردنظر انتخاب شده، آن را به کار برد. شیوه استفاده از آن در کد زیر نشان داده شده است.

  <div id="app">
    <awesome-button></awesome-button>
  </div>

حال اگر صفحه مرورگر را نگاه کنید دکمه ای مشابه دکمه موجود در عکس زیر را خواهید دید:

ابتدا یک آرایه ی جدید با اسم games را در ()data تعریف می‌کنیم. 

data: {
        games: [
          { name: 'Super Mario 64', console: 'Nintendo 64', rating: 4 },
          { name: 'The Legend of Zelda Ocarina of Time', console: 'Nintendo 64', rating: 5 },
          { name: 'Secret of Mana', console: 'Super Nintendo', rating: 4 },
          { name: 'Fallout 76', console: 'Multiple', rating: 1 },
          { name: 'Super Metroid', console: 'Super Nintendo', rating: 6 }
        ]
      }

برای استفاده کردن از این آرایه قصد داریم یک کامپوننت جدید به اسم game-card مانند کدی که در زیر نوشتیم، بسازیم. 

Vue.component('game-card', {
      props: ['gameData'],
      template: `
        <div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
          <h2>{{ gameData.name }} - <small>{{ gameData.console }}</small></h2>

          <span v-for="heart in gameData.rating">❤️</span>

          <button @click="increaseRating">Increase Rating</button>
        </div>
      `,
      methods: {
        increaseRating() {
          // this.game.rating++;
        }
      }
    });

شیوه استفاده از این کامپوننت هم مانند مثال قبل است با این تفاوت که برای نمایش اطلاعات مورد نیاز باید از حلقه v-for همانند کد زیر استفاده کرد.

<game-card v-for="game in games" :key="game.name"></game-card>

اگر دقت کنید پس از استفاده از این کد، همچنان هیچ خروجی مشخصی قابل‌مشاهده نیست. دلیل آن نیز این است که داده‌های موجود در آرایه games از طریق یک props به تگ های داخلی کامپوننت ارجاع داده شده است. اما ما هنوز در کد خود از این props استفاده نکرده‌ایم.


Props های یک کامپوننت


در واقع props به ما کمک می‌کنند تا داده‌ها را از طریق یک کامپوننت parent به کامپوننت های داخلی آن (child) منتقل کنیم و از آن‌ها استفاده کنیم. در حلقه v-for ایجاد شده روی کامپوننت، هر آبجکت از آرایه games را درون متغیر game می ریزیم اما در کدهای نوشته شده هیچ گونه استفاده ای از این متغیر نکردیم در واقع قصد داریم این متغیر را به صورت یک props برای کامپوننت تعریف کنیم. برای استفاده از props کافی است مانند کد زیر با استفاده از یک اسم دلخواه (که در اینجا game-data است) داده‌های موردنظر را روی کامپوننت تعریف کرد. 

<game-card v-for="game in games" :game-data="game" :key="game.name"></game-card>

همان‌طور که دیدید game را به عنوان یک props با اسم game-data برای کامپوننت game-card تعریف کردیم. دقت کنید که در اینجا به دلیل این کهHTML، یک case insensitive است برای اسم props از kebab case به جای camel case استفاده کردیم.

زمانی که ما یک property را به یک نمونه vue اختصاص می‌دهیم معمولاً property موردنظر را به صورت یک رشته تعریف می‌کنیم. حال اگر این property یک مقدار متغیر داشته باشد (مانند کد بالا) باید قبل از آن از علامت ‘:’ استفاده کنیم ( علامت ‘:’ در واقع کوتاه شده ی ‘:v-bind’ است). 

Data‌ و Props برای یک کامپوننت


در واقع Props ها می‌توانند موضوع پیچیده ای برای کامپوننت ها باشند اما یک قانون مهم و اساسی وجود دارد که باید همیشه آن را به خاطر بسپارید که یک property هرگز نباید از داخل یک کامپوننت تغییر داده شود. در حقیقت، اگر سعی کنید این کار را انجام دهید Vue انواع اخطارها را در کنسول به شما نشان می‌دهد.
اگر دقت کرده باشید ما یک متد برای کامپوننت game-card تعریف کردیم که اسم آن ()increaseRating بود. در این متد قصد داریم که عدد rating را افزایش دهیم. اما همان‌طور که گفته شد نمی‌توان محتوای داخلی یک props را در داخل کامپوننت تغییر داد بنابراین اگر کد ++this.game.rating را از حالت کامنت خارج کنیم با زدن روی دکمه ‘Increase Rating’ مقدار rating نه تنها افزایش پیدا نکرده بلکه در کنسول مرورگر خطای زیر را نیز مشاهده خواهیم کرد.

 برای رفع چنین خطایی بهتر است ابتدا اطلاعات موجود در props را درون یک متغیر جدید ریخته و سپس از این متغیر درون کامپوننت استفاده کرد. با این روش، دیگر مشکلی برای تغییر مقادیر props وجود ندارد. فقط دقت کنید که اسم انتخاب شده برای متغیر جدید نباید با اسم props یکی باشد. کد زیر را  درون کامپوننت game-card تعریف کنید و در متد ()increaseRating به جای game از متغیر تعریف شده جدید (newGameData) استفاده کنید.

            data() {
        return {
          newGameData: {...this.gameData}
        }
      },

همان‌طور که مشاهده می‌کنید دیگر خبری از خطای کنسول نیست زیرا همه ی  مقادیر موجود در props را در متغیر جدید ریختیم و در متد تعریف شده نیز از این متغیر مانند کد زیر استفاده کردیم. اما قبل از نوشتن ادامه ی کدها، بهتر است دلیل قرار دادن  ‘…’قبل از this.gameData را توضیح دهم. این علامت در واقع spread operator نامیده می‌شود که به این معناست که همه اطلاعات موجود در this.gameData درون newGameData کپی می‌شوند. 

increaseRating() {
   this.newGameData.rating++;
}


دقت شود که ما هنوز HTML خود را به‌روزرسانی نکردیم به همین دلیل است که با کلیک کردن روی دکمه افزایش rating هیچ اتفاقی نمی‌افتد. برای به‌روزرسانی کردن HTML باید درون template به جای props تعریف شده (gameData) از متغیری جدیدی که تعریف کردیم، استفاده کرد. به عبارتی به جای gameData باید newGameData را مانند کد زیر قرار دهیم.

<div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
      <h2>{{ newGameData.name }} - <small>{{ newGameData.console }}</small></h2>
      <span v-for="heart in newGameData.rating">❤️</span>
      <button @click="increaseRating">Increase Rating</button>
</div>

حالا اگر روی دکمه ی موردنظر کلیک کنید خواهید دید که با هر کلیک تعداد قلب ها افزایش می‌یابد. البته دقت کنید که تعداد قلب ها درون props تغییری نمی‌کند و فقط تعداد آن‌ها در متغیر newGameData که یک کپی از props است افزایش پیدا می‌کند. 

تمام کدی که در این مقاله با هم زدیم به صورت زیر است. امیدوارم به اندازه ی کافی شما را با کاربرد کامپوننت ها آشنا کرده باشم. منتظر مقالات بعدی ما باشید.

<html>

<head>
  <title>use of components</title>
</head>

<body>

  <div id="app">
    <awesome-button></awesome-button>

    <game-card v-for="game in games" :game-data="game" :key="game.name"></game-card>
    
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    Vue.component('awesome-button', {
      template: `<button @click="clickHandler">Click me for some awesomeness</button>`,
      methods: {
        clickHandler() {
          alert('Hello Vue.js');
        }
      }
    });
    Vue.component('game-card', {
      props: ['gameData'],
      template: `
        <div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
          <h2>{{ newGameData.name }} - <small>{{ newGameData.console }}</small></h2>

          <span v-for="heart in newGameData.rating">❤️</span>

          <button @click="increaseRating">Increase Rating</button>
        </div>
      `,
      data() {
        return {
          newGameData: {...this.gameData}
        }
      },
      methods: {
        increaseRating() {
          this.newGameData.rating++;
        }
      }
    });
    const app = new Vue({
      el: '#app',
      data: {
        games: [
          { name: 'Super Mario 64', console: 'Nintendo 64', rating: 4 },
          { name: 'The Legend of Zelda Ocarina of Time', console: 'Nintendo 64', rating: 5 },
          { name: 'Secret of Mana', console: 'Super Nintendo', rating: 4 },
          { name: 'Fallout 76', console: 'Multiple', rating: 1 },
          { name: 'Super Metroid', console: 'Super Nintendo', rating: 6 }
        ]
      }
    });
  </script>
</body>

</html>
دوره در دست تالیف است ... rocket
نظرات
اگر login نکردی برامون ایمیلت رو بنویس: