Vue mixins, cara eksplisit (dengan contoh plugin pengubah BEM)


Vue mixins adalah cara yang disarankan untuk berbagi fungsionalitas umum antar komponen. Mereka baik-baik saja sampai Anda menggunakan lebih dari satu untuk mereka. Itu karena mereka tersirat oleh desain dan mencemari konteks komponen Anda. Mari kita coba perbaiki ini dengan memberi mereka sebanyak mungkin kesaksian.


Tujuan


Kami ingin memiliki mixin global yang memberikan komponen apa pun yang disebut prop types dan output array kelas CSS yang disebut mods yang berasal dari baseClass .


Diberikan markup ini:


 <SampleComponent :types="['active', 'block']"></SampleComponent> 

Kami berharap memiliki ini (dengan asumsi baseClass kami adalah sample-component ):


 <div class="sample-component sample-component--active sample-component--block"></div> 

Pendekatan naif


Dari hanya membaca dokumentasi Vue, pemikiran pertama Anda mungkin hanya menggunakan penggabungan properti bawaan dan menyediakan mods sebagai properti yang dikomputasi dalam komponen yang diinginkan.


 // bemMods.js export default (baseClass) => ({ props: { types: { type: Array, default: () => [] } }, computed: { mods() { return this.types.map(type => `${baseClass}--${type}`); } } }) 

 // SampleComponent.vue <template> <div class="sample-component" :class="mods"><slot /></div> </template> 

 import bemMods from 'bemMods.js'; export default { name: 'SampleComponent', mixins: [ bemMods('sample-component') ] } 

Pendekatan ini menderita banyak masalah:


  1. Kode Boilerplate di setiap komponen. (setidaknya dalam pendekatan Vue)
  2. Ketergantungan pada argumen baseClass .
  3. Tidak ada indikasi yang jelas dari mana properti mods berasal.
  4. Konflik nama mudah dilakukan.

Kami akan mencoba memperbaiki semua masalah ini di langkah berikutnya.


Mixin dengan ekspor eksplisit


Vue memiliki mekanisme Injeksi Ketergantungan, yang disebut Inject \ Provid . Ini berpotensi memecahkan masalah kita dengan konteks polusi.


Pada awalnya, mari kita beralih dari mixin sederhana ke sebuah plugin, yang menerima opsi, yang akan kita gunakan nanti untuk menghindari konflik nama.


Kedua, kita juga bisa menggunakan kembali nama komponen kita sebagai baseClass dan tidak memasukkan itu sebagai opsi khusus di setiap komponen tunggal.


Dan terakhir kita akan meninggalkan opsi untuk melewatkan baseClass sebagai argumen fungsi jika baseClass komponen kita tidak cocok dengan namanya.


 // bemMods.js // Converts ComponentName to component-name const transformName = string => string.replace(/\s+/g, '-').toLowerCase(); const install = (Vue, { propName = 'types', modsName = 'mods' } = {}) => { Vue.mixin({ props: { // Prop name is now dynamic and allows to avoid conflits [propName]: { type: Array, default: () => [], } }, // Dependency injection forces us to explicitly require that function provide: { [modsName](baseClass) { baseClass = baseClass || transformName(this.$options.name); return (this[propName] || []).map(type => `${baseClass}--${type}`); } } }); }; export default { install }; 

Kami sekarang siap untuk mendaftarkan plugin kami secara global.


 import Vue from 'vue'; import bemMods from 'bemMods.js'; Vue.use(bemMods); 

Kami juga dapat menyesuaikan bagaimana alat peraga kami dipanggil, dengan menyediakan objek opsi.


 import Vue from 'vue'; import bemMods from 'bemMods.js'; Vue.use(bemMods, { propName: 'modifiers', modsName: 'classes' }); 

Dan inilah tampilan komponen kami setelah refactoring mixin:


 <template> <div class="sample-component" :class="mods"><slot /></div> </template> 

 export default { name: 'SampleComponent', // Explicit property inject: ['mods'] } 

Mari kita bayangkan komponen kita tidak memiliki name atau memiliki baseClass berbeda dari namanya:


 <template> <div class="special-component" :class="mods('snowflake')"><slot /></div> </template> 

 export default { name: 'SpecialComponent', inject: ['mods'] } 

Atau jika kita ingin siap untuk penghapusan refactoring atau plugin:


 export default { name: 'SomeComponent', inject: { // If mixin export property changes name it's now possible to replace it in every single component instance withouth any additional rework 'mods': { // In this case 'mods' becomes 'classes' from: 'classes', } } } 

Anda juga dapat menggunakan Symbol sebagai nama mod untuk sepenuhnya menghilangkan konflik nama, tetapi itu mengharuskan Anda untuk memasukkan simbol itu di setiap komponen tunggal di mana Anda ingin menggunakan bemMods .


Kami tidak secara implisit menentukan nama prop kami, tetapi itu adalah batasan mixin inti, yang kami coba atasi dengan nama prop kustom di konfigurasi plugin.


Semoga ini bisa membantu Anda dan Anda telah menemukan cara menulis mixin yang lebih baik untuk Vue.

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


All Articles