واکنش به تغییرات URL و پارامترهای آن

Reacting to URL Changes and Parameters

25 آبان 1399
Vue.JS 2: واکنش به تغییرات URL و پارامتر های آن - قسمت 80

در قسمت قبل به این مبحث رسیدیم که می توانیم مقداری (id) را به صورت پویا دریافت کنیم:

<script>
export default {
  data () {
    return {
      id: this.$route.params.id
    }
  },
  methods: {
    navigateToHome() {
      this.$router.push('/');
    }
  }
};
</script>

همچنین می توانیم آن را با string interpolation نمایش بدهیم:

<template>
  <div>
    <h1>The User Page</h1>
    <hr />
    <p>User ID: {{ id }}</p>
    <button @click="navigateToHome" class="btn btn-primary">Go to Home</button>
  </div>
</template>

در نگاه اول این کد مشکلی ندارد چرا که کامپوننت User.vue (که حاوی کدهای بالا است) تنها زمانی ساخته می شود که در URL به /user/id برویم بنابراین id همیشه حضور خواهد داشت اما حالتی وجود دارد که در آن کدهای ما به مشکل می خورند. بگذارید به شما نشان دهم.

ابتدا به فایل Header.vue رفته و کدهای آن را به شکل زیر تغییر دهید:

<template>
  <ul class="nav nav-pills">
    <router-link to="/" tag="li" active-class="active" exact>
      <a>Home</a>
    </router-link>
    <router-link to="/user/1" tag="li" active-class="active">
      <a>User 1</a>
    </router-link>
    <router-link to="/user/2" tag="li" active-class="active">
      <a>User 2</a>
    </router-link>
  </ul>
</template>

همانطور که به شما گفته بودم router-link باعث حذف شدن کامپوننت های دیگر نمی شود بلکه کامپوننت جدید را به آن پیوست می کند بنابراین می توانیم چند کامپوننت را در یک صفحه بارگذاری کنیم. به همین دلیل در کد بالا یک router-link جدید اضافه کرده ام که باز هم کامپوننت User را بارگذاری کند. بدین صورت دو id پاس داده می شود. حالا اگر به مرورگر برویم و روی User 1 کلیک کنیم به کامپوننت User با آیدی 1 می رویم:

کامپوننت user بارگذاری شده و پارامتر موجود در URL به درستی نمایش داده می شود.
کامپوننت user بارگذاری شده و پارامتر موجود در URL به درستی نمایش داده می شود.

اما اگر روی User 2 کلیک کنیم id نمایش داده شده یکسان است و روی حالت قبلی (یعنی عدد 1) باقی می ماند:

کامپوننت user بارگذاری شده اما پارامتر موجود در URL در حالت قبلی باقی مانده است.
کامپوننت user بارگذاری شده اما پارامتر موجود در URL در حالت قبلی باقی مانده است.

چرا؟ ما در هر دو لینک از یک کامپوننت استفاده می کنیم (User.vue) و Vue زمانی که می بینید در حال بارگذاری همان کامپوننت قبلی هستیم دیگر string interpolation را دوباره انجام نمی دهد:

<p>User ID: {{ id }}</p>

این مقدار از حالت قبلی باقی مانده و دوباره ساخته نمی شود. این رفتار در حالت عادی بسیار خوب است و سرعت برنامه ما را بالا می برد تا هر دفعه آن را نابود نکرده و دوباره نسازیم اما در چنین موارد خاصی باید id را watch کنیم. اگر یادتان باشد در فصل های اولیه این دوره در مورد Watch صحبت کرده بودیم:

<script>
export default {
  data() {
    return {
      id: this.$route.params.id
    };
  },
  watch: {
    '$route'(to, from) {
      this.id = to.params.id;
    }
  },
  methods: {
    navigateToHome() {
      this.$router.push("/");
    }
  }
};
</script>

از آنجایی که قبلا با watch آشنا شدیم می دانیم که روش استفاده از آن به شکل بالا است. دو پارامتر to (همان URL فعلی و جدید) و from (همان URL قبلی) توسط پکیج vue-router به صورت خودکار پاس داده می شوند. بنابراین می توان گفت که خصوصیت id درون Data (همان this.id) را برابر با to.params.id بگذار یا به عبارتی در URL جدید (to) به دنبال پارامتری به نام id بگرد و آن را برابر با id درون data بگذار. شما می توانید علاوه بر کد بالا، کدهای دیگری را هم در آن اجرا کنید اما من نیازی به آن ها ندارم. همچنین یادتان نرود که می توانستیم از computed نیز استفاده کنیم و watch تنها راهِ ممکن نیست.

البته استفاده از برنامه بدین شکل (وارد کردن دستی URL ها) اصلا واقعی نیست بنابراین بهتر است برنامه را کمی واقع گرایانه تر بنویسیم. من می خواهم با استفاده از کامپوننت UserStart.vue لیستی از کاربران را نمایش بدهم و از بین آن ها کاربر مورد نظر را انتخاب کنیم. بنابراین باید همه چیز را به حالت طبیعی برگردانیم. یعنی ابتدا به فایل Header.vue رفته و کدها را به شکل قبلی برگردانید:

<template>
  <ul class="nav nav-pills">
    <router-link to="/" tag="li" active-class="active" exact>
      <a>Home</a>
    </router-link>
    <router-link to="/user" tag="li" active-class="active">
      <a>User</a>
    </router-link>
  </ul>
</template>

سپس به routes.js رفته و پارامتر پویا را از آن حذف می کنیم:

export const routes = [
    { path: '', component: Home },
    { path: '/user', component: User }
];

سپس در فایل User.vue کدهای اضافی قسمت script را حذف می کنیم:

<template>
  <div>
    <h1>The User Page</h1>
    <hr />
    <button @click="navigateToHome" class="btn btn-primary">Go to Home</button>
  </div>
</template>

<script>
export default {
  methods: {
    navigateToHome() {
      this.$router.push("/");
    }
  }
};
</script>

حالا همه چیز به حالت قبلی برگشته است. از آنجایی که کامپوننت های بعدی ما (کامپوننت های موجود در پوشه user که در ابتدای فصل ساخته ایم) به کامپوننت اصلی User.vue مربوط است، می خواهم آن ها را درون User نمایش بدهم. یعنی کامپوننت های تو در تو داشته باشیم به صورتی که چندین کامپوننت درون User.vue باشند. برای این کار به فایل routes.js رفته و کدهای مربوط به user را بدین شکل تغییر می دهیم:

import User from './components/user/User.vue';
import UserStart from './components/user/UserStart.vue';
import UserDetail from './components/user/UserDetail.vue';
import UserEdit from './components/user/UserEdit.vue';
import Home from './components/Home.vue';

export const routes = [
    { path: '', component: Home },
    {
        path: '/user', component: User, children: [
            { path: '', component: UserStart },
            { path: ':id', component: UserDetail },
            { path: ':id/edit', component: UserEdit }
        ]
    }
];

من ابتدا کامپوننت های موجود در پوشه user را import کرده ام تا بتوانیم از آن ها استفاده کنیم. سپس از آنجایی که می خواهم این کامپوننت ها درون User.vue بارگذاری شوند باید از خصوصیتی به نام children استفاده کنیم. همانطور که در کد بالا مشخص است، children آرایه ای از اشیاء مختلف است که هر شیء نماینده یک کامپوننت دیگر است. تمام موارد دیگر قبلا توضیح داده شده است اما نکته جالبی در مورد path وجود دارد. برای کامپوننت هایی که درون children هستند path به دو حالت ممکن تعریف می شود:

  • اگر path را با علامت / شروع کنید، مقدار تایپ شده پس از آن مستقیما به root URL (مثلا com) پیوست می شود. بنابراین 'path: '/mypage یعنی example.com/mypage
  • اگر path بدون علامت / شروع شود، مقدار پاس داده شده به آن به انتهای path پدر (کامپوننت vue) متصل می شود. بنابراین 'path: 'mypage می شود example.com/user/mypage

شما می توانید از هر کدام از این روش ها استفاده کنید و انتخاب شما بستگی به نیاز برنامه و سلیقه شما دارد. با این حساب سه کامپوننت فرزند دورن User.vue بدین شکل عمل می کنند:

  • کامپوننت UserStart زمانی اجرا می شود که در آدرس user/ باشیم.
  • کامپوننت userDetail زمانی اجرا می شود که در آدرس user/id/ باشیم.
  • کامپوننت userEdit زمانی اجرا می شود که در آدرس user/id/edit/ باشیم.

ما در فایل App.vue یک router-view داشتیم که router سراسری و اصلی (Root router) ما بود اما این ها کامپوننت های nest شده درون User.vue هستند و در آنجا بارگذاری نخواهند شد بنابراین باید به User.vue رفته و بگوییم:

<template>
  <div>
    <h1>The User Page</h1>
    <hr />
    <button @click="navigateToHome" class="btn btn-primary">Go to Home</button>
    <hr />
    <router-view></router-view>
  </div>
</template>

تنها کاری که کرده ام اضافه کردن router-view به این کامپوننت است تا کامپوننت های فرزند درون آن بارگذاری شوند. حالا می توانید به مرورگر بروید و این کدها را تست کنید. کامپوننت های ما در آدرس های زیر قابل دسترسی هستند:

http://localhost:8080/user

http://localhost:8080/user/1

http://localhost:8080/user/1/edit

با مراجعه به این لینک ها، کامپوننت های آن ها بارگذاری می شوند. در مرحله بعد لیست کاربران را به صورت لینک در می آوریم تا با کلیک روی آن ها کامپوننت userDetail را بارگذاری کنیم.

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

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