مرگ کامپوننت‌های پویا

Death of Dynamic Components

Vue.JS 2: مرگ کامپوننت های پویا - قسمت 41

در قسمت قبل با کامپوننت های پویا آشنا شدیم اما جزئیاتی راجع به آن ها بدون پاسخ مانده است و مهم ترین این جزئیات، مبحث رفتار <component> در مقابل تغییر است. اگر یادتان باشد در قسمت قبل سیستم ساده ای را برای تغییر کامپوننت ها چیده بودیم. در قدم اول در فایل App.vue کد زیر را داشتیم:

<template>
  <div class="container">
    <div class="row">
      <div class="col-xs-12"></div>
      <button @click="selectedComponent = 'appQuote'">Quote</button>
      <button @click="selectedComponent = 'appAuthor'">Author</button>
      <button @click="selectedComponent = 'appNew'">New</button>
      <hr />
      <p>{{ selectedComponent }}</p>
      <component :is="selectedComponent">
        <p>Default Content</p>
      </component>
    </div>
  </div>
</template>

یعنی خصوصیتی به نام selectedComponent وجود دارد که از طریق کلیک روی یکی از سه دکمه بالا تغییر خواهد کرد. هر سه کامپوننت appQuote و appAuthor و appNew را در جلسه قبل ساخته بودیم. مقدار <p> که در بین تگ های <component> قرار گرفته است مقدار پیش فرض ما می باشد تا با بارگذاری برنامه در دفعه اول، چیزی برای نمایش داشته باشیم.

ما باید در چنین برنامه ای متوجه مسئله مهمی باشیم: اگر برنامه را در مرورگر باز کنیم، کامپوننت appQuote (فایل Quote.vue) به صورت پیش فرض باز می شود. حالا اگر روی دکمه Author کلیک کنیم، کامپوننت appAuthor (فایل Author.vue) باز می شود. سوال اینجاست که آیا اگر دوباره روی دکمه Quote کلیک کنیم، کامپوننت appQuote از ابتدا ساخته می شود یا از همان نسخه ای که از قبل آماده شده بود استفاده می شود؟ آیا با کلیک روی هر کدام از این دکمه ها و تغییر کامپوننت ها، کامپوننت قبلی از بین می رود یا در پس زمینه باقی می ماند؟ آیا مسئله مخفی شدن کامپوننت است یا به طور کل از بین رفتن آن؟

برای پی بردن به این سوال باید آن را تست کنیم. وارد فایل New.vue شده و یک خصوصیت جدید در data اضافه کنید. این خصوصیت باید یک عدد را در خود نگه دارد (ما نامش را counter یا شمارنده می گذاریم) و سپس دکمه جدیدی داشته باشیم که به مقدار این خصوصیت اضافه کند. نقشه من این است که مرورگر را باز کنیم و روی این دکمه کلیک کنیم تا عدد شمارنده بالا برود. حالا به یک کامپوننت دیگر رفته و دوباره به کامپوننت New برگردیم. اگر با تغییر کامپوننت ها، کامپوننت قبلی نابود شود، عدد شمارنده دوباره صفر می شود (چرا که state برنامه از بین می رود و همه چیز از اول ساخته می شود) اما اگر از همان کامپوننت قبلی استفاده شود، عدد شمارنده نباید تغییر کند.

برای این کار ابتدا counter را به عنوان یک خصوصیت در این فایل اضافه می کنم:

<script>
export default {
  data: function() {
    return {
      counter: 0
    };
  }
};
</script>

حالا باید در قسمت Template دکمه ای را تعریف کنم که با کلیک روی آن مقدار این شمارنده اضافه شود. همچنین باید خود counter را نیز چاپ کنیم تا بتوانیم تغییر مقدار آن را مشاهده کنیم:

<template>
  <div>
    <h3>New Quote</h3>
    <button @click="counter++">Increase!</button>
    <p>{{ counter }}</p>
  </div>
</template>

حالا کدها را ذخیره کرده (npm run dev در حال اجرا باشد) و به مرورگر بروید. ابتدا روی دکمه New کلیک کنید تا کامپوننت New باز شود. حالا چند بار روی دکمه Increase کلیک کنید تا به عدد شمارنده ما اضافه شود:

شمارنده ی من روی عدد 8 ایستاده است
شمارنده من روی عدد 8 ایستاده است

حالا روی دکمه Author یا Quote کلیک کنید تا به کامپوننت دیگری بروید. یادتان باشد که شمارنده ما روی عدد 8 بود (تصویر بالا). حالا اگر دوباره روی دکمه New کلیک کنید، شمارنده را روی عدد صفر می بینید:

شمارنده ی من تبدیل به صفر شده است
شمارنده من تبدیل به صفر شده است

بنابراین نتیجه می گیریم که کامپوننت ها به طور کامل نابود شده و دوباره ساخته می شوند. روش دیگر برای تست کردن این موضوع استفاده از lifecycle hook ای به نام destroyed است که در جلسات قبل با آن آشنا شدیم. ما می دانیم که اگر کامپوننتی از بین برود، دقیقا قبل از اینکه به طور کامل نابود شود، متد destroyed را صدا خواهد زد، بنابراین به فایل New.vue رفته از این lifecycle استفاده می کنیم:

<script>
export default {
  data: function() {
    return {
      counter: 0
    };
  },
  destroyed() {
    console.log("Destroyed!");
  }
};
</script>

باز هم تکرار می کنم که ما destroyed را درون methods تعریف نکرده ایم. حالا اگر به مرورگر رفته و قسمت console را باز کنیم، با تغییر دادن کامپوننت ها از New به هر کامپوننت دیگر، شاهد عبارت Destroyed خواهیم بود.

سوالی که باقی می ماند این است: آیا ما می توانیم کاری کنیم که کامپوننت ها در چنین حالتی نابود نشوند؟ پاسخ این سوال مثبت است. ما در Vue کامپوننت خاصی به نام keep-alive را داریم که مسئول همین کار است. باز هم باید بگویم که keep-alive یک نام رزرو شده در Vue است بنابراین نمی توانید نام کامپوننت های خودتان را keep-alive بگذارید. نحوه استفاده از این کامپوننت بدین شکل است: وارد فایل App.vue شده و سپس کامپوننت پویای <component> را درون آن قرار دهید.

<template>
  <div class="container">
    <div class="row">
      <div class="col-xs-12"></div>
      <button @click="selectedComponent = 'appQuote'">Quote</button>
      <button @click="selectedComponent = 'appAuthor'">Author</button>
      <button @click="selectedComponent = 'appNew'">New</button>
      <hr />
      <p>{{ selectedComponent }}</p>
      <keep-alive>
        <component :is="selectedComponent">
          <p>Default Content</p>
        </component>
      </keep-alive>
      <!-- <app-quote>
        <h2 slot="title">{{ quoteTitle }}</h2>
        <p>A wonderful quote</p>
      </app-quote>-->
    </div>
  </div>
</template>

همانطور که می بینید نحوه استفاده از این کامپوننت بسیار ساده است. حالا تست قدیمی خودمان را دوباره امتحان می کنیم. کدها را ذخیره کرده (npm run dev در حال اجرا باشد) و به مرورگر بروید. ابتدا روی دکمه New کلیک کنید تا کامپوننت New باز شود. حالا چند بار روی دکمه Increase کلیک کنید تا به عدد شمارنده ما اضافه شود (من آن را روی 8 می گذارم). سپس روی دکمه Author یا Quote کلیک کنید تا به کامپوننت دیگری بروید. یادتان باشد که شمارنده ما روی عدد 8 بود. حالا اگر دوباره روی دکمه New کلیک کنید، شمارنده هیچ تغییری نکرده است! بنابراین کامپوننت ما از بین نرفته است. همچنین اگر کنسول مرورگر را ببینید، عبارت Destroyed دیگر نمایش داده نمی شود.

اینجا سوال اساسی دیگری مطرح می شود. ما قبلا می توانستیم در destroyed نسبت به تغییر کامپوننت ها واکنش نشان دهیم (مثلا کدی را درون destroyed بنویسیم تا اگر کاربر کامپوننت ها را تغییر داد اجرا شود – حتی یک alert ساده) اما حالا که کامپوننت ها زنده هستند چه کار باید کرد؟ برای این کار دو lifecycle hook دیگر وجود دارد که دو مرحله خاص را کنترل می کنند:

  • آیا کامپوننت ما بارگذاری شده است (فعال شده است یا کسی روی دکمه مربوط به آن کلیک کرده است)؟ نام آن deactivated است.
  • آیا کامپوننت ما غیر فعال شده است (روی دکمه دیگری کلیک شده است و از کامپوننت ما خارج شده اند)؟ نام آن activated است.

بگذارید هر دو را امتحان کنیم. وارد فایل New.vue شده و هر دو lifecycle hook را برایش می نویسیم:

<script>
export default {
  data: function() {
    return {
      counter: 0
    };
  },
  destroyed() {
    console.log("Destroyed!");
  },
  deactivated() {
    console.log("Deactivated!");
  },
  activated() {
    console.log("Activated!");
  }
};
</script>

حالا به مرورگر می رویم و در صورتی که روی دکمه New کلیک کنیم (حتما مرورگر را یک بار refresh کنید) عبارت activated در کنسول مرورگر دیده می شود. چرا؟ به دلیل اینکه با کلیک روی New کامپوننت New.vue را بارگذاری کرده ایم بنابراین فعال شده است و به همین جهت lifecycle hook ای به نام activated برای آن اجرا می شود. سپس این بار روی یکی دیگر از کامپوننت ها (دکمه های Author یا Quote) کلیک کنید. با این کار عبارت Deactivated را مشاهده خواهید کرد!

مشاهده ی lifecycle های activated و deactivated
مشاهده lifecycle های activated و deactivated

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

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش رایگان Vue js از صفر تا صد توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مهدی
28 تیر 1400
سلام مرسی بابت مطلبت خیلی کاربردی و جذاب بود. فقط توی خطوط: ؛آیا کامپوننت ما بارگذاری شده است (فعال شده است یا کسی روی دکمه مربوط به آن کلیک کرده است)؟ نام آن deactivated است. آیا کامپوننت ما غیر فعال شده است (روی دکمه دیگری کلیک شده است و از کامپوننت ما خارج شده اند)؟ نام آن activated است.؛ اسم کامپونت ها را جابجا نوشتید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.