Apa versi baru Vuex?

Vuex adalah manajer negara untuk aplikasi Vue. Versi selanjutnya adalah Vuex 4, yang hampir siap untuk rilis resmi. Ini akan menambah dukungan untuk Vue 3, tetapi tidak akan membawa fungsionalitas baru.



Meskipun Vuex dianggap sebagai solusi yang hebat, dan banyak pengembang memilihnya sebagai pustaka manajemen negara utama mereka, mereka berharap mendapatkan lebih banyak fitur di rilis mendatang. Oleh karena itu, sementara Vuex 4 baru saja bersiap untuk dirilis, salah satu pengembangnya, Kia King Ishii (bagian dari tim inti) sudah membagikan rencana untuk versi 5 berikutnya. Perlu dicatat bahwa ini hanya rencana dan beberapa hal dapat berubah, namun, arah utama telah dipilih. Tentang dia akan dibahas.



Dengan munculnya Vue 3 dan API Komposisi , pengembang mulai membuat alternatif sederhana. Misalnya, artikel "Anda Mungkin Tidak Membutuhkan Vuex " mendemonstrasikan cara sederhana, fleksibel, dan andal untuk membuat penyimpanan berdasarkan API Komposisi dalam hubungannya dengan provide/inject



. Kita dapat berasumsi bahwa ini dan beberapa alternatif lain baik-baik saja untuk aplikasi kecil, tetapi seperti yang sering terjadi, mereka memiliki kekurangan: dokumentasi, komunitas, konvensi penamaan, integrasi, alat pengembang.







Poin terakhir sangat penting. Vue sekarang memiliki ekstensi browser yang bagus untuk membantu pengembangan dan debugging. Membuangnya bisa menjadi pemborosan besar, terutama saat membangun aplikasi besar. Untungnya, ini tidak akan terjadi dengan Vuex 5. Sedangkan untuk pendekatan alternatif, mereka akan berhasil, tetapi mereka tidak akan membawa banyak manfaat seperti solusi resmi. Karena itu, mari kita lihat keuntungan seperti apa yang mereka janjikan kepada kita.



Membuat toko



Sebelum melakukan apa pun dengan sisi, kita perlu membuatnya. Di Vuex 4, 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')
    }
  }
})

      
      





Penyimpanan juga terdiri dari 4 bagian: keadaan, tempat data disimpan; getter yang menyediakan status komputasi; mutasi diperlukan untuk mengubah status dan tindakan yang dipanggil di luar penyimpanan untuk melakukan operasi di atasnya. Biasanya, tindakan tidak hanya menyebabkan mutasi (seperti pada contoh), tetapi digunakan untuk melakukan tugas asinkron (karena mutasi harus sinkron ) atau menerapkan logika yang lebih kompleks. Seperti apa Vuex 5 itu?



import { defineStore } from 'vuex'

export const counterStore = defineStore({
  name: 'counter',
  
  state() {
    return { count: 0 }
  },
  
  getters: {
    double () {
      return this.count * 2
    }
  },
  
  actions: {
    increment () {
      this.count++
    }
  }
})

      
      





Hal pertama yang berubah adalah penggantian nama createStore



menjadi defineStore



. Beberapa saat kemudian akan jelas mengapa. Selanjutnya, ada parameter name



untuk menentukan nama toko. Sebelumnya, kita membagi sisi menjadi modul, dan nama modul dalam bentuk objek bernama. Lebih lanjut, modul-modul tersebut didaftarkan di ruang global, itulah sebabnya modul-modul tersebut tidak mandiri dan siap untuk digunakan kembali. Sebagai solusinya, parameter harus digunakan namespaced



untuk mencegah beberapa modul merespons jenis mutasi dan tindakan yang sama. Saya pikir banyak yang telah menemukan ini, tetapi saya tetap akan menambahkan tautan ke dokumentasi . Sekarang kami tidak memiliki modul, setiap penyimpanan secara default adalah penyimpanan terpisah dan independen.



Setelah menentukan namanya, kita perlu membuatnya menjadi state



fungsi yang mengembalikan status awal, bukan hanya menyetelnya. Ini sangat mirip dengan tampilannya data



di komponen. Perubahan juga memengaruhi getter, alih-alih state



menggunakan fungsi sebagai parameter this



untuk mengakses data. Pendekatan yang sama diterapkan pada tindakan, this



bukan stat



sebagai parameter. Terakhir, dan yang terpenting, mutasi digabungkan dengan game aksi. Kia merayakanbahwa mutasi sering kali menjadi penyetel sederhana, membuatnya bertele-tele, rupanya inilah alasan penghapusan. Dia tidak menyebutkan kemungkinan perubahan status di luar store, misalnya dari komponen. Di sini, kami hanya dapat mengacu pada pola Flux, yang tidak merekomendasikan melakukan hal ini dan mendorong pendekatan perubahan status dari tindakan.



Tambahan: Mereka yang menggunakan API Komposisi untuk membuat komponen akan senang mengetahui bahwa ada cara untuk membuat penyimpanan dengan cara yang sama.



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 }  
})

      
      





Pada contoh di atas, kami meneruskan nama toko sebagai argumen pertama defineStore



. Sisanya adalah API Komposisi biasa, dan hasilnya akan sama persis seperti pada contoh pada API klasik.



Inisialisasi toko



Perubahan signifikan menunggu kami di sini. Untuk menjelaskan bagaimana inisialisasi toko akan terjadi di versi ke-5, mari kita lihat bagaimana itu terjadi di versi ke-4. Saat kami membuat toko lewat createStore



, kami langsung menginisialisasi, sehingga kami dapat menggunakannya di dalam app.use



atau secara langsung.



import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

const app = createApp(App)

app.use(store)
app.mount('#app')

//        `this.$store`
//   `useStore()`   Composition API

import store from './store'

store.state.count // -> 0
store.commit('increment')
store.dispatch('increment')
store.getters.double // -> 4

      
      





Pada versi ke-5, kami mengakses setiap instance Vuex secara terpisah, yang menjamin independensi. Oleh karena itu, proses ini terlihat berbeda:



import { createApp } from 'vue'
import { createVuex } from 'vuex'
import App from './App.vue'

const app = createApp(App)
const vuex = createVuex()

app.use(vuex)
app.mount('#app')

      
      





Semua komponen sekarang memiliki kemampuan untuk mengakses semua instance Vuex secara langsung, daripada mengakses ruang global. Lihat contoh:



import { defineComponent } from 'vue'
import store from './store'

export default defineComponent({
  name: 'App',

  computed: {
    counter () {
      return this.$vuex.store(store)
    }
  }
})

      
      





Panggilan tersebut $vuex.store



membuat dan menginisialisasi penyimpanan (ingat tentang mengganti nama createStore



). Sekarang, setiap kali Anda berkomunikasi dengan repositori ini $vuex.store



, Anda akan dikembalikan ke instance yang sudah dibuat. Dalam contoh, ini this.counter



yang bisa kita gunakan lebih lanjut dalam kode. Anda juga dapat menginisialisasi toko melalui createVuex()



.



Dan tentu saja, opsi untuk API Komposisi, yang $vuex.store



digunakan sebagai gantinya useStore



.



import { defineComponent } from 'vue'
import { useStore } from 'vuex'
import store from './store'

export default defineComponent({
  setup () {
    const counter = useStore(store)

    return { counter }
  }
})

      
      





Pendekatan yang dijelaskan di atas (menginisialisasi penyimpanan melalui komponen) memiliki kelebihan dan kekurangan. Di satu sisi, ini adalah pemisahan kode dan kemampuan untuk menambahkannya hanya jika diperlukan. Di sisi lain, menambahkan ketergantungan (sekarang Anda perlu mengimpor penyimpanan setiap kali Anda berencana menggunakannya). Oleh karena itu, jika Anda ingin menggunakan DI, maka opsi 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('name', store)
app.mount('#app')

      
      





Dan kemudian membuang toko ke dalam komponen (ada keinginan untuk menggantinya name



dengan konstanta dan sudah menggunakannya):



import { defineComponent } from 'vue'

export default defineComponent({
  name: 'App',
  inject: ['name']
})

// Composition API

import { defineComponent, inject } from 'vue'

export default defineComponent({
  setup () {
    const store = inject('name')

    return { store }
  }
})

      
      





Tidak banyak kegembiraan tentang solusi ini, tetapi terlihat lebih eksplisit dan fleksibel daripada pendekatan saat ini. Hal yang sama tidak dapat dikatakan tentang penggunaan lebih lanjut. Sekarang terlihat seperti ini:



store.state.count            // State
store.getters.double         // Getters
store.commit('increment')    // Mutations
store.dispatch('increment')  // Actions

      
      





Versi baru diharapkan:



store.count        // State
store.double       // Getters
store.increment()  // Actions

      
      





Semua entitas (status, pengambil, dan tindakan) dapat diakses secara langsung, sehingga bekerja dengannya lebih mudah dan lebih logis. Pada saat yang sama itu dihapus kebutuhan mapState



, mapGetters



, mapActions



dan mapMutations



, serta menulis dihitung tambahan properti.



Berbagi



Hal terakhir yang perlu dipertimbangkan adalah berbagi. Kami ingat bahwa di Vuex 5 kami tidak lagi memiliki modul bernama dan setiap sisi terpisah dan independen. Ini memungkinkan untuk mengimpornya saat dibutuhkan dan menggunakan data sesuai kebutuhan, seperti komponen. Muncul pertanyaan logis, bagaimana cara menggunakan banyak penyimpanan secara bersamaan? Di versi 4 masih ada namespace global dan kita perlu menggunakan rootGetters



dan rootState



merujuk ke toko yang berbeda dalam lingkup ini (seperti di versi 3). Pendekatan di Vuex 5 berbeda:



// 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'

export default defineStore({
  name: 'counter',

  use () {
    return { greeter: greeterStore }
  },
  
  state () {
    return { count: 0 }
  },
  
  getters: {
    greetingCount () {
      return `${this.greeter.greeting} ${this.count}'
    }
  }
})
      
      





Kami mengimpor toko, lalu mendaftarkannya use



dan mengaksesnya. Semuanya terlihat lebih mudah jika Anda menggunakan API Komposisi:



// store/counter.js
import { ref, computed } from 'vue'
import { defineStore } from 'vuex'
import greeterStore from './greeter'

export default defineStore('counter', ({use}) => {
  const greeter = use(greeterStore)
  const count = 0

  const greetingCount = computed(() => {
    return  `${greeter.greeting} ${this.count}`
  })

  return { count, greetingCount }
})

      
      





Satu-satunya hal yang perlu disebutkan adalah bahwa ia use



bekerja dengan cara yang persis sama vuex.store



dan bertanggung jawab untuk inisialisasi toko yang benar.



Dukungan TypeScript



Dengan perubahan API dan lebih sedikit abstraksi, dukungan TypeScript di versi 4 akan jauh lebih baik, tetapi kami masih membutuhkan banyak pekerjaan manual. Rilis versi 5 akan memungkinkan untuk menambahkan jenis jika perlu, dan di mana kita menginginkannya.



Kesimpulan



Vuex 5 terlihat menjanjikan dan persis seperti yang diharapkan banyak orang (memperbaiki bug lama, menambahkan fleksibilitas). Daftar lengkap diskusi dan tampilan tim inti dapat ditemukan di repositori Vue RFC .



All Articles