Apa yang Akan Datang ke VueX?
Diterbitkan: 2022-03-10Vuex adalah solusi untuk manajemen negara dalam aplikasi Vue. Versi berikutnya — Vuex 4 — sedang melewati langkah terakhir sebelum dirilis secara resmi. Rilis ini akan membawa kompatibilitas penuh dengan Vue 3, tetapi tidak menambahkan fitur baru. Sementara Vuex selalu menjadi solusi yang kuat, dan pilihan pertama bagi banyak pengembang untuk manajemen negara di Vue, beberapa pengembang berharap untuk melihat lebih banyak masalah alur kerja ditangani. Namun, bahkan saat Vuex 4 baru saja diluncurkan, Kia King Ishii (anggota tim inti Vue) berbicara tentang rencananya untuk Vuex 5, dan saya sangat senang dengan apa yang saya lihat sehingga saya harus membagikannya kepada Anda semua. Perhatikan bahwa paket Vuex 5 belum diselesaikan, jadi beberapa hal mungkin berubah sebelum Vuex 5 dirilis, tetapi jika sebagian besar akhirnya mirip dengan apa yang Anda lihat di artikel ini, itu akan menjadi peningkatan besar bagi pengalaman pengembang.
Dengan munculnya Vue 3 dan API komposisinya, orang-orang telah mencari alternatif sederhana yang dibuat dengan tangan. Misalnya, Anda Mungkin Tidak Membutuhkan Vuex menunjukkan pola yang relatif sederhana, namun fleksibel dan kuat untuk menggunakan API komposisi bersama dengan provide/inject
untuk membuat penyimpanan status bersama. Seperti yang dinyatakan Gabor dalam artikelnya, ini (dan alternatif lain) hanya boleh digunakan dalam aplikasi yang lebih kecil karena mereka tidak memiliki semua hal yang tidak secara langsung tentang kode: dukungan komunitas, dokumentasi, konvensi, integrasi Nuxt yang baik, dan pengembang peralatan.
Yang terakhir selalu menjadi salah satu masalah terbesar bagi saya. Ekstensi browser Vue devtools selalu menjadi alat yang luar biasa untuk men-debug dan mengembangkan aplikasi Vue, dan kehilangan inspektur Vuex dengan "perjalanan waktu" akan menjadi kerugian yang cukup besar untuk men-debug aplikasi non-sepele.

Untungnya, dengan Vuex 5 kita bisa memiliki kue dan memakannya juga. Ini akan berfungsi lebih seperti alternatif API komposisi ini tetapi tetap mempertahankan semua manfaat menggunakan perpustakaan manajemen negara resmi. Sekarang mari kita lihat apa yang akan berubah.
Mendefinisikan Toko
Sebelum kita dapat melakukan apa pun dengan toko Vuex, kita perlu mendefinisikannya. Di Vuex 4, definisi toko akan terlihat seperti ini:
import { createStore } from 'vuex' export const counterStore = createStore({ state: { count: 0 }, getters: { double (state) { return state.count * 2 } }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
Setiap toko memiliki empat bagian: state
menyimpan data, getters
memberi Anda status terkomputasi, mutations
digunakan untuk mengubah status, dan actions
adalah metode yang dipanggil dari luar penyimpanan untuk menyelesaikan apa pun yang terkait dengan penyimpanan. Biasanya, tindakan tidak hanya melakukan mutasi seperti yang ditunjukkan contoh ini. Sebaliknya, mereka digunakan untuk melakukan tugas asinkron karena mutasi harus sinkron atau mereka hanya menerapkan fungsionalitas yang lebih rumit atau multi-langkah. Tindakan juga tidak dapat mengubah statusnya sendiri; mereka harus menggunakan mutator. Jadi seperti apa tampilan Vuex 5?
import { defineStore } from 'vuex' export const counterStore = defineStore({ name: 'counter', state() { return { count: 0 } }, getters: { double () { return this.count * 2 } }, actions: { increment () { this.count++ } } })
Ada beberapa perubahan yang perlu diperhatikan di sini. Pertama, alih-alih createStore
, kami menggunakan defineStore
. Perbedaan ini dapat diabaikan, tetapi ada karena alasan semantik, yang akan kita bahas nanti. Selanjutnya, kita perlu memberikan name
untuk toko, yang tidak kita perlukan sebelumnya. Di masa lalu, modul memiliki namanya sendiri, tetapi tidak disediakan oleh modul itu sendiri; mereka hanyalah nama properti yang ditetapkan oleh toko induk yang menambahkannya. Sekarang, tidak ada modul . Sebagai gantinya, setiap modul akan menjadi toko terpisah dan memiliki nama. Nama ini digunakan oleh registri Vuex, yang akan kita bicarakan nanti.
Setelah itu, kita perlu membuat state
sebagai fungsi yang mengembalikan status awal alih-alih hanya mengaturnya ke status awal. Ini mirip dengan opsi data
pada komponen. Kami menulis getters
sangat mirip dengan yang kami lakukan di Vuex 4, tetapi alih-alih menggunakan state
sebagai parameter untuk setiap pengambil, Anda bisa menggunakan this
untuk mendapatkan status. Dengan cara yang sama, actions
tidak perlu khawatir tentang objek context
yang diteruskan: mereka hanya dapat menggunakan this
untuk mengakses semuanya. Akhirnya, tidak ada mutations
. Sebaliknya, mutasi digabungkan dengan actions
. Kia mencatat bahwa terlalu sering, mutasi hanya menjadi setter sederhana, membuat mereka bertele-tele, jadi mereka menghapusnya. Dia tidak menyebutkan apakah itu "ok" untuk mengubah status langsung dari luar toko, tetapi kami pasti diizinkan dan didorong untuk mengubah status langsung dari suatu tindakan dan pola Flux mengerutkan kening pada mutasi langsung status.
Catatan : Bagi mereka yang lebih memilih API komposisi daripada API opsi untuk membuat komponen, Anda akan senang mengetahui bahwa ada juga cara untuk membuat penyimpanan dengan cara yang mirip dengan menggunakan API komposisi.
import { ref, computed } from 'vue' import { defineStore } from 'vuex' export const counterStore = defineStore('counter', { const count = ref(0) const double = computed(() => count.value * 2) function increment () { count.value++ } return { count, double, increment } })
Seperti yang ditunjukkan di atas, nama akan diteruskan sebagai argumen pertama untuk defineStore
. Sisanya terlihat seperti fungsi komposisi untuk komponen. Ini akan menghasilkan hasil yang sama persis seperti contoh sebelumnya yang menggunakan API opsi.
Mendapatkan Instansiasi Toko
Di Vuex 4, banyak hal telah berubah dari Vuex 3, tetapi saya hanya akan melihat v4 agar semuanya tidak lepas kendali. Di v4, saat Anda memanggil createStore
, Anda sudah membuat instance-nya. Anda kemudian dapat menggunakannya di aplikasi Anda, baik melalui app.use
atau langsung:
import { createApp } from 'vue' import App from './App.vue' // Your root component import store from './store' // The store definition from earlier const app = createApp(App) app.use(store) app.mount('#app') // Now all your components can access it via `this.$store` // Or you can use in composition components with `useStore()` // ----------------------------------------------- // Or use directly... this is generally discouraged import store from './store' store.state.count // -> 0 store.commit('increment') store.dispatch('increment') store.getters.double // -> 4
Ini adalah satu hal yang membuat Vuex 5 sedikit lebih rumit daripada di v4. Setiap aplikasi sekarang bisa mendapatkan instance terpisah dari Vuex, yang memastikan bahwa setiap aplikasi dapat memiliki instance terpisah dari toko yang sama tanpa berbagi data di antara mereka. Anda dapat membagikan instance Vuex jika Anda ingin membagikan instance toko antar aplikasi.

import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' // Your root component const app = createApp(App) const vuex = createVuex() // create instance of Vuex app.use(vuex) // use the instance app.mount('#app')
Sekarang semua komponen Anda memiliki akses ke instance Vuex. Alih-alih memberikan definisi toko Anda secara langsung, Anda kemudian mengimpornya ke dalam komponen tempat Anda ingin menggunakannya dan menggunakan instance Vuex untuk membuat instance dan mendaftarkannya:
import { defineComponent } from 'vue' import store from './store' export default defineComponent({ name: 'App', computed: { counter () { return this.$vuex.store(store) } } })
Memanggil $vuex.store
, membuat instance dan mendaftarkan store dalam instance Vuex. Sejak saat itu, setiap kali Anda menggunakan $vuex.store
di toko itu, itu akan mengembalikan toko yang sudah dipakai alih-alih membuat instance lagi. Anda dapat memanggil metode store
langsung pada instance Vuex yang dibuat oleh createVuex()
.
Sekarang toko Anda dapat diakses pada komponen tersebut melalui this.counter
. Jika Anda menggunakan komposisi API untuk komponen Anda, Anda dapat menggunakan useStore
sebagai ganti this.$vuex.store
:
import { defineComponent } from 'vue' import { useStore } from 'vuex' // import useStore import store from './store' export default defineComponent({ setup () { const counter = useStore(store) return { counter } } })
Ada pro dan kontra untuk mengimpor toko langsung ke komponen dan membuat instance di sana. Ini memungkinkan Anda untuk membagi kode dan dengan malas memuat toko hanya di tempat yang diperlukan, tetapi sekarang ini adalah ketergantungan langsung alih-alih disuntikkan oleh orang tua (belum lagi Anda perlu mengimpornya setiap kali Anda ingin menggunakannya). Jika Anda ingin menggunakan injeksi ketergantungan untuk menyediakannya di seluruh aplikasi, terutama jika Anda tahu itu akan digunakan di akar aplikasi di mana pemisahan kode tidak akan membantu, maka Anda bisa menggunakan provide
:
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' import store from './store' const app = createApp(App) const vuex = createVuex() app.use(vuex) app.provide('store', store) // provide the store to all components app.mount('#app')
Dan Anda bisa menyuntikkannya ke komponen mana pun yang akan Anda gunakan:
import { defineComponent } from 'vue' export default defineComponent({ name: 'App', inject: ['store'] }) // Or with Composition API import { defineComponent, inject } from 'vue' export default defineComponent({ setup () { const store = inject('store') return { store } } })
Saya tidak senang dengan verbositas ekstra ini, tetapi ini lebih eksplisit dan lebih fleksibel, yang saya sukai. Jenis kode ini umumnya ditulis sekali langsung di awal proyek dan kemudian tidak mengganggu Anda lagi, meskipun sekarang Anda harus menyediakan setiap toko baru atau mengimpornya setiap kali Anda ingin menggunakannya, tetapi mengimpor atau menyuntikkan modul kode adalah cara kita umumnya harus bekerja dengan hal lain, jadi itu hanya membuat Vuex bekerja lebih sesuai dengan cara orang cenderung bekerja.
Menggunakan Toko
Selain sebagai penggemar fleksibilitas dan cara baru mendefinisikan toko dengan cara yang sama seperti komponen menggunakan API komposisi, ada satu hal lagi yang membuat saya lebih bersemangat daripada yang lainnya: bagaimana toko digunakan. Begini tampilannya saat menggunakan toko di Vuex 4.
store.state.count // Access State store.getters.double // Access Getters store.commit('increment') // Mutate State store.dispatch('increment') // Run Actions
State
, getters
, mutations
, dan actions
semuanya ditangani dengan cara yang berbeda melalui properti atau metode yang berbeda. Ini memiliki keuntungan dari keeksplisitan, yang saya puji sebelumnya, tetapi keeksplisitan ini tidak benar-benar memberi kita apa-apa. Dan API ini semakin sulit digunakan saat Anda menggunakan modul dengan namespace. Sebagai perbandingan, Vuex 5 terlihat bekerja persis seperti yang biasanya Anda harapkan:
store.count // Access State store.double // Access Getters (transparent) store.increment() // Run actions // No Mutators
Semuanya — status, getter, dan tindakan — tersedia langsung di root toko, membuatnya mudah digunakan dengan jauh lebih sedikit verbositas dan praktis menghilangkan semua kebutuhan untuk menggunakan mapState
, mapGetters
, mapActions
dan mapMutations
untuk API opsi atau untuk menulis pernyataan yang computed
ekstra atau fungsi sederhana untuk komposisi API. Ini hanya membuat toko Vuex terlihat dan bertindak seperti toko biasa yang Anda buat sendiri, tetapi ia mendapatkan semua manfaat dari plugin, alat debugging, dokumentasi resmi, dll.
Toko Penulisan
Aspek terakhir dari Vuex 5 yang akan kita lihat hari ini adalah komposisi. Vuex 5 tidak memiliki modul namespace yang semuanya dapat diakses dari satu toko. Masing-masing modul tersebut akan dipecah menjadi toko yang benar-benar terpisah. Itu cukup sederhana untuk menangani komponen: mereka hanya mengimpor toko mana pun yang mereka butuhkan dan menjalankannya serta menggunakannya. Namun bagaimana jika satu toko ingin berinteraksi dengan toko lain? Di v4, penspasian nama memperumit semuanya, jadi Anda perlu menggunakan ruang nama dalam panggilan commit
dan dispatch
, gunakan rootGetters
dan rootState
dan kemudian naik ke ruang nama yang ingin Anda akses getter dan statusnya. Inilah cara kerjanya di Vuex 5:
// store/greeter.js import { defineStore } from 'vuex' export default defineStore({ name: 'greeter', state () { return { greeting: 'Hello' } } }) // store/counter.js import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore({ name: 'counter', // Then `use` the store use () { return { greeter: greeterStore } }, state () { return { count: 0 } }, getters: { greetingCount () { return `${this.greeter.greeting} ${this.count}' // access it from this.greeter } } })
Dengan v5, kami mengimpor toko yang ingin kami gunakan, lalu mendaftarkannya dengan use
dan sekarang dapat diakses di seluruh toko dengan nama properti apa pun yang Anda berikan. Hal-hal bahkan lebih sederhana jika Anda menggunakan variasi API komposisi dari definisi toko:
// store/counter.js import { ref, computed } from 'vue' import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore('counter', ({use}) => { // `use` is passed in to function const greeter = use(greeterStore) // use `use` and now you have full access const count = 0 const greetingCount = computed(() => { return `${greeter.greeting} ${this.count}` // access it like any other variable }) return { count, greetingCount } })
Tidak ada lagi modul dengan namespace. Setiap toko terpisah dan digunakan secara terpisah. Anda dapat menggunakan use
untuk membuat toko tersedia di dalam toko lain untuk menyusunnya. Dalam kedua contoh, use
pada dasarnya hanya mekanisme yang sama seperti vuex.store
dari sebelumnya dan mereka memastikan bahwa kita membuat instance toko dengan instance Vuex yang benar.
Dukungan TypeScript
Untuk pengguna TypeScript, salah satu aspek terbesar dari Vuex 5 adalah bahwa penyederhanaan membuatnya lebih mudah untuk menambahkan tipe ke semuanya. Lapisan abstraksi yang dibuat oleh versi Vuex yang lebih lama hampir tidak mungkin dan sekarang, dengan Vuex 4, mereka meningkatkan kemampuan kita untuk menggunakan tipe, tetapi masih ada terlalu banyak pekerjaan manual untuk mendapatkan jumlah dukungan tipe yang layak, sedangkan di v5 , Anda dapat menempatkan tipe Anda sebaris, seperti yang Anda harapkan dan harapkan.
Kesimpulan
Vuex 5 terlihat hampir persis seperti yang saya — dan mungkin banyak orang lain — harapkan, dan saya merasa itu tidak bisa segera datang. Ini menyederhanakan sebagian besar Vuex, menghilangkan beberapa overhead mental yang terlibat, dan hanya menjadi lebih rumit atau bertele-tele di mana ia menambahkan fleksibilitas. Tinggalkan komentar di bawah tentang apa yang Anda pikirkan tentang perubahan ini dan perubahan apa yang mungkin Anda buat sebagai gantinya atau sebagai tambahan. Atau langsung ke sumbernya dan tambahkan RFC (Permintaan Komentar) ke daftar untuk melihat pendapat tim inti.