آموزش Vue.js: ارتباط بین کامپوننت ها و مدیریت حالت با Vuex — بخش نهم

۲۴۳ بازدید
آخرین به‌روزرسانی: ۱۴ شهریور ۱۴۰۲
زمان مطالعه: ۷ دقیقه
آموزش Vue.js: ارتباط بین کامپوننت ها و مدیریت حالت با Vuex — بخش نهم

روش‌های مختلفی برای برقراری ارتباط بین کامپوننت‌های Vue وجود دارد. در این مقاله به توضیح این روش‌های مختلف خواهیم پرداخت. همچنین روش برقراری ارتباط را از طریق کتابخانه مدیریت حالت با Vuex توضیح می‌دهیم. برای مطالعه بخش‌های قبلی این مجموعه مقالات آموزشی روی لینک زیر کلیک کنید.

ارتباط با استفاده از props

نخستین روش برای برقراری ارتباط بین کامپوننت‌های Vue استفاده از props است.

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

1<template>
2  <div>
3    <Car color="green" />
4  </div>
5</template>
6
7<script>
8import Car from './components/Car'
9
10export default {
11  name: 'App',
12  components: {
13    Car
14  }
15}
16</script>

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

استفاده از رویدادها برای ارتباط از سمت فرزندان به والدین

رویدادها امکان برقراری ارتباط از سمت فرزندان به والدین را فراهم می‌سازند:

1<script>
2export default {
3  name: 'Car',
4  methods: {
5    handleClick: function() {
6      this.$emit('clickedSomething')
7    }
8  }
9}
10</script>

والدین می‌توانند این مقدار را با استفاده از دایرکتیو v-on در زمان include کردن کامپوننت در قالب خود تفسیر کنند:

1<template>
2  <div>
3    <Car v-on:clickedSomething="handleClickInParent" />
4    <!-- or -->
5    <Car @clickedSomething="handleClickInParent" />
6  </div>
7</template>
8
9<script>
10export default {
11  name: 'App',
12  methods: {
13    handleClickInParent: function() {
14      //...
15    }
16  }
17}
18</script>

ارسال پارامتر

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

1<script>
2export default {
3  name: 'Car',
4  methods: {
5    handleClick: function() {
6      this.$emit('clickedSomething', param1, param2)
7    }
8  }
9}
10</script>

و آن‌ها را از والد بازیابی نمود:

1<template>
2  <div>
3    <Car v-on:clickedSomething="handleClickInParent" />
4    <!-- or -->
5    <Car @clickedSomething="handleClickInParent" />
6  </div>
7</template>
8
9<script>
10export default {
11  name: 'App',
12  methods: {
13    handleClickInParent: function(param1, param2) {
14      //...
15    }
16  }
17}
18</script>

استفاده از Event Bus برای ارتباط بین کامپوننت‌ها

با استفاده از رویدادها دیگر محدود به روابط فرزند-والد نیستیم.

بلکه می‌توانیم از Event Bus استفاده کنیم. در کد فوق ما از this.emit$ برای ارسال یک رویداد به وهله کامپوننت استفاده کردیم. به جای آن می‌توانیم رویدادی را روی کامپوننتی با دسترسی عمومی‌تر ارسال کنیم. یعنی کامپوننت root به طور معمول به این منظور استفاده می‌شود. همچنین می‌توانید کامپوننت Vue اختصاصی برای این کار ایجاد کنید و آن را هر جا که لازم است ایمپورت کنید.

1<script>
2export default {
3  name: 'Car',
4  methods: {
5    handleClick: function() {
6      this.$root.$emit('clickedSomething')
7    }
8  }
9}
10</script>

هر کامپوننت دیگری می‌تواند به این رویداد گوش دهد. این کار در یک callback به نام mounted میسر است:

1<script>
2export default {
3  name: 'App',
4  mounted() {
5    this.$root.$on('clickedSomething', () => {
6      //...
7    })
8  }
9}
10</script>

روش‌های جایگزین

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

مدیریت حالت با Vuex

Vuex کتابخانه مدیریت حالت رسمی برای Vue.js است. وظیفه آن اشتراک داده‌ها در میان کامپوننت‌های یک اپلیکیشن است.

چنان که در بخش قبل توضیح دادیم، کامپوننت‌ها در Vue.js به صورت پیش‌فرض می‌توانند با استفاده از props و همچنین رویدادها با هم ارتباط برقرار سازند. اما در موارد پیچیده‌تر به ابزارهای پیشرفته‌تری هم نیاز داریم. در این حالت گزینه مناسب متمرکز ساختن حالت در یک انباره متمرکز است. این همان کاری است Vuex انجام می‌دهد.

چرا باید از Vuex استفاده کنیم؟

  • Vuex تنها گزینه مدیریت حالت نیست که می‌توان در Vue استفاده کرد بلکه می‌توان از Redux نیز استفاده کرد. اما بزرگ‌ترین مزیت آن این است که رسمی است و به خوبی با Vue.js یکپارچه شده است.
  • در React همواره با دردسر انتخاب از بین تعداد زیادی کتابخانه مواجه هستیم، زیرا اکوسیستم آن بزرگ است و هیچ استاندارد عملی وجود ندارد. تا همین اواخر ریداکس یک گزینه رایج بود و بعد از آن MobX در رتبه دوم محبوبیت قرار داشت. در مورد Vue لازم نیست به دنبال چیزی به جز Vuex بگردیم.
  • Vuex بسیاری از ایده‌های خود را از اکوسیستم React گرفته است، چون این الگوی Flux است که از سوی ریداکس رواج یافته است.
  • اگر با Flux یا Redux آشنا باشید، Vuex برایتان کاملاً آشنا خواهد بود. اگر هم چنین آشنایی ندارید، جای نگرانی نیست، چون همه مفاهیم از سطح پایه توضیح داده می‌شوند.
  • کامپوننت‌ها در اپلیکیشن Vue می‌توانید حالت خاص خود را داشته باشند. برای نمونه در کادر ورودی داده‌های وارد شده به صورت لوکال ذخیره می‌شوند. این وضعیت کاملاً مناسب است و کامپوننت‌ها می‌توانند درزمان استفاده از Vuex دارای حالت لوکال باشند.

انباره Vuex

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

استفاده از Vuex نوعی پیچیدگی وارد اپلیکیشن می‌کند، چون همه چیز باید به ترتیبی تنظیم شود که به درستی کار کند. اما استفاده از Vuex موجب حل مشکل ارسال props سازمان‌دهی نشده و سیستم رویداد می‌شود که در صورت بزرگ شدن بیش از حد می‌تواند به وضعیت سردرگم‌کننده‌ای تبدیل شود و از این رو گزینه خوبی محسوب می‌شود.

مثالی از کاربرد Vuex

در مثالی که در این بخش بررسی می‌کنیم کار خود را از یک اپلیکیشن Vue CLI آغاز می‌کنیم. می‌توان از Vuex از طریق بارگذاری مستقیم در تگ اسکریپت نیز استفاده کرد. اما از آنجا که Vuex عموماً در اپلیکیشن‌های بزرگ‌تر کاربرد دارد، بهتر است آن را در یک اپلیکیشن ساخت‌یافته‌تری استفاده کنیم. بدین ترتیب از یک اپلیکیشن شروع می‌کنیم که به سرعت با Vue CLI ساخته می‌شود. مثالی که استفاده می‌کنیم در یک CodeSandbox قرار می‌گیرد که سرویسی مناسب است و مثال Vue Cli آماده‌ای نیز دارد.

Vuex

به این مثال آماده (+) بروید و روی دکمه Add dependency کلیک کنید و با وارد کردن عبارت Vuex روی آن کلیک کنید. اینک Vuex در وابستگی‌های پروژه لیست شده است. برای نصب محلی Add dependency می‌توانید یکی از دستورهای زیر را درون پوشه پروژه اجرا کنید:

npm install Vuex

یا

yarn add Vuex

ایجاد انباره Vuex

اکنون آماده ایجاد انباره Vuex هستیم. این فایل را می‌توان هر جایی قرار دارد. به طور کلی پیشنهاد می‌شود که آن را در فایل زیر قرار دهید:

src/store/store.js

ما نیز چنین می‌کنیم. در این فایل Vuex را مقداردهی کرده و به Vue اعلام می‌کنیم که از آن استفاده کند:

1import Vue from 'vue'
2import Vuex from 'vuex'
3
4Vue.use(Vuex)
5
6export const store = new Vuex.Store({})

Vuex

در ادامه شیء انباره Vuex را که با استفاده از API به نام ()Vuex.Store ایجاد کرده‌ایم اکسپورت می‌کنیم.

کاربردی از انباره

اینک که چارچوبی آماده کردیم نوبت آن رسیده که ایده‌ای برای کاربرد Vuex داشته باشیم تا با مفاهیم آن بهتر آشنا شویم. برای نمونه فرض کنید دو کامپوننت هم‌نیا داریم که یکی یک فیلد ورودی دارد و دیگری محتوای فیلد ورودی را پرینت می‌کند. زمانی که فیلد تغییر یابد، می‌خواهیم که محتوای کامپوننت دوم نیز تغییر پیدا کند. این کار بسیار ساده است اما برای یادگیری مفاهیم مناسب است.

معرفی کامپوننت‌های جدید مورد نیاز

ما کامپوننت HelloWorld را حذف کرده و یک کامپوننت Form و یک کامپوننت display اضافه می‌کنیم:

1<template>
2	<div>
3		<label for="flavor">Favorite ice cream flavor?</label>
4		<input name="flavor">
5	</div>
6</template>
1<template>
2	<div>
3		<p>You chose ???</p>
4	</div>
5</template>

افزودن کامپوننت‌ها به اپلیکیشن

در این مرحله کد App.vue را به جای کامپوننت HelloWorld اضافه می‌کنیم:

1<template>
2  <div id="app">
3    <Form/>
4    <Display/>
5  </div>
6</template>
7
8<script>
9import Form from './components/Form'
10import Display from './components/Display'
11
12export default {
13  name: 'App',
14  components: {
15    Form,
16    Display
17  }
18}
19</script>

افزودن حالت به انباره

اینک که موارد را تنظیم کردیم به فایل store.js بازمی‌گردیم و یک مشخصه به نام state به انباره اضافه می‌کنیم که یک شیء حاوی مشخصه flavor است. این شیء در ابتدا یک رشته خالی است.

1import Vue from 'vue'
2import Vuex from 'vuex'
3
4Vue.use(Vuex)
5
6export const store = new Vuex.Store({
7  state: {
8    flavor: ''
9  }
10})

زمانی که کاربر چیزی در فیلد ورودی وارد کند، آن را به‌روزرسانی می‌کنیم.

افزودن یک تغییر

حالت نمی‌تواند دستکاری شود، مگر با استفاده از mutation به این منظور یک mutation تنظیم می‌کنیم که درون کامپوننت Form استفاده می‌شود تا به انباره اطلاع دهد که حالت باید تغییر یابد.

1import Vue from 'vue'
2import Vuex from 'vuex'
3
4Vue.use(Vuex)
5
6export const store = new Vuex.Store({
7  state: {
8    flavor: ''
9  },
10  mutations: {
11    change(state, flavor) {
12      state.flavor = flavor
13    }
14  }
15})

افزودن یک getter برای ارجاع به مشخصه حالت

بدین ترتیب باید یک روش برای بررسی حالت داشته باشیم. به این منظور از getter-ها استفاده می‌کنیم. در ادامه یک getter برای مشخصه flavor تنظیم می‌کنیم:

1import Vue from 'vue'
2import Vuex from 'vuex'
3
4Vue.use(Vuex)
5
6export const store = new Vuex.Store({
7  state: {
8    flavor: ''
9  },
10  mutations: {
11    change(state, flavor) {
12      state.flavor = flavor
13    }
14  },
15  getters: {
16    flavor: state => state.flavor
17  }
18})

توجه کنید که getters یک شیء است. flavor مشخصه این شیء است که حالت را به عنوان یک پارامتر می‌گیرد و مشخصه flavor حالت را بازگشت می‌دهد.

افزودن انباره Vuex به اپلیکیشن

اکنون انباره آماده استفاده است. به کد اپلیکیشن بازمی‌گردیم و در فایل main.js حالت را ایمپورت کرده و آن را در اپلیکیشن Vue ارائه می‌کنیم. به این منظور کد زیر را اضافه می‌کنیم:

1import { store } from './store/store'

و آن را به اپلیکیشن Vue می‌افزاییم:

1new Vue({
2  el: '#app',
3  store,
4  components: { App },
5  template: '<App/>'
6})

زمانی که این مورد را اضافه کردیم، از آنجا که این کامپوننت اصلی Vue است، متغیر store درون هر کامپوننت Vue به انباره Vuex اشاره می‌کند.

به‌روزرسانی حالت روی یک عمل کاربر با commit

فرض کنید زمانی که کاربر چیزی را تایپ می‌کند، حالت را به‌روزرسانی می‌کنیم. این کار با استفاده از API به نام ()store.commit انجام می‌شود.

اما قبل از آن متدی ایجاد کنیم که هنگام تغییر یافتن محتوای ورودی فراخوانی می‌شود. ما به جای change@ از input@ استفاده می‌کنیم، زیرا تنها زمانی تحریک می‌شود که فوکوس از روی کادر ورودی برداشته شود، در حالی که input@ در زمان فشرده شدن هر کلید کیبورد فراخوانی می‌شود.

1<template>
2  <div>
3    <label for="flavor">Favorite ice cream flavor?</label>
4    <input @input="changed" name="flavor">
5  </div>
6</template>
7
8<script>
9export default {
10  methods: {
11    changed: function(event) {
12      alert(event.target.value)
13    }
14  }
15}
16</script>

اینک که مقدار flavor را در اختیار داریم از API مربوط به Vuex استفاده می‌کنیم:

1<script>
2export default {
3  methods: {
4    changed: function(event) {
5      this.$store.commit('change', event.target.value)
6    }
7  }
8}
9</script>

به شیوه ارجاع به انباره دقت کنید. این کار به لطف گنجاندن شیء انباره در مقداردهی اولیه کامپوننت Vue ممکن شده است. متد ()commit یک نام mutation و یک payload می‌پذیرد که به عنوان پارامتر دوم تابع callback به mutation ارسال می‌شود.

استفاده از getter برای پرینت مقدار حالت

اینک باید با استفاده از $store.getters.flavor‎ به getter این مقدار در قالب Display اشاره کنیم. می‌توانیم this را حذف کنیم، زیرا اینک در قالب هستیم و this اشاره ضمنی است.

1<template>
2  <div>
3    <p>You chose {{ $store.getters.flavor }}</p>
4  </div>
5</template>

برای مشاهده کد کامل این مثال به این آدرس (+) مراجعه کنید. البته مفاهیم زیادی مانند موارد فهرست زیر وجود دارند که در این زمینه باید مورد بررسی قرار گیرند:

  • actions
  • modules
  • helpers
  • plugins

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

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

==

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *