Foto Media Sosial oleh Andre Taissin di Unsplash
Cara termudah untuk menambah elemen bawaan DOM:
Tidak diperlukan polyfill , semua browser modern berfungsi™️
dimungkinkan untuk memperluas HTML , SVG , atau namespace khusus lainnya, seperti MathML , tanpa masalah
elemen dapat dibuat dari awal atau ditingkatkan sesuai permintaan untuk hidrasi yang anggun
cocok dengan 313 byte (inti) atau 774 byte dengan callback siklus hidup Elemen Kustom disertakan (tidak ada hal baru untuk dipelajari)
Contoh ini dapat diuji secara langsung dan hanya menjelaskan permukaan dari apa yang mungkin dilakukan dengan modul yang sangat kecil namun kuat ini.
// const createRegistry = require('nonchalance');import createRegistry from 'nonchalance/core';// ekspor default secara opsional menerima konteks `globalThis` like// untuk semua lingkungan yang tidak memiliki DOM asli./ / Registri tersebut dapat menampilkan nama kelas yang memenuhi syarat ditambah referensi `dokumen`// yang akan digunakan untuk membuat elemen.const {HTML} = createRegistry();// memperluas elemen apa pun yang akan dibuat, atau tingkatkan, elemen khusus// dari registri yang tidak berbagi apa pun dengan kelas konteks global Kata sandi diperluas HTML.Input { // akan mewarisi bidang tag statis = "input" konstruktor(...args) {super(...args);this.type = 'kata sandi'; } // hindari skrip berbahaya dengan mudah mengambil kata sandi apa pun dapatkan nilai() {kembalikan '********'; } // kasus demo: masih menggunakan `nilai` asli saat kata sandi disetel tetapkan nilai(rahasia) {super.nilai = rahasia; }}document.body.innerHTML = ` <form> <input type = "text" name = "pengguna" placeholder = "pengguna"> <input type = "password" name = "password" placeholder = "password"> <input type="submit"> </form>`;// tingkatkan elemen yang diinginkan melalui ClassTypenew Password baru(document.querySelector('[type="password"]'));// atau buat instanceconst secret baru = Objek.assign(Kata Sandi baru, { nama: 'lulus', nilai: Matematika.acak(), placeholder: 'kata sandi runtime'});const form = document.querySelector('form');form.insertBefore(secret, form.lastElementChild);
Dengan menggunakan modul fungsi khusus, dimungkinkan untuk memutakhirkan elemen apa pun tanpa harus menghadapi kesalahan Konstruktor Ilegal yang muncul setiap kali class extends HTMLSomethingElement {}
, ketika kelas tersebut tidak didefinisikan secara global sebagai entri dalam registri customElements
.
Tidak hanya tidak ada yang dibagikan secara global melalui modul ini dalam konteks global, setiap pekerjaan ekstra yang canggung untuk membuat perluasan bawaan berfungsi sama sekali tidak diperlukan:
elemen baru atau yang sudah lewat selalu mempertahankan rantai akar prototipenya
tidak ada atribut tambahan atau nama bentrok yang dapat terjadi
Selain itu, karena registri HTML apa pun dapat dibuat per setiap modul atau proyek untuk dibagikan di antara komponen-komponennya, dimungkinkan juga untuk meneruskan ke pembuatan registri tersebut lingkungan globalThis
seperti ini yang palsu atau tiruan, dengan setidaknya bidang document
yang mengekspos createElementNS(namespace, tagName)
, dan satu atau lebih kelas yang ingin diuji oleh proyek, seperti HTMLElement
dan/atau kelas lain yang diperlukan agar proyek tersebut berhasil.
Namun, karena target utama modul ini adalah DOM , referensi globalThis
digunakan sebagai default yang masuk akal, namun hal itu tetap tidak berarti apa pun dibagikan di sekitar registri yang dibuat melalui ekspor default.
TIDAK . Cara kerja custom-function
dapat diringkas sebagai berikut:
# a native <p> protoype chain HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to new (class CustomP extends HTML.P {}) CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to class AnotherP extends CustomP {} AnotherP -> CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node
Singkatnya, membuat elemen melalui new AnotherP
atau memutakhirkan elemen melalui new AnotherP(liveParagraph)
cukup memperbarui rantai prototipe, tanpa mengharuskan elemen tersebut meninggalkan DOM atau mengubah sifat aslinya, karena hal tersebut dipertahankan dalam rantai pewarisan prototipe .
Ringkasan: pendaftar nonchalance hanya memutakhirkan elemen tanpa mengubah sifatnya, persis seperti cara bawaan asli memperluas pekerjaan di balik terpal.
Ya , baik melalui ekspor /jsx
atau melalui /ref
.
Tentang /jsx Silakan lihat Apa itu ekspor /jsx? bagian.
Tentang /ref Silakan lihat Apa itu ekspor /ref? bagian.
Ekspor /ce
secara otomatis meningkatkan elemen dengan cara yang kompatibel dengan metode connectedCallback
, disconnectedCallback
, dan attributeChangedCallback
kelas, bersama dengan bidang observedAttributes
statisnya.
Modul ini menggunakan versi yang telah disempurnakan dari modul as-custom-element yang sudah berfungsi dengan baik.
Lihat demo langsung di codepen ini untuk mengetahui cara kerjanya.
TIDAK . Secara metaforis, elemen HTML memiliki makna semantik dan utilitas yang terdefinisi dengan baik dan diinginkan saat aktif, sama seperti fungsi JS selamanya akan menjadi fungsi JS , bahkan jika Object.setPrototypeOf(() => {}, Number.prototype)
terjadi... dapatkah Anda melihat, atau setuju, seberapa salahnya?
Modul ini tidak ingin (dan mungkin juga tidak bisa) mencegah penyalahgunaan fitur-fiturnya, jadi pastikan bahwa setiap kali sebuah elemen ditingkatkan, ia mempertahankan rantai prototipe aslinya di belakang layar, atau Anda sendirian melawan DOM . .. mana yang cukup merepotkan, jika Anda bertanya kepada saya?
Singkatnya, dengan cara yang sama customElements.define('my-link', class extends HTMLDivElement {}, {extends: 'a'})
tidak masuk akal, modul ini mempercayai penggunanya bahwa kelas yang tidak masuk akal diharapkan akan dihindari.
Saat ini, default / ekspor utama untuk modul ini menunjuk pada ekspor /core
yang sama.
Karena modul ini membuka kotak Pandora dengan kesederhanaan dan ukuran kode vaporware, dan terutama karena masih di belakang versi 0.
, saya mencoba mempertimbangkan apa yang harus dimasukkan dalam indeks, dan inilah beberapa pemikiran saya:
bukankah menyenangkan memiliki modul berbasis ESX yang memahami komponen yang didefinisikan dengan cara ini?
bukankah keren memiliki fungsi pragma JSX yang membuat komponen melalui modul ini?
bukankah menyenangkan memiliki ... (tempat Anda di sini) ... ?
Ya, itu akan keren, dan jika saya dapat memutuskan bagaimana penamaan ekspor default, saya siap untuk membawa nama itu di antara kebaikan lainnya sebagai entri default untuk modul ini ... pantau terus atau tolong beri saya pemikiran dan petunjuk tentang bagaimana melakukan itu
Namun sampai saat itu tiba, harap gunakan ekspor eksplisit untuk memastikan pembaruan di masa mendatang tidak akan mengacaukan logika Anda, dan saya minta maaf jika perubahan terkini menimbulkan masalah bagi Anda, namun saya cukup yakin Anda dapat dengan mudah mengaitkan atau memahami hal itu untuk selamanya!
Ketika elemen ditingkatkan dari jarak jauh, mungkin saja elemen tersebut memiliki beberapa properti terpasang yang tidak mendapat kesempatan untuk melewati pengaksesnya.
Pembantu ini hanya memastikan bahwa properti yang diwariskan dihapus sebagai kunci elemennya sendiri untuk kemudian dipicu sebagai pengakses setelahnya.
impor createRegistry dari 'nonchalance/ce';impor pengakses dari 'nonchalance/accessors';const {HTML} = createRegistry();class WithAccessors extends HTML.Div { konstruktor(...args) {aksesor(super(...args)); } dapatkan nilai() {console.log('dapatkan nilai', ini._nilai);kembalikan ini._nilai; } setel nilai(_nilai) {ini._nilai = _nilai;konsole.log('set nilai', ini._nilai); }}// div asli elementconst div = document.createElement('div');div.value = 123;// upgradenew WithAccessors(div);// re-checkconsole.log(div.value);
Lihat langsung untuk menguji lebih lanjut.
Ekspor /builtin
(248 byte) persis seperti /core
kecuali tidak menggunakan custom-function
di belakang layar, artinya:
tidak mungkin untuk new BuiltIn()
atau ke new BuiltIn(element)
karena hal itu akan menimbulkan kesalahan, kecuali belum terdaftar sebagai customElement builtin extend
ini dapat digunakan untuk mengotomatiskan pendaftaran komponen, seperti yang ditunjukkan dalam demo langsung di CodePen
Satu-satunya peringatan besar seputar ekspor ini adalah, karena didasarkan pada elemen kustom standar sebenarnya, polyfill bawaan mungkin diperlukan untuk Safari atau WebKit, misalnya:
<!-- skrip halaman paling atas untuk Safari saja polyfill --><script>self.chrome ||self.netscape ||document.write('<script src="//unpkg.com/@webreflection/custom-elements -builtin"><x2fscript>');</script>
Harap dicatat bahwa meskipun namespace HTML
dan SVG
diperbolehkan secara default sebagai perluasan bawaan, elemen khusus tidak menerima perluasan SVG sehingga secara praktis hanya perluasan HTML yang dimungkinkan dengan ekspor /builtin
saat ini.
Ekspor ./dummy
sebagian besar ditujukan untuk SSR , menyediakan utilitas yang sama persis untuk memperluas kelas yang hanya akan membawa bidang tag
statis.
Dikombinasikan dengan /tag
dimungkinkan untuk melakukan 100% SSR dengan sikap acuh tak acuh dan hidrat dari jarak jauh.
impor createRegistry dari 'https://unpkg.com/nonchalance/dummy';import createTag dari 'https://unpkg.com/nonchalance/tag';const {HTML} = createRegistry();class HelloDiv extends HTML.Div { connectCallback() {console.log('inilah saya'); }}// membuat namespace yang dapat digunakan kembali untuk hydrconst nmsp = {HelloDiv};// membuat tag transformatorconst tag = createTag(nmsp);// bayangkan respons server// catatan: kode ini hanya untuk kepentingan democonsole.log( tag`<!doctype html><script type="module">impor createRegistry dari 'https://unpkg.com/nonchalance/ce';const {HTML} = createRegistry();const nmsp = {};for (const el dari document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; hapus el.dataset.comp baru; (el);}</skrip><HelloDiv></HelloDiv>` .bergabung('') .memangkas() .replace('const nmsp = {};',`const nmsp = { ${[...Object.entries(nmsp)].map( ([key, value]) => `${key}: ${ nilai}` ).join(',n')} };` ));
Ekspor /jsx
(976 byte) menerima opsi createElement
tambahan dan mengembalikan fungsi jsx
yang dapat digunakan sebagai @jsx pragma untuk mentransformasikan, antara lain sudah bekerja secara default di React atau Preact , juga kelas yang diperluas melalui registri HTML atau SVG , termasuk semua fitur yang /ce
bawa ke kelas tersebut: Elemen Khusus seperti siklus hidup dengan beberapa bumbu di atasnya:
kelas akan menerima di konstruktornya props yang diteruskan sepanjang elemen, mengaktifkan sinyal, fungsi lain, atau menangani apa pun yang sudah mungkin untuk ditangani oleh komponen JSX default.
ketika konstruktor dipanggil, elemen tersebut sudah terisi dengan anak-anaknya, menghindari kemungkinan kejahatan yang diketahui dengan elemen khusus standar ketika kelas ditentukan/didaftarkan sebelum dokumen diurai.
mirip dengan /builtin
extend, tidak mungkin untuk new Component(props)
tetapi selalu mungkin untuk <Component {...props} />
.
Lihat penggunaannya dalam praktik dengan React live di CodePen.
DOM tetaplah DOM , tidak peduli berapa banyak tipuan yang ada di antaranya. DX Anda mungkin bervariasi, sesuai dengan fitur kerangka kerja, tetapi jika React adalah yang Anda cari, ada cara kecil namun elegan dan berbasis ref
untuk mempromosikan node JSX reguler dengan nonchalance/core atau nonchalance/ce :
import referenced from 'nonchalance/ref';// menunjukkan Komponen akan diteruskan sebagai referensi// Catatan: ini hanyalah Proxy ringan yang memberikan integritas kelas// terlepas dari penggunaannya di wildconst Component = referenced(class extends HTML. Div { konstruktor(...args) {super(...args);this.addEventListener('klik', console.log); }});ReactDOM.render( <div ref={Komponen}>klik saya</div>, dokumen.body);
Utilitas ref
juga dapat digunakan sebagai dekorator dan tanpa mempengaruhi fitur apa pun dari kelas acuh tak acuh reguler. Selain itu, setiap elemen hanya diupgrade satu kali sehingga aman untuk menambahkan pendengar atau logika di konstruktor.
Lihat demo ini secara langsung di codepen untuk mencobanya.
Ekspor /selector
(796 byte) memungkinkan definisi langsung dari setiap pemilih CSS sehingga kelas terkait akan secara otomatis meningkatkan elemen apa pun yang cocok dengan pemilih tersebut.
Harap dicatat bahwa pemilih tersebut harus seunik mungkin jika tidak, kejutan dapat terjadi. Gunakan kelas atau atribut data-
tertentu untuk mendeskripsikan pemilih Anda dengan baik.
impor createRegistry dari 'nonchalance/core';import { definisikan } dari 'nonchalance/selector';const { HTML } = createRegistry();const Special = definisikan('[data-comp="special"]', kelas memperluas HTML. Div { konstruktor(...args) {super(...args);this.textContent = 'Saya spesial!'; }});// akan berisi "Saya spesial!" teks sekali livedocument.body.innerHTML = `<div data-comp="special"></div>`;
Ekspor ./tag
(188 byte) memungkinkan transformasi templat dengan cara yang ramah hidrasi.
Ini dapat digunakan sebagai nilai perantara di balik tag literal templat yang berkemampuan penuh dan hidrasi dapat terjadi setelah elemen tersebut mendarat di DOM .
impor createRegistry dari 'nonchalance/ce';impor createTag dari 'nonchalance/tag';const {HTML} = createRegistry();class HelloDiv extends HTML.Div { connectCallback() {console.log('inilah saya'); }}// membuat namespace yang dapat digunakan kembali untuk hydrconst nmsp = {HelloDiv};// membuat tag transformatorconst tag = createTag(nmsp);// demodocument.body.innerHTML = tag`<HelloDiv />`.join( '');// contoh hidrasi (const el dari document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; hapus el.dataset.comp; // tingkatkan elemen satu kali nmsp baru[komp](el);}
Lihat langsung di CodePen.
Ya . Ekspor /core
dan /ce
memungkinkan pembuatan, secara default, registry HTML dan SVG :
impor createRegistry dari 'nonchalance/core';const {HTML, SVG} = createRegistry();class Circle extends SVG.Circle { konstruktor(pilihan) {Objek .assign(super(), pilihan) .setAttribute('isi', 'emas'); } set cx(nilai) { ini.setAttribute('cx', nilai) } set cy(nilai) { ini.setAttribute('cy', nilai) } set r(nilai) { this.setAttribute('r', value) }}document.querySelector('svg').append( Lingkaran baru({cx: 100, cy: 100, r: 50}));
Lihat langsung di codepen.
Dimungkinkan juga untuk meneruskan namespace apa pun ke createRegistry(options)
, menggunakan {MathML: "http://www.w3.org/1998/Math/MathML"}
sebagai contoh.
Namespace apa pun yang memiliki arti document.createElementNS
diperbolehkan, tidak ada batasan jenis elemen DOM apa yang dapat kami tingkatkan.
Saya punya jawaban yang sangat panjang sebelumnya tetapi ringkasannya adalah modul ini menggunakan standar yang disediakan oleh W3C , WHATWG , atau ECMAScript , dan memerlukan kurang dari 1KB untuk bekerja di mana saja.
Ini bukan polyfill, ini adalah utilitas untuk membantu Anda menulis komponen di dunia JS tanpa khawatir komponen ini akan bentrok, memerlukan alat, atau tidak portabel di proyek target apa pun yang Anda suka/butuhkan/pilih.
Singkatnya, jika Anda setuju menambahkan kurang dari 1K byte untuk menghadirkan komponen universal untuk dunia Front End dan Back End, Anda telah menemukan modul yang tepat?
Tidak ada yang lebih membebaskan daripada menjadi anak ceroboh yang bermain lumpur melawan semua pemikir “ jangan lakukan itu! ”.
Modul ini entah bagaimana mewakili perasaan itu melalui kebebasan yang ditawarkan fitur-fitur JS modern, menunjukkan alternatif yang elegan, portabel, dan super ringan terhadap kompleksitas yang semakin meningkat yang ditawarkan oleh vendor browser dan spesifikasi modern, semua diperlukan untuk memaksa pengembang mengatasi kemampuan untuk sekadar memperluas builtin dan mempertahankan kesederhanaan dan aksesibilitas luar biasa yang terkenal dengan Web.