Bagaimana mengatur dependensi Anda dalam aplikasi Vue

Semua orang yang mengenal Vue tahu bahwa aplikasi Vue memiliki satu titik masuk - file main.js Di sana, selain membuat instance Vue, ada impor dan semacam Injeksi Ketergantungan dari semua dependensi global Anda (arahan, komponen, plugin). Semakin besar proyek, semakin banyak ketergantungan menjadi, yang, apalagi, masing-masing memiliki konfigurasi sendiri. Hasilnya, kami mendapatkan satu file besar dengan semua konfigurasi.
Artikel ini akan membahas cara mengatur dependensi global untuk menghindari hal ini.



Mengapa menulisnya sendiri?


Banyak yang mungkin berpikir - mengapa ini perlu jika ada, misalnya, Nuxt , yang akan melakukan ini untuk Anda? Dalam proyek saya, saya juga menggunakannya, tetapi dalam proyek sederhana ini mungkin berlebihan. Selain itu, tidak ada yang membatalkan proyek dengan kode lawas yang jatuh pada Anda seperti salju di kepala Anda. Dan hubungkan kerangka kerja di sana - praktis melakukannya dari awal.

Dalang


Penyelenggara organisasi semacam itu adalah Nuxt. Itu digunakan oleh saya pada proyek besar dengan Vue.
Nuxt memiliki fitur hebat - plugin. Setiap plugin adalah file yang mengekspor suatu fungsi. Konfigurasi dilewatkan ke fungsi, yang juga akan diteruskan ke konstruktor Vue saat membuat instance, serta seluruh toko .

Selain itu, fitur yang sangat berguna, inject tersedia di setiap plugin. Itu membuat Dependency Injection ke instance root dari Vue dan ke objek store . Dan ini berarti bahwa dalam setiap komponen, dalam setiap fungsi penyimpanan, ketergantungan yang ditentukan akan tersedia melalui this .

Di mana ini bisa berguna?


Selain fakta bahwa main.js secara signifikan menurunkan berat badan, Anda juga akan mendapatkan kesempatan untuk menggunakan dependensi di mana saja dalam aplikasi tanpa impor yang tidak perlu.

Contoh utama dari Dependency Injection adalah vue-router . Ini tidak sering digunakan - untuk mendapatkan parameter dari rute saat ini, untuk membuat arahan ulang, tetapi ini adalah ketergantungan global. Jika itu bisa berguna dalam komponen apa pun, lalu mengapa tidak membuatnya global? Selain itu, berkat ini, kondisinya juga akan disimpan secara global dan diubah untuk seluruh aplikasi.

Contoh lain adalah vue-wait . Pengembang plugin ini melangkah lebih jauh dan menambahkan properti $wait tidak hanya ke instance Vue, tetapi juga ke toko vuex. Mengingat spesifikasi plugin yang spesifik, ini terbukti sangat berguna. Misalnya, toko memiliki tindakan yang disebut pada beberapa komponen. Dan dalam setiap kasus, Anda perlu menunjukkan loader pada beberapa elemen. Alih-alih memanggil $wait.start('action') dan $wait.end('action') sebelum dan setelah setiap panggilan aksi, Anda dapat memanggil metode ini sekali saja dalam aksi itu sendiri. Dan ini jauh lebih mudah dibaca dan lebih sedikit verbose daripada dispatch('wait/start', 'action' {root: true}) . Dalam hal toko, ini adalah gula sintaksis.

Dari kata ke kode


Struktur dasar proyek


Mari kita lihat seperti apa proyek itu sekarang:
src
- store
- App.vue
- main.js

main.js terlihat seperti ini:
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; new Vue({ render: h => h(App), store }).$mount('#app'); 


Kami menghubungkan ketergantungan pertama


Sekarang kami ingin menghubungkan aksioma ke proyek kami dan membuat semacam konfigurasi untuknya. Saya mengikuti terminologi Nuxt dan membuat direktori plugins di src . Di dalam direktori terdapat axios.js index.js dan axios.js .

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

Seperti disebutkan di atas, setiap plugin harus mengekspor fungsi. Pada saat yang sama, di dalam fungsi kami ingin memiliki akses ke toko dan fungsi inject .

axios.js
 import axios from 'axios'; export default function (app) { //       – , , interceptors  .. axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; axios.interceptors.request.use(config => { ... return config; }); } 

index.js :
 import Vue from 'vue'; import axios from './axios'; export default function (app) { let inject = () => {}; //   inject,        Dependency Injection axios(app, inject); //       Vue    } 


Seperti yang Anda lihat, file index.js juga mengekspor fungsi. Ini dilakukan agar dapat melewati objek app sana. Sekarang mari kita ubah main.js dan panggil fungsi ini.

main.js :
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; //    // ,    Vue,  ,     initPlugins const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); //   initPlugins    


Hasil


Pada tahap ini, kami telah mencapai bahwa kami menghapus konfigurasi plugin dari main.js dalam file terpisah.

Ngomong-ngomong, manfaat dari melewatkan objek app ke semua plugin kita adalah bahwa di dalam setiap plugin kita sekarang memiliki akses ke toko. Anda dapat menggunakannya dengan bebas dengan memanggil commit , dispatch , serta mengakses store.state dan store.getters .

Jika Anda menyukai gaya ES6, Anda bahkan dapat melakukan ini:

axios.js
 import axios from 'axios'; export default function ({store: {dispatch, commit, state, getters}}) { ... } 

Tahap Kedua - Injeksi Ketergantungan


Kami telah membuat plugin pertama dan sekarang proyek kami terlihat seperti ini:

src
- plugins
-- index.js
-- axios.js
- store
- App.vue
- main.js

Karena di sebagian besar perpustakaan di mana itu benar-benar diperlukan, Dependency Injection sudah diterapkan menggunakan Vue.use , kita akan membuat plugin sederhana kita sendiri.

Misalnya, coba ulangi apa yang vue-wait lakukan. Ini adalah perpustakaan yang agak berat, jadi jika Anda ingin menunjukkan loader pada sepasang tombol, lebih baik untuk meninggalkannya. Namun, saya tidak dapat menahan kenyamanannya dan mengulangi dalam proyeknya fungsi dasarnya, termasuk gula sintaksis di toko.

Tunggu plugin


Buat file lain di direktori plugins - wait.js

Saya sudah memiliki modul vuex, yang juga saya sebut wait . Dia melakukan tiga langkah sederhana:

- start - set properti state dari objek bernama action menjadi true
- end - menghapus dari properti properti dari suatu action bernama action
- is - get from state properti dari objek bernama action

Dalam plugin ini kita akan menggunakannya.

wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


Dan hubungkan plugin kami:

index.js :
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = () => {}; Injection axios(app, inject); wait(app, inject); } 


Fungsi injeksi


Sekarang kami menerapkan fungsi inject .
 //   2 : // name – ,       this.  ,   Vue        Dependency Injection // plugin – ,       this.  ,  ,           let inject = (name, plugin) => { let key = `$${name}`; //      app[key] = plugin; //     app app.store[key] = plugin; //     store //  Vue.prototype Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; 


Keajaiban Vue.prototype


Sekarang tentang sihir. Dokumentasi Vue mengatakan cukup untuk menulis Vue.prototype.$appName = ' '; dan $appName akan tersedia untuk this .

Namun, pada kenyataannya ternyata tidak demikian. Karena googling, tidak ada jawaban mengapa desain seperti itu tidak berhasil. Oleh karena itu, saya memutuskan untuk menghubungi penulis plugin yang sudah menerapkan ini.

Mixin global


Seperti dalam contoh kita, saya melihat kode plugin vue-wait . Mereka menawarkan implementasi seperti itu (kode sumber dibersihkan untuk kejelasan):

 Vue.mixin({ beforeCreate() { const { wait, store } = this.$options; let instance = null; instance.init(Vue, store); // inject to store this.$wait = instance; // inject to app } }); 

Alih-alih prototipe, diusulkan untuk menggunakan mixin global. Efeknya pada dasarnya sama, mungkin, dengan pengecualian beberapa nuansa. Tetapi mengingat injeksi dilakukan di toko di sini, itu tidak terlihat persis dengan cara yang benar dan sama sekali tidak sesuai dengan dokumentasi.

Tapi bagaimana kalau prototipe?


Gagasan di balik solusi prototipe yang digunakan dalam inject fungsi inject dipinjam dari Nuxt. Kelihatannya jauh lebih benar daripada mixin global, jadi saya memutuskannya.

  Vue.use(() => { // ,        if (Vue.prototype.hasOwnProperty(key)) { return; } //    ,         app  Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; //  ,    this } }); }); 


Hasil


Setelah manipulasi ini, kami mendapat kesempatan untuk mengaksesnya this.$wait dari komponen apa pun, serta metode apa pun di toko.

Apa yang terjadi


Struktur proyek:

src
- plugins
-- index.js
-- axios.js
-- wait.js
- store
- App.vue
- main.js


index.js :
 import Vue from 'vue'; import axios from './axios'; import wait from './wait'; export default function (app) { let inject = (name, plugin) => { let key = `$${name}`; app[key] = plugin; app.store[key] = plugin; Vue.use(() => { if (Vue.prototype.hasOwnProperty(key)) { return; } Object.defineProperty(Vue.prototype, key, { get () { return this.$root.$options[key]; } }); }); }; axios(app, inject); wait(app, inject); } 


wait.js
 export default function ({store: {dispatch, getters}}, inject) { const wait = { start: action => dispatch('wait/start', action), end: action => dispatch('wait/end', action), is: action => getters['wait/waiting'](action) }; inject('wait', wait); } 


axios.js
 import axios from 'axios'; export default function (app) { axios.defaults.baseURL = process.env.API_BASE_URL; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['Content-Type'] = 'application/json'; } 


main.js :
 import Vue from 'vue'; import App from './App.vue'; import store from './store'; import initPlugins from './plugins'; const app = { render: h => h(App), store }; initPlugins(app); new Vue(app).$mount('#app'); 

Kesimpulan


Sebagai hasil dari manipulasi kami menerima satu impor dan satu panggilan fungsi di file main.js Dan sekarang sudah jelas tempat untuk mencari konfigurasi untuk setiap plugin dan setiap ketergantungan global.

Saat menambahkan plugin baru, Anda hanya perlu membuat file yang mengekspor fungsi, impor ke index.js dan panggil fungsi ini.

Dalam praktik saya, struktur seperti itu telah terbukti sangat nyaman, apalagi, mudah ditransfer dari proyek ke proyek. Sekarang tidak ada rasa sakit jika Anda perlu melakukan Dependency Injection atau mengkonfigurasi plugin lain.

Bagikan pengalaman Anda dengan manajemen ketergantungan di komentar. Proyek yang berhasil!

Source: https://habr.com/ru/post/id423013/


All Articles