Petua & trik untuk membuat komponen UI boleh diguna semula

Gambar oleh Farzard Nazifi

Dalam artikel ini saya ingin berkongsi beberapa tips dan trik yang saya gunakan semasa membina perpustakaan frontend utama kami menggunakan Ember.js. Tidak ada hubungan dengannya sebelum ini, ia merupakan peluang pembelajaran yang hebat. Saya harap anda menikmatinya! Sila ambil perhatian, kod yang digunakan untuk memberi contoh idea di dalam artikel mengandungi hanya maklumat yang cukup untuk mendapatkan titik sebaliknya. Ia juga menggunakan istilah Ember.js, tetapi konsep-konsep itu dimaksudkan untuk menjadi kerangka-agnostik.

Objektifnya

Untuk meletakkannya dengan mudah, keperluan untuk membina perpustakaan adalah seperti berikut:

  1. Ia mesti produktif.
  2. Ia mesti dipelihara.
  3. Ia mesti konsisten.

Pendekatan

Kurangkan logika perniagaan

Salah satu masalah yang sering saya hadapi pada projek adalah komponen yang mengandungi terlalu banyak logik di dalamnya. Oleh itu, melaksanakan tugas yang, secara teorinya, daripada skop mereka.

Sebelum melaksanakan fungsi apa-apa, adalah baik untuk menggariskan beberapa tugas yang komponen itu bertanggungjawab.

Bayangkan kami sedang membina komponen butang.

Saya ingin dapat:

  • Maklumkan jenis butang itu - Utama atau biasa
  • Maklumkan kandungan yang dipaparkan di dalam butang (Ikon dan teks)
  • Lumpuhkan atau dayakan butang itu
  • Lakukan beberapa tindakan setelah klik

Mempunyai garis kecil ini, tarik bahagian-bahagian yang berlainan yang terlibat dalam proses membina komponen ini. Cuba untuk mengenal pasti perkara yang boleh diletakkan.

1 - Jenis dan kandungan adalah komponen yang khusus, supaya mereka boleh dimasukkan ke dalam fail komponen.

Oleh kerana jenisnya - sedikit sebanyak - diperlukan, mari tambahkan pengesahan sekiranya tiada nilai diberikan.

const type = get (this, 'type');
jenis const = {
  utama: 'btn - primary',
  biasa: 'btn - biasa',
}
kembali (jenis)? jenis [jenis]: types.regular;

Saya suka memetakan sifat-sifat ke dalam objek kerana ia membolehkan perkara untuk skala tanpa banyak usaha - sekiranya kita memerlukan butang bahaya atau apa sahaja yang menyukainya.

2 - Negeri kurang upaya boleh didapati pada komponen yang berbeza seperti input. Untuk mengelakkan pengulangan, tingkah laku ini boleh dipindahkan ke dalam modul atau mana-mana struktur yang dikongsi - orang menyebutnya sebagai campuran.

3 - Tindakan klik boleh didapati dalam komponen yang berbeza. Jadi ia juga boleh dipindahkan ke fail lain dan tidak mengandungi logik di dalamnya - hanya memanggil panggil balik yang disediakan oleh pemaju.

Dengan cara ini, kita boleh mempunyai idea tentang apa yang diperlukan oleh komponen kami untuk ditangani semasa membantu menggariskan seni bina asas yang menyokong pengembangan.

Mengasingkan UI yang boleh diguna semula

Interaksi UI tertentu adalah umum di antara komponen yang berbeza, seperti:

  • Membolehkan / melumpuhkan - contohnya. butang, input
  • Kembangkan / Mengecut - contohnya. runtuh, senarai jatuh turun
  • Show / hide - Cukup banyak segalanya

Sifat-sifat ini sering digunakan hanya untuk mengawal keadaan visual - mudah-mudahan.

Mengekalkan tatanama yang konsisten dalam pelbagai komponen. Semua tindakan yang berkaitan dengan keadaan visual boleh dipindahkan ke mixin.

/ * UIStateMixin * /
lumpuhkan () {
  tetapkan (ini, 'dilumpuhkan', benar);
  kembalikan ini;
},
dayakan () {
  tetapkan (ini, 'dilumpuhkan', palsu ');
  kembalikan ini;
},

Setiap kaedah hanya bertanggungjawab untuk mengubah pembolehubah tertentu dan mengembalikan konteks semasa untuk chaining, seperti:

butang
  .disable ()
  .showLoadingIndicator ();

Pendekatan ini boleh diperluaskan. Ia boleh menerima konteks yang berbeza dan mengawal pemboleh ubah luaran daripada menggunakan yang dalaman. Sebagai contoh:

_getCurrentDisabledAttr () {
  kembali (isPresent (dapatkan (ini, 'dilumpuhkan')))
    ? 'dilumpuhkan' / * Parameter luaran * /
    : 'isDisabled'; / * Pembolehubah dalaman * /
},
dayakan (konteks) {
  tetapkan (konteks || ini, ini._getCurrentDisabledAttr (), palsu);
  kembalikan ini;
}

Fungsi pangkalan abstrak

Setiap komponen mengandungi rutin tertentu. Rutin ini mesti dilakukan tanpa mengira tujuan komponen. Contohnya, mengesahkan panggilan balik sebelum mencetuskannya.

Kaedah lalai ini juga boleh dipindahkan ke mixins mereka sendiri, seperti:

/ * BaseComponentMixin * /
_isCallbackValid (panggil balikName) {
  const callback = get (ini, callbackName);
  
  kembali !! (isPresent (panggil balik) && callof typeback === 'function');
},
_handleCallback (panggil balik, params) {
  jika (! this._isCallbackValid (panggil balik)) {
    buang Ralat baru (/ * message * /);
  }
  this.sendAction (panggil balik, params);
},

Dan kemudian dimasukkan ke dalam komponen.

/ * Komponen * /
onClick (params) {
  this._handleCallback ('onClick', params);
}

Ini memastikan kerangka asas anda tetap konsisten. Ia juga membolehkan pengembangan dan integrasi dengan perisian pihak ketiga. Tetapi sila, janganlah menjadi abstrak filosofis.

Mengarang komponen

Elakkan menulis semula fungsi seberapa banyak yang anda boleh. Pengkhususan boleh dicapai. Ia boleh dilakukan melalui komposisi dan pengelompokan. Serta menaikkan komponen yang lebih kecil bersama-sama untuk menghasilkan komponen baru.

Sebagai contoh:

Komponen asas: Butang, jatuh turun, input.
Butang jatuh = = butang + dropdown
Autocomplete => input + dropdown
Pilih => input (readonly) + dropdown

Dengan cara ini, setiap komponen mempunyai tugasnya sendiri. Setiap mengendalikan keadaan dan parameternya sendiri manakala komponen pembalut mengendalikan logik khususnya.

Pemisahan kebimbangan yang paling baik.

Memecah kebimbangan

Apabila menyusun komponen yang lebih kompleks, terdapat kebimbangan pemisahan. Anda boleh membahagi keprihatinan antara komponen yang berbeza

Katakan kita sedang membina komponen pilih.

{{form-select binding = productId items = items}}
barang = [
  {description: 'Product # 1', nilai: 1},
  {description: 'Product # 2', nilai: 2}
]

Secara dalaman, kami mempunyai komponen input mudah dan drop-down.

{{form-input binding = _description}}
{{ui-dropdown items = items onSelect = (action 'selectItem')}}

Tugas utama kami adalah untuk membentangkan penerangan kepada pengguna, tetapi ia tidak mempunyai makna kepada aplikasi kami - nilai itu.

Apabila memilih pilihan, anda memecah objek, menghantar deskripsi ke input kami melalui pembolehubah dalaman sambil menolak nilai sehingga pengawal, mengemas kini pemboleh ubah terikat.

Konsep ini boleh digunakan pada komponen di mana nilai terikat mesti diubah, seperti nombor, bidang autoklampung atau pilih. Datepickers juga boleh melaksanakan tingkah laku ini. Mereka boleh membongkar tarikh sebelum mengemas kini pemboleh ubah terikat semasa menyampaikan nilai bertopeng kepada pengguna.

Risiko semakin tinggi apabila transformasi meningkat dalam kerumitan. Dengan logik berlebihan atau perlu menyokong acara - jadi fikirkanlah sebelum melaksanakan pendekatan ini.

Pratetap vs Komponen Baru

Kadang-kadang perlu untuk mengoptimumkan komponen dan perkhidmatan untuk memudahkan pembangunan. Ini dihantar dalam bentuk pratet atau komponen baru.

Pratetap adalah parameter. Apabila dimaklumkan, mereka menetapkan nilai-nilai yang telah dipratentukan pada komponen itu, memudahkan pengisytiharannya. Walau bagaimanapun, komponen baru biasanya lebih khusus versi komponen asas.

Bahagian yang sukar adalah mengetahui kapan untuk melaksanakan pratetap atau membuat komponen baru. Saya menggunakan garis panduan berikut apabila membuat keputusan ini:

Bila untuk membuat pratetap

1 - Corak penggunaan berulang

Ada kalanya komponen tertentu digunakan semula di pelbagai tempat dengan parameter yang sama. Dalam kes ini, saya suka mempercepat pratetap komponen baru, terutamanya apabila komponen asas mempunyai bilangan parameter yang berlebihan.

/ * Perlaksanaan tetap * /
{{form-autocomplete
    mengikat = productId
    url = "produk" / * URL yang akan diambil * /
    labelAttr = "description" / * Atribut yang digunakan sebagai label * /
    valueAttr = "id" / * Atribut yang digunakan sebagai nilai * /
    apiAttr = "produk" / * Param dihantar atas permintaan * /
}}
/ * Praset * /
{{form-autocomplete
    pratet = "produk"
    mengikat = productId
}}

Nilai dari pratetap hanya ditetapkan jika parameter tersebut tidak dimaklumkan, mengekalkan kelenturannya.

/ * Pelaksanaan modul pratetap yang lebih lemah * /
const presets = {
  produk: {
    url: 'produk',
    labelAttr: 'description',
    valueAttr: 'id',
    apiAttr: 'produk',
  },
}
const attrs = presets [get (this, 'preset')];
Object.keys (attrs) .forEach ((prop) => {
  jika (! dapatkan (this, prop)) {
    tetapkan (ini, prop, attrs [prop]);
  }
});

Pendekatan ini mengurangkan pengetahuan yang diperlukan untuk menyesuaikan komponen anda. Pada masa yang sama, ia memudahkan penyelenggaraan dengan membolehkan anda mengemas kini nilai lalai di satu tempat.

2 - Komponen asas terlalu rumit

Apabila komponen asas yang anda gunakan untuk membuat komponen yang lebih spesifik menerima terlalu banyak parameter. Oleh itu, mewujudkannya akan menjana beberapa masalah. Sebagai contoh:

  • Anda perlu menyuntik sebahagian besar - jika tidak semua - parameter dari komponen baru kepada komponen asas. Memandangkan semakin banyak komponen yang diperoleh daripadanya, sebarang kemas kini pada komponen asas akan mencerminkan banyak perubahan. Oleh itu, membawa kepada kejadian bug yang tinggi.
  • Oleh kerana lebih banyak komponen dibuat, semakin sukar untuk mendokumentasikan dan menghafal nuansa yang berbeza. Ini amat sesuai untuk pemaju baru.

Bila hendak membuat komponen baru

1 - Memperluas fungsi

Ia boleh membuat komponen baharu apabila memanjangkan fungsi dari komponen yang lebih mudah. Ia membantu anda mengelakkan logik khusus komponen yang bocor ke komponen lain. Ini amat berguna semasa melaksanakan tingkah laku yang lebih baik.

/ * Akuan * /
{{ui-button-dropdown items = items}}
/ * Di bawah tudung * /
{{# ui-button onClick = (action 'toggleDropdown')}}
  {{label}}  
{{/ ui-button}}
{{#if isExpanded}}
  {{ui-dropdown items = items}}
{{/ if}}

Contoh di atas menggunakan komponen butang. Ini meluaskan susun aturnya untuk menyokong ikon tetap semasa termasuk komponen drop-down dan keadaan penglihatannya.

2 - Parameter menghias

Ada sebab lain untuk membuat komponen baharu. Ini adalah apabila perlu untuk mengawal ketersediaan parameter atau menghiasi nilai lalai.

/ * Akuan * /
{{form-datepicker onFocus = (action 'doSomething')}}
/ * Di bawah tudung * /
{{form-input onFocus = (action '_onFocus')}}
_onFocus () {
  $ (this.element)
    .find ('input')
    .select (); / * Pilih nilai medan pada fokus * /
  this._handleCallback ('onFocus'); / * Mencetuskan panggil balik param * /
}

Dalam contoh ini, ia diberikan kepada komponen fungsi yang perlu dipanggil apabila medan itu ditumpukan.

Secara dalaman, bukannya melepaskan panggilan balik terus ke komponen asas, ia melangkah fungsi dalaman. Ini melaksanakan tugas tertentu (memilih nilai medan) dan kemudian panggilan panggil balik yang disediakan.

Ia tidak mengalihkan semua parameter yang diterima oleh komponen input asas. Ini membantu mengawal ruang lingkup fungsi tertentu. Ia juga mengelakkan pengesahan yang tidak perlu.

Dalam kes saya, acara onllur digantikan dengan acara lain - onChange. Ini mencetuskan apabila pengguna sama ada mengisi medan atau memilih tarikh pada kalendar.

Kesimpulannya

Apabila membina komponen anda, pertimbangkan sebelah anda serta sesiapa yang menggunakan komponen itu dalam kehidupan seharian mereka. Dengan cara ini, setiap orang menang.

Hasil terbaik datang dari semua orang dalam kumpulan melakukan apa yang terbaik untuk dirinya sendiri dan kumpulan - John Nash

Juga, jangan malu untuk meminta maklum balas. Anda akan sentiasa mencari sesuatu yang boleh dilakukan.

Untuk mempertajam kemahiran kejuruteraan perisian anda, saya sarankan mengikuti siri Eric Elliott's "Composing Software". Ia hebat!

Nah, saya harap anda menikmati artikel ini. Sila ambil konsep ini, bertukar idea anda sendiri dan kongsi dengan kami!

Juga, jangan ragu untuk menghubungi saya di twitter @gcolombo_! Saya suka mendengar pendapat anda dan juga bekerja bersama.

Terima kasih!