Vuex Action vs Mutations

173

Dalam Vuex, apa logika memiliki "tindakan" dan "mutasi?"

Saya memahami logika komponen yang tidak dapat mengubah keadaan (yang tampaknya pintar), tetapi memiliki kedua tindakan dan mutasi sepertinya Anda sedang menulis satu fungsi untuk memicu fungsi lainnya, untuk kemudian mengubah keadaan.

Apa perbedaan antara "aksi" dan "mutasi," bagaimana mereka bekerja bersama, dan lebih lagi, saya ingin tahu mengapa pengembang Vuex memutuskan untuk melakukannya dengan cara ini?

Kobi
sumber
2
Lihat "On To Actions", saya pikir: vuex.vuejs.org/en/mutations.html#on-to-actions
Roy J
diskusi terkait: github.com/vuejs/vuex/issues/587
chuck911
1
Anda tidak dapat secara langsung mengubah status toko. Satu-satunya cara untuk mengubah keadaan toko adalah dengan secara eksplisit melakukan mutasi. Untuk itu kita perlu tindakan untuk melakukan mutasi.
Suresh Sapkota
1
@ SureshSapkota pernyataan itu sangat membingungkan, karena keduanya mutationsdan actionsdidefinisikan dalam dokumentasi vuex sebagai metode mengubah negara. Anda tidak perlu melakukan tindakan untuk melakukan mutasi.
Graham
1
Mutasi, seperti namanya digunakan untuk memodifikasi / mengubah objek negara Anda. Actions sangat mirip dengan mutasi, tetapi alih-alih bermutasi, Actions melakukan mutasi. Tindakan dapat berisi kode asinkron atau logika bisnis yang sewenang - wenang . Vuex merekomendasikan objek keadaan hanya boleh dimutasi di dalam fungsi Mutasi. Disarankan juga untuk tidak menjalankan kode yang berat atau memblokir di dalam fungsi Mutasi karena sifatnya sinkron .
Emmanuel Neni

Jawaban:

221

Pertanyaan 1 : Mengapa pengembang Vuejs memutuskan untuk melakukannya dengan cara ini?

Menjawab:

  1. Ketika aplikasi Anda menjadi besar, dan ketika ada banyak pengembang yang mengerjakan proyek ini, Anda akan menemukan "pengelolaan negara" (terutama "keadaan global"), akan menjadi semakin rumit.
  2. Cara vuex (seperti halnya Redux di react.js ) menawarkan mekanisme baru untuk mengelola keadaan, menjaga keadaan, dan "menyimpan dan dapat dilacak" (itu berarti setiap tindakan yang memodifikasi keadaan dapat dilacak dengan alat debug: vue-devtools )

Pertanyaan 2 : Apa perbedaan antara "aksi" dan "mutasi"?

Mari kita simak dulu penjelasan resminya:

Mutasi:

Mutasi Vuex pada dasarnya adalah peristiwa: setiap mutasi memiliki nama dan penangan.

import Vuex from 'vuex'

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    INCREMENT (state) {
      // mutate state
      state.count++
    }
  }
})

Tindakan: Tindakan hanyalah fungsi yang mengirim mutasi.

// the simplest action
function increment (store) {
  store.dispatch('INCREMENT')
}

// a action with additional arguments
// with ES2015 argument destructuring
function incrementBy ({ dispatch }, amount) {
  dispatch('INCREMENT', amount)
}

Inilah penjelasan saya tentang hal di atas:

  • mutasi adalah satu - satunya cara untuk mengubah keadaan
  • mutasi tidak peduli dengan logika bisnis, ia hanya peduli tentang "keadaan"
  • tindakan adalah logika bisnis
  • action dapat mengirim lebih dari 1 mutasi pada satu waktu, itu hanya mengimplementasikan logika bisnis, tidak peduli dengan perubahan data (yang dikelola dengan mutasi)
Kaicui
sumber
80
Fakta bahwa tindakan "adalah logika bisnis" dan dapat mengirim banyak mutasi pada suatu waktu sangat membantu. Itulah jawaban yang saya cari. Terima kasih.
Kobi
11
Anda dapat mengatakan Anda "mengirim mutasi". Bukankah kata-kata yang tepat bahwa Anda BERKOMITMEN mutasi?
ProblemsOfSumit
4
Anda mengirim tindakan dan melakukan mutasi.
eirik
4
pengiriman tidak lagi berfungsi di vue 2.0 untuk mutasi, Anda harus melakukan mutasi dalam tindakan.
SKLTFZ
18
@ Kaicui Jawaban ini tidak memiliki catatan tentang mutasi yang selalu disinkronkan, dan tindakan yang berpotensi asinkron. Selain itu, jawaban yang bagus!
mendeklarasikan
58

Mutasi bersifat sinkron, sedangkan tindakan bisa asinkron.

Dengan kata lain: Anda tidak perlu melakukan tindakan jika operasi Anda sinkron, jika tidak, implementasikan.

Bas
sumber
2
ini sebenarnya menjawab pertanyaan yang akan saya buat, tentang bagaimana contoh todomvc tidak menggunakan tindakan.
sksallaj
7
'Anda tidak perlu tindakan jika operasi Anda sinkron' : Itu tidak benar: Anda melakukan tindakan perlu jika Anda ingin menulis beberapa mutasi dari modul yang sama, karena Anda tidak dapat memanggil tindakan lain dari tindakan.
Raymundus
1
Tindak lanjut yang jelas untuk jawaban ini adalah "lalu mengapa tidak hanya bertindak dan menyingkirkan mutasi"
Michael Mrozek
34

Saya percaya bahwa memiliki pemahaman tentang motivasi di balik Mutasi dan Tindakan memungkinkan seseorang untuk menilai dengan lebih baik kapan harus menggunakan mana dan bagaimana. Ini juga membebaskan programmer dari beban ketidakpastian dalam situasi di mana "aturan" menjadi kabur. Setelah beralasan tentang tujuan mereka masing-masing, saya sampai pada kesimpulan bahwa meskipun mungkin ada cara yang salah untuk menggunakan Tindakan dan Mutasi, saya tidak berpikir bahwa ada pendekatan kanonik.

Pertama-tama mari kita mencoba memahami mengapa kita melakukan mutasi atau tindakan.

Mengapa harus melalui boilerplate? Mengapa tidak mengubah status langsung dalam komponen?

Sebenarnya Anda dapat mengubah statelangsung dari komponen Anda. Ini statehanyalah objek JavaScript dan tidak ada yang ajaib yang akan mengembalikan perubahan yang Anda buat padanya.

// Yes, you can!
this.$store.state['products'].push(product)

Namun, dengan melakukan ini, Anda menyebarkan mutasi negara Anda di semua tempat. Anda kehilangan kemampuan untuk hanya membuka satu modul untuk perumahan negara dan sekilas melihat jenis operasi apa yang dapat diterapkan untuk itu. Setelah mutasi terpusat menyelesaikan ini, meskipun dengan biaya beberapa boilerplate.

// so we go from this
this.$store.state['products'].push(product)

// to this
this.$store.commit('addProduct', {product})

...
// and in store
addProduct(state, {product}){
    state.products.push(product)
}
...

Saya pikir jika Anda mengganti sesuatu yang pendek dengan boilerplate, Anda ingin boilerplate juga kecil. Karena itu saya berasumsi bahwa mutasi dimaksudkan untuk menjadi pembungkus yang sangat tipis di sekitar operasi asli di negara bagian, dengan hampir tidak ada logika bisnis. Dengan kata lain, mutasi dimaksudkan untuk sebagian besar digunakan seperti setter.

Sekarang, karena Anda telah memusatkan mutasi Anda, Anda memiliki gambaran yang lebih baik tentang perubahan status Anda dan karena tooling Anda (vue-devtools) juga mengetahui lokasi itu, menjadikan proses debug menjadi lebih mudah. Perlu juga diingat bahwa banyak plugin Vuex tidak menonton negara secara langsung untuk melacak perubahan, mereka lebih mengandalkan mutasi untuk itu. Perubahan-perubahan "tidak terikat" pada negara dengan demikian tidak terlihat oleh mereka.

Jadi mutations, actionsapa bedanya sih?

Tindakan, seperti mutasi, juga berada di modul toko dan dapat menerima stateobjek. Yang menyiratkan bahwa mereka juga bisa bermutasi secara langsung. Jadi apa gunanya memiliki keduanya? Jika kita beralasan bahwa mutasi harus dijaga tetap kecil dan sederhana, itu menyiratkan bahwa kita memerlukan sarana alternatif untuk menampung logika bisnis yang lebih rumit. Tindakan adalah cara untuk melakukan ini. Dan karena seperti yang telah kita ketahui sebelumnya, vue-devtools dan plugins menyadari perubahan melalui Mutasi, agar tetap konsisten kita harus terus menggunakan Mutasi dari tindakan kita. Lebih jauh, karena tindakan dimaksudkan untuk mencakup semua dan bahwa logika yang mereka enkapsulasi mungkin tidak sinkron, maka masuk akal jika Tindakan juga akan dibuat tidak sinkron sejak awal.

Sering ditekankan bahwa tindakan bisa tidak sinkron, sedangkan mutasi biasanya tidak. Anda dapat memutuskan untuk melihat perbedaan sebagai indikasi bahwa mutasi harus digunakan untuk apa pun yang sinkron (dan tindakan untuk apa pun yang tidak sinkron); Namun, Anda akan mengalami beberapa kesulitan jika misalnya Anda perlu melakukan lebih dari satu mutasi (secara serempak), atau jika Anda perlu bekerja dengan Getter dari mutasi Anda, karena fungsi mutasi tidak menerima Getters atau Mutations sebagai argumen ...

... yang mengarah ke pertanyaan menarik.

Mengapa Mutasi tidak menerima Getters?

Saya belum menemukan jawaban yang memuaskan untuk pertanyaan ini. Saya telah melihat beberapa penjelasan oleh tim inti yang saya temukan terbaik diperdebatkan. Jika saya merangkum penggunaannya, Getters dimaksudkan untuk dihitung (dan sering di-cache) ekstensi ke status. Dengan kata lain, mereka pada dasarnya masih keadaan, meskipun itu memerlukan beberapa perhitungan dimuka dan mereka biasanya hanya baca. Setidaknya itulah cara mereka didorong untuk digunakan.

Dengan demikian, mencegah Mutasi dari mengakses langsung Getters berarti bahwa satu dari tiga hal sekarang diperlukan, jika kita perlu mengakses dari yang sebelumnya beberapa fungsi yang ditawarkan oleh yang terakhir: (1) salah satu perhitungan keadaan yang disediakan oleh Getter digandakan di suatu tempat yang dapat diakses ke Mutasi (bau tak sedap), atau (2) nilai yang dihitung (atau Getter yang relevan itu sendiri) diturunkan sebagai argumen eksplisit terhadap Mutasi (funky), atau (3) logika Getter sendiri digandakan langsung dalam Mutasi , tanpa manfaat tambahan dari caching seperti yang diberikan oleh Getter (bau busuk).

Berikut ini adalah contoh (2), yang dalam kebanyakan skenario yang saya temui tampaknya merupakan opsi "paling buruk".

state:{
    shoppingCart: {
        products: []
    }
},

getters:{
    hasProduct(state){
        return function(product) { ... }
    }
}

actions: {
    addProduct({state, getters, commit, dispatch}, {product}){

        // all kinds of business logic goes here

        // then pull out some computed state
        const hasProduct = getters.hasProduct(product)
        // and pass it to the mutation
        commit('addProduct', {product, hasProduct})
    }
}

mutations: {
    addProduct(state, {product, hasProduct}){ 
        if (hasProduct){
            // mutate the state one way
        } else {
            // mutate the state another way 
        }
    }
}

Bagi saya, di atas tampaknya tidak hanya sedikit berbelit-belit, tetapi juga agak "bocor", karena beberapa kode yang ada dalam Aksi jelas mengalir dari logika internal Mutasi.

Menurut pendapat saya, ini merupakan indikasi kompromi. Saya percaya bahwa memungkinkan Mutasi untuk secara otomatis menerima Getters menghadirkan beberapa tantangan. Ini dapat berupa desain Vuex sendiri, atau tooling (vue-devtools et al), atau untuk mempertahankan beberapa kompatibilitas, atau kombinasi dari semua kemungkinan yang dinyatakan.

Apa yang saya tidak percaya adalah bahwa menyerahkan Surat kepada Mutasi Anda sendiri adalah pertanda bahwa Anda melakukan sesuatu yang salah. Saya melihatnya hanya sebagai "menambal" salah satu kelemahan kerangka kerja.

Michael Ekoka
sumber
1
Bagi saya itu jawaban terbaik. Hanya setelah membacanya, saya merasakan "klik" ini ketika Anda merasa telah memahami sesuatu.
Robert Kusznier
Surat kabar pada dasarnya adalah computedkeluaran. Mereka hanya baca. Cara yang lebih baik untuk melihat mutasi bisa dengan menghapus yang if elseAnda miliki. Dokumentasi vuex mengatakan Anda dapat menampung lebih dari 1 commitdi dalam suatu tindakan. Jadi akan logis untuk berasumsi Anda dapat melakukan mutasi tertentu tergantung pada logika. Saya melihat tindakan sebagai cara untuk menentukan mutasi WHICH ke api.
Tamb
@Tamb: Negara dan Getters keduanya menawarkan data kontekstual. Masuk akal bahwa mereka akan ditanyai sebelum memutuskan bagaimana memodifikasi Negara. Ketika informasi itu dapat diambil seluruhnya dari Negara, masuk akal bahwa seluruh logika dirangkum dalam satu Mutasi, karena ia memiliki akses ke Negara. Ini adalah prosedur operasi standar untuk penyetel. Yang kurang masuk akal adalah memiliki pendekatan yang sangat berbeda hanya karena kita sekarang perlu menanyakan Getter untuk informasi serupa.
Michael Ekoka
@Tamb: Apa yang Anda sarankan adalah bahwa ketika kita perlu menanyakan Getters kita harus mengubah pola di atas dan memindahkan logika seleksi ke Action proxy yang dapat mengakses Getter dan dapat merekatkan sekelompok Mutasi bodoh kecil. Memang berhasil, tapi masih berputar-putar dan tidak mengatasi bau busuk yang saya maksudkan dalam jawaban saya, itu hanya memindahkannya ke tempat lain.
Michael Ekoka
Dokumen mengatakan bahwa untuk menggunakan getter saat Anda harus menghitung status. Jadi sepertinya benar untuk hari ini mereka mirip dengan properti yang dihitung. Idk apa yang Anda maksudkan dengan mengatakan tindakan itu bisa merekatkan mutasi. Dokumen jelas mengatakan untuk menempatkan logika bisnis di dalam tindakan.
Tamb
15

Saya pikir jawaban TLDR adalah bahwa Mutasi dimaksudkan untuk sinkron / transaksional. Jadi, jika Anda perlu menjalankan panggilan Ajax, atau melakukan kode asinkron lainnya, Anda harus melakukannya dalam suatu Tindakan, dan kemudian melakukan mutasi setelahnya, untuk mengatur keadaan baru.

Nuh Namey
sumber
1
Ini terlihat seperti ringkasan dari dokumentasi; yang tidak ada yang salah dengan. Namun, masalah dengan jawaban ini adalah bahwa apa yang dinyatakannya belum tentu benar. Anda BISA memodifikasi keadaan di dalam mutasi saat menjalankan fungsi asinkron / AJAX, yang kemudian dapat diubah dalam panggilan balik lengkap. Saya pikir inilah yang menyebabkan begitu banyak kebingungan tentang mengapa tindakan harus digunakan untuk praktik pengembangan terbaik ketika bekerja dengan Vuex. Saya tahu itu tentu menjadi sumber kebingungan bagi saya ketika saya mulai bekerja dengan Vuex.
Erutan409
8

Perbedaan utama antara Tindakan dan Mutasi:

  1. Di dalam tindakan Anda dapat menjalankan kode asinkron tetapi tidak dalam mutasi. Jadi gunakan tindakan untuk kode asinkron kalau tidak gunakan mutasi.
  2. Di dalam tindakan Anda dapat mengakses getter, status, mutasi (melakukan itu), tindakan (mengirim mereka) dalam mutasi Anda dapat mengakses negara. Jadi, jika Anda ingin mengakses hanya negara, gunakan mutasi jika tidak gunakan tindakan.
roli roli
sumber
5

Menurut docs

Tindakannya mirip dengan mutasi , perbedaannya adalah:

  • Alih-alih bermutasi negara, tindakan melakukan mutasi.
  • Tindakan dapat berisi operasi asinkron yang sewenang-wenang .

Pertimbangkan cuplikan berikut.

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++               //Mutating the state. Must be synchronous
    }
  },
  actions: {
    increment (context) {
      context.commit('increment') //Committing the mutations. Can be asynchronous.
    }
  }
})

Penangan tindakan ( increment ) menerima objek konteks yang memperlihatkan set metode / properti yang sama pada instance store, sehingga Anda dapat memanggil context.commit untuk melakukan mutasi, atau mengakses negara dan pengambil melalui context.state dan context.getters

Abdullah Khan
sumber
1
itu panggilan positif dari fungsi 'mutasi', metode dari komponen vuejs?
Alberto Acuña
@ AlbertoAcuña Saya memiliki pertanyaan yang sama, karena ketika saya mencoba melakukan itu, ada kesalahan karena mutasi lokal tidak ditentukan.
Rutwick Gangurde
5

Penafian - Saya baru saja mulai menggunakan vuejs jadi ini hanya saya yang mengekstrapolasi maksud desain.

Debugging mesin waktu menggunakan snapshots dari negara, dan menunjukkan garis waktu tindakan dan mutasi. Secara teori kita bisa saja actionsbersama dengan rekaman negara setter dan getter untuk secara sinkron menggambarkan mutasi. Tapi kemudian:

  • Kami akan memiliki input yang tidak murni (hasil async) yang menyebabkan setter dan getter. Ini akan sulit untuk mengikuti secara logis dan setter dan getter async yang berbeda secara mengejutkan berinteraksi. Itu masih bisa terjadi dengan mutationstransaksi tetapi kemudian kita dapat mengatakan transaksi perlu ditingkatkan sebagai lawan dari kondisi perlombaan dalam tindakan. Mutasi anonim di dalam suatu tindakan bisa lebih mudah muncul kembali bug semacam ini karena pemrograman async rapuh dan sulit.
  • Log transaksi akan sulit dibaca karena tidak ada nama untuk perubahan negara. Akan jauh lebih mirip kode dan lebih sedikit bahasa Inggris, kehilangan pengelompokan mutasi logis.
  • Mungkin lebih sulit dan kurang berkinerja untuk merekam instrumen mutasi pada objek data, berbeda dengan sekarang di mana ada titik-titik berbeda yang ditentukan secara sinkron - sebelum dan sesudah panggilan fungsi mutasi. Saya tidak yakin seberapa besar masalah itu.

Bandingkan log transaksi berikut dengan mutasi bernama.

Action: FetchNewsStories
Mutation: SetFetchingNewsStories
Action: FetchNewsStories [continuation]
Mutation: DoneFetchingNewsStories([...])

Dengan log transaksi yang tidak memiliki mutasi bernama:

Action: FetchNewsStories
Mutation: state.isFetching = true;
Action: FetchNewsStories [continuation]
Mutation: state.isFetching = false;
Mutation: state.listOfStories = [...]

Saya harap Anda dapat memperkirakan dari contoh itu potensi penambahan kompleksitas dalam aksi async dan anonim di dalam tindakan.

https://vuex.vuejs.org/en/mutations.html

Sekarang bayangkan kita sedang men-debug aplikasi dan melihat log mutasi devtool. Untuk setiap mutasi yang dicatat, devtool perlu mengambil snapshot dari "sebelum" dan "setelah" negara. Namun, callback asinkron di dalam contoh mutasi di atas membuat itu tidak mungkin: callback belum dipanggil saat mutasi dilakukan, dan tidak ada cara bagi devtool untuk mengetahui kapan callback akan benar-benar dipanggil - setiap mutasi negara yang dilakukan dalam callback pada dasarnya tidak dapat dilacak!

ubershmekel
sumber
4

Mutasi:

Can update the state. (Having the Authorization to change the state).

Tindakan:

Actions are used to tell "which mutation should be triggered"

Dalam Redux Way

Mutations are Reducers
Actions are Actions

Kenapa Keduanya ??

Ketika aplikasi tumbuh, pengkodean dan garis akan meningkat, Saat itu Anda harus menangani logika dalam Tindakan tidak dalam mutasi karena mutasi adalah satu-satunya otoritas untuk mengubah keadaan, itu harus sebersih mungkin.

Gopinath Kaliappan
sumber
2

Ini juga membingungkan saya, jadi saya membuat demo sederhana.

komponen.vue

<template>
    <div id="app">
        <h6>Logging with Action vs Mutation</h6>
        <p>{{count}}</p>
        <p>
            <button @click="mutateCountWithAsyncDelay()">Mutate Count directly with delay</button>
        </p>
        <p>
            <button @click="updateCountViaAsyncAction()">Update Count via action, but with delay</button>
        </p>
        <p>Note that when the mutation handles the asynchronous action, the "log" in console is broken.</p>
        <p>When mutations are separated to only update data while the action handles the asynchronous business
            logic, the log works the log works</p>
    </div>
</template>

<script>

        export default {
                name: 'app',

                methods: {

                        //WRONG
                        mutateCountWithAsyncDelay(){
                                this.$store.commit('mutateCountWithAsyncDelay');
                        },

                        //RIGHT
                        updateCountViaAsyncAction(){
                                this.$store.dispatch('updateCountAsync')
                        }
                },

                computed: {
                        count: function(){
                                return this.$store.state.count;
                        },
                }

        }
</script>

store.js

import 'es6-promise/auto'
import Vuex from 'vuex'
import Vue from 'vue';

Vue.use(Vuex);

const myStore = new Vuex.Store({
    state: {
        count: 0,
    },
    mutations: {

        //The WRONG way
        mutateCountWithAsyncDelay (state) {
            var log1;
            var log2;

            //Capture Before Value
            log1 = state.count;

            //Simulate delay from a fetch or something
            setTimeout(() => {
                state.count++
            }, 1000);

            //Capture After Value
            log2 = state.count;

            //Async in mutation screws up the log
            console.log(`Starting Count: ${log1}`); //NRHG
            console.log(`Ending Count: ${log2}`); //NRHG
        },

        //The RIGHT way
        mutateCount (state) {
            var log1;
            var log2;

            //Capture Before Value
            log1 = state.count;

            //Mutation does nothing but update data
            state.count++;

            //Capture After Value
            log2 = state.count;

            //Changes logged correctly
            console.log(`Starting Count: ${log1}`); //NRHG
            console.log(`Ending Count: ${log2}`); //NRHG
        }
    },

    actions: {

        //This action performs its async work then commits the RIGHT mutation
        updateCountAsync(context){
            setTimeout(() => {
                context.commit('mutateCount');
            }, 1000);
        }
    },
});

export default myStore;

Setelah meneliti ini, kesimpulan yang saya dapatkan adalah bahwa mutasi adalah konvensi yang berfokus hanya pada perubahan data untuk memisahkan masalah yang lebih baik dan meningkatkan logging sebelum dan sesudah data yang diperbarui. Sedangkan tindakan adalah lapisan abstraksi yang menangani logika tingkat yang lebih tinggi dan kemudian memanggil mutasi dengan tepat

Nathaniel Rink
sumber
0

1.Dari dokumen :

Tindakannya mirip dengan mutasi, perbedaannya adalah:

  • Alih-alih bermutasi negara, tindakan melakukan mutasi.
  • Tindakan dapat berisi operasi asinkron yang sewenang-wenang.

Tindakan dapat berisi operasi asinkron, tetapi mutasi tidak bisa.

2. Kami meminta mutasi, kita dapat mengubah negara secara langsung. dan kita juga bisa dalam aksi mengubah status dengan seperti ini:

actions: {
  increment (store) {
    // do whatever ... then change the state
    store.dispatch('MUTATION_NAME')
  }
}

Tindakan dirancang untuk menangani lebih banyak hal lain, kita dapat melakukan banyak hal di sana (kita dapat menggunakan operasi asinkron) kemudian mengubah keadaan dengan mengirimkan mutasi di sana.

pesawat terbang
sumber
0

Karena tidak ada keadaan tanpa mutasi! Ketika dilakukan - sepotong logika, yang mengubah keadaan dengan cara yang dapat diduga, dieksekusi. Mutasi adalah satu-satunya cara untuk mengatur atau mengubah keadaan (jadi tidak ada perubahan langsung!), Dan selanjutnya - mereka harus sinkron. Solusi ini menggerakkan fungsi yang sangat penting: mutasi masuk ke devtools. Dan itu memberi Anda keterbacaan dan prediksi yang bagus!

Satu hal lagi - tindakan. Seperti yang telah dikatakan - tindakan melakukan mutasi. Jadi mereka tidak mengubah toko, dan tidak perlu ini sinkron. Tapi, mereka dapat mengelola bagian tambahan dari logika asinkron!

Sumit Patel
sumber
0

Tampaknya tidak perlu memiliki lapisan tambahan actionshanya untuk memanggil mutations, misalnya:

const actions = {
  logout: ({ commit }) => {
    commit("setToken", null);
  }
};

const mutations = {
  setToken: (state, token) => {
    state.token = token;
  }
};

Jadi jika menelepon actionspanggilan logout, mengapa tidak memanggil mutasi itu sendiri?

Seluruh gagasan tindakan adalah untuk memanggil beberapa mutasi dari dalam satu tindakan atau membuat permintaan Ajax atau segala jenis logika asinkron yang dapat Anda bayangkan.

Kami mungkin pada akhirnya memiliki tindakan yang membuat beberapa permintaan jaringan dan akhirnya memanggil banyak mutasi berbeda.

Jadi kami mencoba untuk memasukkan sebanyak mungkin kerumitan dari kami Vuex.Store()di kami actionsdan ini meninggalkan kami mutations, statedan getterslebih bersih dan langsung dan jatuh sejalan dengan jenis modularitas yang membuat perpustakaan seperti Vue dan React populer.

Daniel
sumber
0

Saya telah menggunakan Vuex secara profesional selama sekitar 3 tahun, dan di sini saya pikir saya telah menemukan perbedaan mendasar antara tindakan dan mutasi, bagaimana Anda bisa mendapat manfaat dari menggunakannya bersama-sama, dan bagaimana Anda dapat membuat hidup Anda lebih sulit jika Anda jangan menggunakannya dengan baik.

Tujuan utama Vuex adalah menawarkan pola baru untuk mengontrol perilaku aplikasi Anda: Reaktivitas. Idenya adalah untuk menurunkan orkestrasi keadaan aplikasi Anda ke objek khusus: toko. Ini dengan mudah memasok metode untuk menghubungkan komponen Anda langsung ke data toko Anda untuk digunakan sesuka mereka. Ini memungkinkan komponen Anda untuk fokus pada pekerjaan mereka: mendefinisikan templat, gaya, dan perilaku komponen dasar untuk disajikan kepada pengguna Anda. Sementara itu, toko menangani beban data yang berat.

Itu bukan hanya satu-satunya keuntungan dari pola ini. Fakta bahwa toko adalah sumber data tunggal untuk keseluruhan aplikasi Anda menawarkan potensi besar kegunaan kembali data ini di banyak komponen. Ini bukan pola pertama yang mencoba untuk mengatasi masalah ini komunikasi lintas-komponen, tetapi di mana itu bersinar adalah bahwa itu memaksa Anda untuk menerapkan perilaku yang sangat aman untuk aplikasi Anda dengan pada dasarnya melarang komponen Anda untuk mengubah keadaan data yang dibagikan ini , dan paksakan untuk menggunakan "titik akhir publik" untuk meminta perubahan.

Ide dasarnya adalah ini:

  • Toko memiliki keadaan internal, yang tidak boleh diakses secara langsung oleh komponen (mapState secara efektif dilarang)
  • Toko memiliki mutasi, yang merupakan modifikasi sinkron ke keadaan internal. Satu-satunya tugas mutasi adalah mengubah keadaan. Mereka seharusnya dipanggil dari suatu tindakan. Mereka harus dinamai untuk menggambarkan hal-hal yang terjadi pada negara (ORDER_CANCELED, ORDER_CREATED). Buat mereka pendek dan manis. Anda dapat melangkah melaluinya dengan menggunakan ekstensi browser Vue Devtools (bagus juga untuk debugging!)
  • Toko juga memiliki tindakan, yang harus async atau mengembalikan janji. Mereka adalah tindakan yang akan dipanggil komponen Anda ketika mereka ingin mengubah keadaan aplikasi. Mereka harus dinamai dengan tindakan berorientasi bisnis (kata kerja, yaitu cancelOrder, createOrder). Di sinilah Anda memvalidasi dan mengirim permintaan Anda. Setiap tindakan dapat memanggil komitmen berbeda pada langkah berbeda jika diperlukan untuk mengubah status.
  • Akhirnya, toko memiliki getter, yang Anda gunakan untuk mengekspos keadaan Anda ke komponen Anda. Harapkan mereka akan banyak digunakan di banyak komponen saat aplikasi Anda berkembang. Tembolok cache Vuex sangat untuk menghindari siklus perhitungan yang tidak berguna (selama Anda tidak menambahkan parameter pada pengambil Anda - cobalah untuk tidak menggunakan parameter) jadi jangan ragu untuk menggunakannya secara luas. Pastikan Anda memberikan nama yang menggambarkan sedekat mungkin status aplikasi saat ini.

Yang sedang berkata, keajaiban dimulai ketika kita mulai merancang aplikasi kita dengan cara ini. Sebagai contoh:

  • Kami memiliki komponen yang menawarkan daftar pesanan kepada pengguna dengan kemungkinan untuk menghapus pesanan tersebut
  • Komponen telah memetakan pengambil toko (deletableOrders), yang merupakan array objek dengan id
  • Komponen memiliki tombol pada setiap baris pesanan, dan kliknya dipetakan ke tindakan toko (deleteOrder) yang meneruskan objek pesanan ke sana (yang, akan kita ingat, berasal dari daftar toko itu sendiri)
  • Tindakan deleteOrder toko melakukan hal berikut:
    • itu memvalidasi penghapusan
    • ia menyimpan perintah untuk menghapus sementara
    • itu melakukan mutasi ORDER_DELETED dengan pesanan
    • ia mengirim panggilan API untuk benar-benar menghapus pesanan (ya, SETELAH memodifikasi negara!)
    • menunggu panggilan untuk mengakhiri (keadaan sudah diperbarui) dan pada kegagalan, kami memanggil mutasi ORDER_DELETE_FAILED dengan urutan yang kami simpan sebelumnya.
  • Mutasi ORDER_DELETED hanya akan menghapus pesanan yang diberikan dari daftar pesanan yang dapat dihapus (yang akan memperbarui pembuatnya)
  • Mutasi ORDER_DELETE_FAILED hanya mengembalikannya, dan memodifikasi untuk menyatakan pemberitahuan kesalahan (komponen lain, pemberitahuan kesalahan, akan melacak negara itu untuk mengetahui kapan harus menampilkan sendiri)

Pada akhirnya, kami memiliki pengalaman pengguna yang dianggap "reaktif". Dari perspektif pengguna kami, item telah dihapus segera. Sebagian besar waktu, kami berharap titik akhir kami hanya berfungsi, jadi ini sempurna. Ketika gagal, kami masih memiliki beberapa kontrol atas bagaimana aplikasi kami akan bereaksi , karena kami telah berhasil memisahkan kekhawatiran keadaan aplikasi front-end kami, dengan data aktual.

Anda tidak selalu membutuhkan toko, ingatlah. Jika Anda menemukan bahwa Anda sedang menulis toko yang terlihat seperti ini:

export default {
  state: {
    orders: []
  },
  mutations: {
    ADD_ORDER (state, order) {
       state.orders.push(order)
    },
    DELETE_ORDER (state, orderToDelete) {
       state.orders = state.orders.filter(order => order.id !== orderToDelete.id)
    }
  },
  actions: {
    addOrder ({commit}, order) {
      commit('ADD_ORDER', order)
    },
    deleteOrder ({commit}, order) {
      commit('DELETE_ORDER', order)
    }
  },
  getters: {
    orders: state => state.orders
  }
}

Bagi saya sepertinya Anda hanya menggunakan store sebagai data store, dan mungkin kehilangan aspek reaktivitasnya, dengan tidak membiarkannya juga mengendalikan variabel yang bereaksi terhadap aplikasi Anda. Pada dasarnya, Anda dapat dan mungkin harus menurunkan beberapa baris kode yang ditulis dalam komponen Anda ke toko Anda.

Alex
sumber