Komponen Bergaya: Pustaka CSS-in-JS untuk Web Modern
Diterbitkan: 2022-03-11CSS dirancang untuk dokumen, apa yang diharapkan dari "web lama". Munculnya preprocessor seperti Sass or Less menunjukkan bahwa komunitas membutuhkan lebih dari apa yang ditawarkan CSS. Dengan aplikasi web yang semakin kompleks dari waktu ke waktu, batasan CSS menjadi semakin terlihat dan sulit untuk dikurangi.
Komponen bergaya memanfaatkan kekuatan bahasa pemrograman lengkap—JavaScript—dan kemampuan pelingkupannya untuk membantu menyusun kode menjadi komponen. Ini membantu menghindari perangkap umum dalam menulis dan memelihara CSS untuk proyek besar. Pengembang dapat menjelaskan gaya komponen tanpa risiko efek samping.
Apa masalahnya?
Salah satu keuntungan menggunakan CSS adalah bahwa gaya benar-benar dipisahkan dari kode. Ini berarti bahwa pengembang dan desainer dapat bekerja secara paralel tanpa mengganggu satu sama lain.
Di sisi lain, komponen gaya membuatnya lebih mudah untuk jatuh ke dalam perangkap gaya dan logika kopling yang kuat. Max Stoiber menjelaskan bagaimana menghindari hal ini. Meskipun ide untuk memisahkan logika dan presentasi bukanlah hal baru, orang mungkin tergoda untuk mengambil jalan pintas saat mengembangkan komponen React. Misalnya, mudah untuk membuat komponen untuk tombol validasi yang menangani tindakan klik serta gaya tombol. Dibutuhkan sedikit lebih banyak usaha untuk membaginya menjadi dua komponen.
Kontainer/Arsitektur Presentasi
Ini adalah prinsip yang cukup sederhana. Komponen menentukan tampilan sesuatu, atau mereka mengelola data dan logika. Aspek yang sangat penting dari komponen presentasi adalah bahwa mereka tidak boleh memiliki ketergantungan. Mereka menerima props dan merender DOM (atau anak-anak) yang sesuai. Wadah, di sisi lain, tahu tentang arsitektur data (status, redux, fluks, dll.) tetapi tidak boleh bertanggung jawab atas tampilan. Artikel Dan Abramov adalah penjelasan yang sangat baik dan rinci tentang arsitektur ini.
Mengingat SMACSS
Meskipun Arsitektur Scalable dan Modular untuk CSS adalah panduan gaya untuk mengatur CSS, konsep dasarnya adalah yang diikuti, sebagian besar secara otomatis, oleh komponen gaya. Idenya adalah untuk memisahkan CSS menjadi lima kategori:
- Basis berisi semua aturan umum.
- Layout bertujuan untuk mendefinisikan properti struktural serta berbagai bagian konten (header, footer, sidebar, konten, misalnya).
- Modul berisi subkategori untuk berbagai blok logis UI.
- Status mendefinisikan kelas pengubah untuk menunjukkan status elemen, misalnya bidang dalam kesalahan, tombol dinonaktifkan.
- Tema berisi warna, font, dan aspek kosmetik lainnya yang dapat dimodifikasi atau tergantung pada preferensi pengguna.
Menjaga pemisahan ini saat menggunakan komponen gaya itu mudah. Proyek biasanya menyertakan semacam normalisasi atau penyetelan ulang CSS. Ini biasanya termasuk dalam kategori Basis . Anda juga dapat menentukan ukuran font umum, ukuran garis, dll. Ini dapat dilakukan melalui CSS normal (atau Sass/Less), atau melalui fungsi injectGlobal
yang disediakan oleh styled-components .
Untuk aturan Tata Letak , jika Anda menggunakan kerangka kerja UI, maka itu mungkin akan menentukan kelas wadah, atau sistem kisi. Anda dapat dengan mudah menggunakan kelas tersebut bersama dengan aturan Anda sendiri dalam komponen tata letak yang Anda tulis.
Modul secara otomatis diikuti oleh arsitektur komponen gaya , karena gaya dilampirkan ke komponen secara langsung, bukan dijelaskan dalam file eksternal. Pada dasarnya, setiap komponen gaya yang Anda tulis akan menjadi modulnya sendiri. Anda dapat menulis kode gaya Anda tanpa khawatir tentang efek samping.
Status akan menjadi aturan yang Anda tetapkan dalam komponen Anda sebagai aturan variabel. Anda cukup mendefinisikan fungsi untuk menginterpolasi nilai atribut CSS Anda. Jika menggunakan kerangka kerja UI, Anda mungkin memiliki kelas yang berguna untuk ditambahkan ke komponen Anda juga. Anda mungkin juga akan memiliki aturan pemilih semu CSS (arahkan kursor, fokus, dll.)
Tema dapat dengan mudah diinterpolasi di dalam komponen Anda. Sebaiknya definisikan tema Anda sebagai kumpulan variabel yang akan digunakan di seluruh aplikasi Anda. Anda bahkan dapat memperoleh warna secara terprogram (menggunakan perpustakaan, atau secara manual), misalnya untuk menangani kontras dan sorotan. Ingatlah bahwa Anda memiliki kekuatan penuh bahasa pemrograman yang Anda inginkan!
Bawa Mereka Bersama untuk Solusi
Penting untuk menyatukannya, untuk pengalaman navigasi yang lebih mudah; Kami tidak ingin mengaturnya berdasarkan jenis (presentasi vs. logika) melainkan berdasarkan fungsionalitas.
Jadi, kita akan memiliki folder untuk semua komponen generik (tombol dan semacamnya). Yang lain harus diatur tergantung pada proyek dan fungsinya. Misalnya, jika kita memiliki fitur manajemen pengguna, kita harus mengelompokkan semua komponen khusus untuk fitur tersebut.
Untuk menerapkan arsitektur wadah/presentasi komponen bergaya ke pendekatan SMACSS, kita memerlukan tipe komponen tambahan: struktural. Kami berakhir dengan tiga jenis komponen; ditata, struktural, dan wadah. Karena komponen bergaya menghiasi tag (atau komponen), kita memerlukan jenis komponen ketiga ini untuk menyusun DOM. Dalam beberapa kasus, dimungkinkan untuk mengizinkan komponen wadah untuk menangani struktur sub-komponen, tetapi ketika struktur DOM menjadi kompleks dan diperlukan untuk tujuan visual, yang terbaik adalah memisahkannya. Contoh yang baik adalah tabel, di mana DOM biasanya menjadi sangat bertele-tele.
Contoh Proyek
Mari buat aplikasi kecil yang menampilkan resep untuk mengilustrasikan prinsip-prinsip ini. Kita bisa mulai membangun komponen Resep. Komponen induk akan menjadi pengontrol. Ini akan menangani keadaan—dalam hal ini, daftar resep. Ini juga akan memanggil fungsi API untuk mengambil data.

class Recipes extends Component{ constructor (props) { super(props); this.state = { recipes: [] }; } componentDidMount () { this.loadData() } loadData () { getRecipes().then(recipes => { this.setState({recipes}) }) } render() { let {recipes} = this.state return ( <RecipesContainer recipes={recipes} /> ) } }
Ini akan membuat daftar resep, tetapi tidak (dan tidak seharusnya) perlu tahu caranya. Jadi kami merender komponen lain yang mendapatkan daftar resep dan keluaran DOM:
class RecipesContainer extends Component{ render() { let {recipes} = this.props return ( <TilesContainer> {recipes.map(recipe => (<Recipe key={recipe.id} {...recipe}/>))} </TilesContainer> ) } }
Di sini, sebenarnya, kami ingin membuat kotak ubin. Mungkin ide yang bagus untuk membuat tata letak ubin yang sebenarnya sebagai komponen umum. Jadi jika kita mengekstraknya, kita mendapatkan komponen baru yang terlihat seperti ini:
class TilesContainer extends Component { render () { let {children} = this.props return ( <Tiles> { React.Children.map(children, (child, i) => ( <Tile key={i}> {child} </Tile> )) } </Tiles> ) } }
TilesStyles.js:
export const Tiles = styled.div` padding: 20px 10px; display: flex; flex-direction: row; flex-wrap: wrap; ` export const Tile = styled.div` flex: 1 1 auto; ... display: flex; & > div { flex: 1 0 auto; } `
Perhatikan bahwa komponen ini murni presentasi. Ini mendefinisikan gayanya dan membungkus anak-anak apa pun yang diterimanya di dalam elemen DOM bergaya lain yang mendefinisikan seperti apa tampilan ubin. Ini adalah contoh bagus tentang seperti apa komponen presentasi umum Anda secara arsitektural.
Kemudian, kita perlu mendefinisikan seperti apa resep itu. Kami membutuhkan komponen wadah untuk menggambarkan DOM yang relatif kompleks serta menentukan gaya bila perlu. Kami berakhir dengan ini:
class RecipeContainer extends Component { onChangeServings (e) { let {changeServings} = this.props changeServings(e.target.value) } render () { let {title, ingredients, instructions, time, servings} = this.props return ( <Recipe> <Title>{title}</Title> <div>{time}</div> <div>Serving <input type="number" min="1" max="1000" value={servings} onChange={this.onChangeServings.bind(this)}/> </div> <Ingredients> {ingredients.map((ingredient, i) => ( <Ingredient key={i} servings={servings}> <span className="name">{ingredient.name}</span> <span className="quantity">{ingredient.quantity * servings} {ingredient.unit}</span> </Ingredient> ))} </Ingredients> <div> {instructions.map((instruction, i) => (<p key={i}>{instruction}</p>))} </div> </Recipe> ) } }
Perhatikan di sini bahwa wadah melakukan beberapa pembuatan DOM, tetapi itu adalah satu-satunya logika yang dikandungnya. Ingatlah bahwa Anda dapat menentukan gaya bertingkat, jadi Anda tidak perlu membuat elemen gaya untuk setiap tag yang memerlukan gaya. Itu yang kami lakukan di sini untuk nama dan jumlah item bahan. Tentu saja, kita dapat membaginya lebih jauh dan membuat komponen baru untuk suatu bahan. Terserah Anda—bergantung pada kerumitan proyek—untuk menentukan perinciannya. Dalam hal ini, ini hanyalah komponen gaya yang ditentukan bersama dengan komponen lainnya dalam file RecipeStyles:
export const Recipe = styled.div` background-color: ${theme('colors.background-highlight')}; `; export const Title = styled.div` font-weight: bold; ` export const Ingredients = styled.ul` margin: 5px 0; ` export const Ingredient = styled.li` & .name { ... } & .quantity { ... } `
Untuk tujuan latihan ini, saya telah menggunakan ThemeProvider. Itu menyuntikkan tema di alat peraga komponen gaya. Anda cukup menggunakannya sebagai color: ${props => props.theme.core_color}
, saya hanya menggunakan pembungkus kecil untuk melindungi dari atribut yang hilang dalam tema:
const theme = (key) => (prop) => _.get(prop.theme, key) || console.warn('missing key', key)
Anda juga dapat menentukan konstanta Anda sendiri dalam modul dan menggunakannya sebagai gantinya. Misalnya: color: ${styleConstants.core_color}
kelebihan
Keuntungan menggunakan komponen gaya adalah Anda dapat menggunakannya sesedikit yang Anda inginkan. Anda dapat menggunakan kerangka UI favorit Anda dan menambahkan komponen gaya di atasnya. Ini juga berarti bahwa Anda dapat dengan mudah memigrasikan komponen proyek yang ada demi komponen. Anda dapat memilih untuk menata sebagian besar tata letak dengan CSS standar dan hanya menggunakan komponen gaya untuk komponen yang dapat digunakan kembali.
Kontra
Desainer/integrator gaya perlu mempelajari JavaScript yang sangat mendasar untuk menangani variabel dan menggunakannya sebagai pengganti Sass/Less.
Mereka juga harus belajar menavigasi struktur proyek, meskipun saya berpendapat bahwa menemukan gaya untuk komponen di folder komponen itu lebih mudah daripada harus menemukan file CSS/Sass/Less yang tepat yang berisi aturan yang perlu Anda ubah.
Mereka juga perlu sedikit mengubah alat mereka jika mereka ingin penyorotan sintaks, linting, dll. Tempat yang baik untuk memulai adalah dengan plugin Atom ini dan plugin babel ini.