The tradeoffs of CSS-in-JS

Gambar oleh Artem Bali

Baru-baru ini saya menulis gambaran keseluruhan peringkat tinggi CSS-in-JS, kebanyakannya bercakap tentang masalah pendekatan ini cuba untuk menyelesaikannya. Penulis perpustakaan jarang meluangkan masa untuk menerangkan tradeoffs penyelesaian mereka. Kadang-kadang ia adalah kerana mereka terlalu berat sebelah, dan kadang-kadang mereka tidak tahu bagaimana pengguna menggunakan alat tersebut. Oleh itu inilah satu usaha untuk menggambarkan pertentangan yang saya lihat setakat ini. Saya fikir penting untuk menyebut bahawa saya adalah pengarang JSS, jadi saya harus dianggap berat sebelah.

Impak sosial

Terdapat lapisan orang yang bekerja di platform web dan tidak mengetahui sebarang JavaScript. Orang-orang itu dibayar untuk menulis HTML dan CSS. CSS-in-JS telah memberi kesan besar kepada alur kerja pemaju. Perubahan yang benar-benar transformatif tidak boleh dilakukan tanpa ada orang yang ditinggalkan. Saya tidak tahu sama ada CSS-in-JS adalah satu-satunya cara, tetapi penggunaan massal adalah tanda masalah yang jelas dengan menggunakan CSS dalam aplikasi moden.

Sebahagian besar masalah ialah ketidakupayaan kita untuk berkomunikasi secara tepat kes-kes penggunaan di mana CSS-in-JS bersinar dan bagaimana menggunakannya dengan betul untuk tugas. Ramai peminat CSS-in-JS telah berjaya mempromosikan teknologi itu, tetapi tidak banyak pengkritik yang membincangkan tentang tradeoffs dengan cara yang membina, tanpa mengambil buaian yang murah di alat. Akibatnya, kami meninggalkan banyak pertentangan tersembunyi dan tidak berusaha keras untuk memberikan penjelasan dan penyelesaian.

CSS-in-JS adalah percubaan untuk membuat kes-kes penggunaan yang lebih mudah untuk dikendalikan, jadi jangan menolaknya di mana ia tidak diperlukan!

Kos Runtime

Apabila CSS dijana daripada JavaScript semasa runtime, dalam penyemak imbas, terdapat overhead yang wujud. Overhed jalan bervariasi dari perpustakaan ke perpustakaan. Ini adalah penanda aras generik yang baik, tetapi pastikan untuk membuat ujian anda sendiri. Perbezaan utama semasa runtime muncul bergantung pada keperluan untuk mempunyai parsing CSS penuh pada rentetan template, jumlah pengoptimuman, butiran pelaksanaan gaya dinamik, algoritma hashing dan biaya pengintegrasian rangka kerja. *

Selain daripada masalah runtime yang berpotensi, anda perlu mempertimbangkan 4 strategi bundling yang berlainan, kerana beberapa perpustakaan CSS-in-JS menyokong pelbagai strategi dan terpulang kepada pengguna untuk menggunakannya. *

Strategi 1: Runtime generasi sahaja

Runtime CSS generation adalah teknik yang menghasilkan string CSS dalam JavaScript dan kemudian menyuntikkan rentetan itu menggunakan tag gaya ke dalam dokumen. Teknik ini menghasilkan Gaya Lembaran, TIDAK gaya inline.

The tradeoff generasi runtime adalah ketidakupayaan untuk menyediakan kandungan gaya pada peringkat awal, ketika dokumen mulai dimuat. Pendekatan ini biasanya sesuai untuk aplikasi tanpa kandungan yang boleh digunakan dengan serta-merta. Biasanya, aplikasi sedemikian memerlukan interaksi pengguna sebelum mereka benar-benar boleh menjadi berguna kepada pengguna. Selalunya aplikasi tersebut berfungsi dengan kandungan yang begitu dinamik sehingga ia menjadi lapuk sebaik sahaja anda memuatkannya, jadi anda perlu membuat saluran paip kemas kini pada awalnya, sebagai contoh, Twitter. Di samping itu, apabila pengguna log masuk, tidak perlu menyediakan HTML untuk SEO.

Sekiranya interaksi memerlukan JavaScript, bundel perlu dimuatkan sebelum aplikasi siap. Contohnya, anda boleh menunjukkan kandungan saluran lalai apabila memuat Slack dalam dokumen, tetapi kemungkinan pengguna akan menukar saluran itu selepas itu. Jadi jika anda memuatkan kandungan awal hanya untuk membuangnya dengan segera.

Prestasi yang dilihat dari aplikasi tersebut boleh diperbaiki dengan ruang letak dan helah lain untuk membolehkan aplikasi merasa lebih cepat daripada yang sebenarnya. Aplikasi seperti biasanya banyak sekali data, jadi mereka tidak akan berguna secepat artikel.

Strategi 2: Generasi penuntun dengan CSS Kritikal

CSS Kritikal adalah jumlah minimum CSS yang diperlukan untuk gaya halaman dalam keadaan awalnya. Ia diberikan menggunakan tag gaya di kepala dokumen. Teknik ini digunakan secara meluas dan tanpa CSS-in-JS. Dalam kedua-dua kes ini, anda mungkin akan memuatkan dua kali ganda peraturan CSS, sekali sebagai sebahagian daripada CSS Kritikal dan sekali sebagai sebahagian daripada JavaScript atau CSS bundle. Saiz CSS Kritikal boleh agak besar bergantung pada jumlah kandungan. Biasanya, dokumen itu tidak akan di-cache.

Tanpa CSS Kritikal, aplikasi halaman tunggal berat kandungan statik dengan runtime CSS-in-JS perlu menunjukkan ruang letak dan bukan kandungan. Ini tidak baik kerana ia mungkin berguna kepada pengguna lebih awal, meningkatkan kebolehaksesan pada peranti rendah dan sambungan jalur lebar yang rendah.

Dengan CSS kritikal, generasi CSS runtime boleh dilakukan pada peringkat kemudian, tanpa menyekat UI pada fasa awal. Walau bagaimanapun, pada peranti mudah alih rendah, kira-kira 5+ tahun, generasi CSS dari JavaScript boleh memberi kesan negatif terhadap prestasi. Ia sangat bergantung pada jumlah CSS yang dihasilkan dan pustaka digunakan, jadi ia tidak boleh umum.

The tradeoff strategi ini adalah kos pengekstrakan CSS Kritikal dan kos generasi CSS runtime.

Strategi 3: Pengekstrakan Build-time sahaja

Strategi ini adalah salah satu lalai di web tanpa CSS-in-JS. Sesetengah perpustakaan CSS-in-JS membolehkan anda mengekstrak CSS statik pada masa membina. * Dalam kes ini, tiada overhead runtime yang terlibat, CSS diberikan pada halaman menggunakan tag pautan. Kos penjanaan CSS dibayar sekali lebih awal daripada masa.

Terdapat 2 tradeoffs utama di sini:

  1. Anda tidak boleh menggunakan beberapa API dinamik tawaran CSS-in-JS pada masa runtime, kerana anda tidak mempunyai akses ke negeri ini. Sering kali anda masih tidak boleh menggunakan sifat tersuai CSS, kerana ia tidak disokong dalam setiap penyemak imbas dan tidak boleh disepanjang pada masa membina mengikut sifatnya. Dalam kes ini, anda perlu melakukan workaround untuk gaya dinamik dan gaya berasaskan negeri. *
  2. Tanpa CSS Kritikal dan dengan cache kosong, anda akan menyekat cat pertama, sehingga berkas CSS anda akan dimuatkan. Satu elemen pautan di kepala dokumen menghalang penyebaran HTML.
  3. Spesifikasi non-deterministik dengan pembahagian bundle berasaskan halaman dalam aplikasi halaman tunggal. *

Strategi 4: Pengekstrakan bina-masa dengan CSS Kritikal

Strategi ini juga tidak unik kepada CSS-in-JS. Pengekstrakan statik penuh dengan CSS kritikal memberikan prestasi terbaik ketika bekerja dengan aplikasi yang lebih statik. Pendekatan ini masih mempunyai tragedi yang disebutkan dalam CSS statik, kecuali bahawa pautan pautan menyekat boleh dipindahkan ke bahagian bawah dokumen.

Terdapat 4 strategi rendering CSS utama. Hanya 2 daripada mereka yang khusus untuk CSS-in-JS dan tidak seorang pun daripada mereka yang memohon kepada semua perpustakaan.

Kebolehcapaian

CSS-in-JS dapat mengurangkan kebolehcapaian apabila digunakan dengan cara yang salah. Ini akan berlaku apabila tapak kandungan sebahagian besarnya statik dilaksanakan tanpa pengekstrakan CSS Kritikal supaya HTML tidak boleh dicat sebelum berkas JavaScript dimuatkan dan dinilai. Ini juga boleh berlaku apabila fail CSS besar diberikan menggunakan tag pautan menyekat di kepala dokumen, yang merupakan masalah semasa yang paling popular dengan penambakan tradisional dan tidak khusus kepada CSS-in-JS.

Pemaju perlu mengambil tanggungjawab untuk kebolehaksesan. Masih ada idea yang sesat yang kuat bahawa sambungan internet tidak stabil adalah masalah negara-negara yang lemah ekonomi. Kita cenderung untuk melupakan bahawa kita mempunyai masalah sambungan setiap hari apabila kita memasuki sistem rel bawah tanah atau bangunan besar. Sambungan mudah alih bebas kabel stabil adalah mitos. Ia tidak mudah untuk mempunyai sambungan WiFi yang stabil, sebagai contoh, rangkaian 2.4 GHz WI-FI boleh mendapat gangguan daripada ketuhar gelombang mikro!

Kos Critical CSS dengan Render Server-Side

Untuk mendapatkan pengekstrakan CSS Kritikal untuk CSS-in-JS, kita memerlukan SSR. SSR adalah proses menghasilkan HTML akhir untuk keadaan aplikasi yang diberikan pada pelayan. Malah, ia boleh menjadi proses yang kompleks dan mahal. Ia memerlukan sejumlah siklus CPU pada pelayan untuk setiap permintaan HTTP.

CSS-in-JS biasanya memanfaatkan hakikat bahawa ia disambungkan ke saluran paip rendering HTML. * Ia tahu apa yang HTML telah diberikan dan CSS yang diperlukannya sehingga dapat menghasilkan jumlah minimal mutlak itu. CSS Kritikal menambah overhed tambahan kepada rendering HTML pada pelayan kerana CSS juga perlu disusun menjadi rentetan CSS akhir. Dalam sesetengah senario, sukar atau bahkan mustahil untuk cache di pelayan sekalipun.

Rendering kotak hitam

Anda perlu sedar bagaimana pustaka CSS-in-JS yang anda gunakan adalah menjadikan CSS anda. Sebagai contoh, orang sering tidak menyedari bagaimana Komponen dan Emosi Bergaya melaksanakan gaya dinamik. Gaya dinamik adalah sintaks yang membolehkan penggunaan fungsi JavaScript dalam deklarasi gaya anda. Fungsi tersebut menerima prop dan mengembalikan blok CSS.

Untuk mengekalkan kekhususan pesanan sumber yang konsisten, kedua perpustakaan bernama di atas menghasilkan peraturan CSS yang baru jika ia mengandungi perisytiharan dinamik dan pembaruan komponen dengan prop baru. Untuk menunjukkan apa yang saya maksud, saya buat kotak pasir ini. Dalam JSS kami memutuskan untuk mengambil tradeoff yang berbeza, yang membolehkan kami mengemas kini sifat dinamik tanpa menghasilkan peraturan CSS baru. *

Keluk pembelajaran yang curam

Bagi orang yang biasa dengan CSS, tetapi baru kepada JavaScript, jumlah kerja awal untuk mendapatkan kelajuan dengan CSS-in-JS mungkin agak besar.

Anda tidak perlu menjadi pemaju JavaScript profesional untuk menulis CSS-in-JS, sehingga titik di mana logik kompleks terlibat. Kita tidak boleh umumkan kerumitan gaya, kerana ia bergantung kepada kes penggunaan. Dalam kes-kes di mana CSS-in-JS mendapat kompleks, kemungkinan pelaksanaannya dengan CSS vanili akan lebih kompleks.

Untuk gaya CSS-in-JS asas, seseorang perlu tahu bagaimana untuk mengisytiharkan pembolehubah, cara menggunakan rentetan template dan interpolasi nilai JavaScript. Jika nota objek digunakan, seseorang perlu mengetahui bagaimana untuk bekerja dengan objek JavaScript dan sintaks berasaskan objek khusus perpustakaan. Sekiranya gaya dinamik terlibat, seseorang perlu mengetahui cara menggunakan fungsi dan syarat JavaScript.

Secara keseluruhan terdapat keluk pembelajaran, kita tidak boleh menafikannya. Keluk pembelajaran ini biasanya tidak jauh lebih besar daripada belajar Sass. Sebenarnya, saya membuat kursus ini untuk menunjukkan ini.

Tiada interoperabiliti

Kebanyakan cetakan CSS-in-JS tidak boleh disambungkan. Ini bermakna gaya yang ditulis dengan menggunakan satu pustaka tidak boleh diberikan menggunakan perpustakaan yang berbeza. Secara praktiknya, anda tidak boleh menukar keseluruhan aplikasi anda dengan mudah dari satu pelaksanaan ke yang lain. Ini juga bermakna bahawa anda tidak dapat dengan mudah berkongsi UI anda di NPM tanpa membawa pilihan CSS CSS-in-JS ke dalam bundle pengguna melainkan anda mempunyai pengekstrakan statik terbina dalam untuk CSS anda.

Kami telah mula bekerja pada format ISTF yang sepatutnya untuk menyelesaikan masalah ini, tetapi malangnya kami belum mempunyai masa untuk mendapatkannya ke keadaan siap produksi. *

Saya fikir berkongsi rangka kerja yang boleh diguna semula komponen agnostik UI dalam domain awam masih merupakan masalah yang umumnya sukar dipecahkan.

Risiko keselamatan

Adalah mungkin untuk memperkenalkan kebocoran keselamatan dengan CSS-in-JS. Seperti mana-mana aplikasi pihak klien, anda perlu melepaskan input pengguna sebelum membuatnya, sentiasa.

Artikel ini akan memberikan anda lebih banyak wawasan dan beberapa contoh membezakan.

Nama kelas yang tidak dapat dibaca

Sesetengah orang masih menganggap penting bahawa kami menyimpan nama kelas yang boleh dibaca secara bermakna di web. Pada masa ini, banyak perpustakaan CSS-dalam-JS menyediakan nama kelas bermakna berdasarkan nama pengisytiharan atau nama komponen dalam mod pembangunan. Sebahagian daripada mereka bahkan membiarkan anda menyesuaikan fungsi penjana nama kelas.

Dalam mod pengeluaran, kebanyakannya menghasilkan nama yang lebih pendek untuk muatan yang lebih kecil. Ini adalah pembaziran pengguna perpustakaan perlu membuat dan menyesuaikan perpustakaan jika diperlukan.

Kesimpulannya

Tradeoffs wujud, dan saya mungkin tidak pernah menyebut semuanya. Tetapi kebanyakan mereka tidak secara universal memohon kepada semua CSS-in-JS. Mereka bergantung kepada perpustakaan yang anda gunakan dan bagaimana anda menggunakannya.

* Ia akan mengambil artikel berdedikasi untuk menjelaskan ayat ini. Izinkan saya tahu di Twitter (@ oleg008) tentang mana yang anda ingin baca lebih lanjut.