آشنایی با مبحث Mutation در VueX

Introduction to Mutation in VueX

Vue.JS 2: آشنایی با مبحث Mutation در VueX - قسمت 93

اگر یادتان باشد یکی از مشکلاتی که در دریافت مستقیم داده های state داشتیم این بود که همه چیز قاطی شده و نظم کار از بین می رفت. علاوه بر این باعث ایجاد code duplication (نوشتن کدهای تکراری) نیز می شدیم. همین مشکلات در هنگام ایجاد تغییرات در خصوصیات (نه دریافت خصوصیات) نیز حضور دارند. در حال حاضر ما مقادیر state را گرفته و مستقیما در همان کامپوننت نمایش می دهیم و اگر بخواهیم آن را تغییر بدهیم در همان کامپوننت تغییر خواهیم داد. توجه داشته باشید که تغییراتی که قبل از دریافت با getter انجام می دادیم بحث جدایی دارند و کلی هستند، نه برای تک تک کامپوننت ها. همچنین تغییر State در getter ها پیشنهاد نمی شود چرا که در برنامه های واقعی کدهای بسیار زیادی داریم و تغییرات فقط در حد ضرب در 2 کردن نیستند. کار Getterها باید منحصرا روی دریافت یک مقدار باشد.

این مسئله همان مبحث Mutation (ایجاد تغییر) است. یعنی محلی مرکزی برای ایجاد تغییرات داریم که دستور ایجاد ویرایش را از یک کامپوننت گرفته و commit (به معنی «ثبت») می کند. زمانی که این commit انجام شد، تمام کامپوننت هایی که از آن خصوصیت خاص در state استفاده می کنند، یک هشدار دریافت می کنند که مقدار خصوصیت در State تغییر کرده است بنابراین همه کامپوننت ها نیز به روز رسانی شده و مقدار جدید را دریافت می کنند. برای این کار به فایل store.js می رویم و mutations را تعریف می کنیم:

 export const store = new Vuex.Store({
    state: {
        counter: 0
    },
    getters: {
        doubleCounter: state => {
            return state.counter * 2;
        },
        stringCounter: state => {
            return state.counter + ' Clicks';
        }
    },
    mutations: {

    }
});

یادتان باشد که mutations نیز یک کلیدواژه رزرو شده است بنابراین نمی توانید نام دلخواه برایش انتخاب کنید. حالا باید درون mutations متدهای مختلف خود را بنویسیم. من می خواهم دو متد را در اینجا تعریف کنم که اولی increment (به معنی اضافه کردن) و دومی Decrement (به معنی کم کردن) نام دارند. بنابراین به سادگی می توان گفت:

export const store = new Vuex.Store({
    state: {
        counter: 0
    },
    getters: {
        doubleCounter: state => {
            return state.counter * 2;
        },
        stringCounter: state => {
            return state.counter + ' Clicks';
        }
    },
    mutations: {
        increment: state => {
            state.counter++;
        },
        decrement: state => {
            state.counter--;
        }
    }
});

مثل همیشه state به صورت خودکار پاس داده می شود و نیازی به انجام کار خاصی نیست. از طرفی می دانیم که در فایل counter.vue کد زیر را نوشته ایم:

<script>
export default {
  methods: {
    increment() {
      this.$store.state.counter++;
    },
    decrement() {
      this.$store.state.counter--;
    }
  }
};
</script>

همانطور که می بینید این کد درون methods در شیء Vue مخصوص این کامپوننت تعریف شده است بنابراین مرکزی نیست. مشکل اینجاست که اگر بخواهیم اضافه کردن یا کم کردن counter را در 10 کامپوننت دیگر نیز انجام بدهیم، باید برای تک تک آن ها در قسمت methods توابع بالا را بنویسیم که باعث نوشتن کدهای تکراری است. مثلا یک فایل دیگر به نام AnotherCounter.vue ایجاد کنید و کدهای counter.vue را دقیقا و بدون تغییر در آن کپی کنید:

<template>
  <div>
    <button class="btn btn-primary" @click="increment">Increment</button>
    <button class="btn btn-primary" @click="decrement">Decrement</button>
  </div>
</template>

<script>
export default {
  methods: {
    increment() {
      this.$store.state.counter++;
    },
    decrement() {
      this.$store.state.counter--;
    }
  }
};
</script>

سپس به App.vue می رویم و آن را import و سپس به عنوان کامپوننت محلی ثبت می کنیم:

<script>
import Counter from "./components/Counter.vue";
import AnotherCounter from "./components/AnotherCounter";
import Result from "./components/Result.vue";
import AnotherResult from "./components/AnotherResult";

export default {
  components: {
    appCounter: Counter,
    appAnotherCounter: AnotherCounter,
    appResult: Result,
    appAnotherResult: AnotherResult
  }
};
</script>

در نهایت نیز می توانیم آن را به عنوان یک کامپوننت عادی در قسمت template نمایش بدهیم:

<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
  <h1>Vuex</h1>
  <app-result></app-result>
  <app-another-result></app-another-result>
  <hr />
  <app-counter></app-counter>
  <app-another-counter></app-another-counter>
</div>

در مرحله بعد به Counter.vue می رویم تا به جای کدهای قبلی آن، از mutation های خودمان استفاده کنیم. سوال اصلی اینجاست که چطور mutation ها را صدا بزنیم؟

<script>
export default {
  methods: {
    increment() {
      this.$store.commit("increment");
    },
    decrement() {
      this.$store.commit("decrement");
    }
  }
};
</script>

همانطور که مشخص است برای صدا زدن mutation ها باید مثل همیشه از this.$store استفاده کنیم و سپس متد commit را صدا بزنیم. این متد یک آرگومان می گیرد که در واقع نام mutation شما است. یادتان باشد که حتما باید آن را درون یک رشته قرار بدهید نه اینکه مستقیما آن را صدا بزنید. حالا اگر کدها را ذخیره کرده و به مرورگر برویم، همه چیز به خوبی کار می کند. یعنی هم دکمه های Counter.vue (که از mutation استفاده می کنند) و هم دکمه های AnotherCounter.vue (که از متدهای محلی خودشان استفاده می کنند) به خوبی کار می کنند. البته برای اینکه روش غیراستاندارد قبلی را کنار بگذاریم، به AnotherCounter.vue می رویم و متدهای عادی آن را به شکل بالا تغییر می دهیم تا تمام کامپوننت ها از mutation ها استفاده کنند.

مشکل بعدی ما این است که کدها باز هم تکراری هستند و code duplication پیش آمده است. در واقع آنچنان تغییری در کدها ما ایجاد نشد و بهتر بود روشی مانند mapGetters داشته باشیم که فرآیند اتصال این mutation ها به کامپوننت ها را به صورت خودکار انجام دهد. خوشبختانه چنین قابلیتی وجود دارد. من در Counter.Vue به شکل زیر عمل می کنم:

<script>
import { mapMutations } from "vuex";
export default {
  methods: {
    ...mapMutations(["increment", "decrement"])
  }
};
</script>

یعنی ابتدا متد mapMutations را از پکیج VueX وارد این فایل می کنم. سپس دقیقا مانند mapGetters عمل می کنیم؛ یعنی با استفاده از اپراتور spread (علامت سه نقطه) جفت های key/value را از mapMutations در آورده و پخش می کنیم تا اگر بعدا خودمان نیز متدهایی برای تعریف داشتیم، بتوانیم این کار را انجام بدهیم. برای صدا زدن این متدها هم نیازی به ویرایش چیزی نیست چرا که متدهای قبلی خودمان نیز increment و decrement نام داشتند:

<template>
  <div>
    <button class="btn btn-primary" @click="increment">Increment</button>
    <button class="btn btn-primary" @click="decrement">Decrement</button>
  </div>
</template>

همین کار را دقیقا برای فایل AnotherCounter.vue نیز انجام بدهید تا کدهایمان کوتاه تر شوند. حالا تمام کدهای مربوط به ایجاد تغییرات برای ما درون یک مکان واحد قرار گرفته اند و همه چیز منظم تر شده است.

microsoft lizenz kaufen

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

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