Dalam materi, terjemahan yang kami terbitkan hari ini, kami akan berbicara tentang cara membuat kisi tipografi untuk sistem desain menggunakan
fungsi render Vue .
Ini adalah demo proyek yang akan kami ulas di sini.
Anda dapat menemukan kodenya di sini. Penulis bahan ini mengatakan bahwa ia menggunakan fungsi render karena fakta bahwa mereka memungkinkan kontrol yang jauh lebih tepat atas proses pembuatan kode HTML daripada template Vue biasa. Namun, yang mengejutkannya, dia tidak dapat menemukan contoh-contoh praktis dari aplikasi mereka. Dia hanya menemukan manual instruksi. Dia berharap bahwa materi ini akan membuat perbedaan menjadi lebih baik berkat contoh praktis menggunakan fungsi render Vue di sini.
Fungsi Pembuat Vue
Bagi saya, fungsi render sepertinya sesuatu yang agak tidak lazim bagi Vue. Segala sesuatu dalam kerangka ini menekankan keinginan untuk kesederhanaan dan pemisahan tugas dari berbagai entitas. Tetapi fungsi render adalah campuran aneh dari HTML dan JavaScript, yang seringkali sulit dibaca.
Misalnya, ini adalah markup HTML:
<div class="container"> <p class="my-awesome-class">Some cool text</p> </div>
Untuk membentuknya, Anda memerlukan fungsi berikut:
render(createElement) { return createElement("div", { class: "container" }, [ createElement("p", { class: "my-awesome-class" }, "Some cool text") ]) }
Saya menduga bahwa konstruksi seperti itu akan membuat banyak orang segera berpaling dari fungsi render. Lagi pula, kemudahan penggunaan adalah apa yang menarik pengembang ke Vue. Sangat disayangkan jika banyak orang tidak melihat pahala sejati mereka di balik penampakan fungsi render yang tidak sedap dipandang. Masalahnya adalah bahwa fungsi render dan komponen fungsional adalah alat yang menarik dan kuat. Saya, untuk menunjukkan kemampuan dan nilai sejati mereka, akan berbicara tentang bagaimana mereka membantu saya memecahkan masalah yang sebenarnya.
Harap dicatat bahwa akan sangat berguna untuk membuka
versi demo proyek yang sedang dipertimbangkan di tab peramban yang berdekatan dan mengaksesnya saat membaca artikel.
Menentukan kriteria untuk sistem desain
Kami memiliki sistem desain berdasarkan
VuePress . Kami perlu memasukkan halaman baru di dalamnya, menunjukkan berbagai kemungkinan tipografi desain teks. Beginilah tata letak yang diberikan perancang itu kepada saya.
Tata Letak HalamanDan berikut ini adalah contoh kode CSS yang sesuai dengan halaman ini:
h1, h2, h3, h4, h5, h6 { font-family: "balboa", sans-serif; font-weight: 300; margin: 0; } h4 { font-size: calc(1rem - 2px); } .body-text { font-family: "proxima-nova", sans-serif; } .body-text--lg { font-size: calc(1rem + 4px); } .body-text--md { font-size: 1rem; } .body-text--bold { font-weight: 700; } .body-text--semibold { font-weight: 600; }
Header diformat berdasarkan nama tag. Untuk memformat elemen lain, nama kelas digunakan. Selain itu, ada kelas terpisah untuk kekayaan dan ukuran font.
Sebelum saya mulai menulis kode, saya merumuskan beberapa aturan:
Opsi untuk memecahkan masalah
Sebelum mulai bekerja, saya mempertimbangkan beberapa opsi untuk menyelesaikan tugas yang ditetapkan sebelum saya. Inilah ikhtisar mereka.
▍ Menulis kode HTML secara manual
Saya suka menulis kode HTML secara manual, tetapi hanya jika memungkinkan kita untuk menyelesaikan masalah yang ada. Namun, dalam kasus saya, penulisan kode manual berarti memasukkan berbagai fragmen kode berulang yang menghadirkan beberapa variasi. Saya tidak menyukainya. Selain itu, ini berarti bahwa data tidak dapat disimpan dalam file terpisah. Akibatnya, saya menolak pendekatan ini.
Jika saya membuat halaman yang dimaksud begitu saja, saya akan mendapatkan sesuatu seperti berikut:
<div class="row"> <h1>Heading 1</h1> <p class="body-text body-text--md body-text--semibold">h1</p> <p class="body-text body-text--md body-text--semibold">Balboa Light, 30px</p> <p class="group body-text body-text--md body-text--semibold"> <span>Product title (once on a page)</span> <span>Illustration headline</span> </p> </div>
▍Menggunakan pola Vue tradisional
Dalam kondisi normal, pendekatan ini paling sering digunakan. Namun, lihat contoh
ini .
Contoh menggunakan templat VueDi kolom pertama ada yang berikut ini:
<h1>
, yang disajikan dalam bentuk di mana browser menampilkannya.<p>
mengelompokkan beberapa <span>
anak-anak dengan teks. Kelas ditugaskan untuk masing-masing elemen ini (tetapi tidak ada kelas khusus yang ditugaskan untuk tag <p>
itu sendiri).<p>
yang tidak memiliki elemen <span>
bersarang tempat kelas ditugaskan.
Untuk menerapkan semua ini, banyak contoh arahan
v-if
dan
v-if-else
diperlukan. Dan ini, saya tahu, akan mengarah pada fakta bahwa kode akan menjadi sangat membingungkan segera. Juga, saya tidak suka menggunakan semua logika kondisional ini di markup.
▍Menghasilkan fungsi
Akibatnya, setelah menganalisis kemungkinan alternatif, saya memilih fungsi render. Di dalamnya, menggunakan JavaScript, menggunakan konstruksi kondisional, simpul anak dari simpul lain dibuat. Saat membuat simpul anak ini, semua kriteria yang diperlukan diperhitungkan. Dalam situasi ini, solusi seperti itu tampak sempurna bagi saya.
Model data
Seperti yang saya katakan, saya ingin menyimpan data tipografi dalam file JSON yang terpisah. Ini akan memungkinkan, jika perlu, untuk melakukan perubahan tanpa menyentuh markup.
Ini datanya.
Setiap objek JSON dalam file adalah deskripsi dari baris terpisah:
{ "text": "Heading 1", "element": "h1", // . "properties": "Balboa Light, 30px", // . "usage": ["Product title (once on a page)", "Illustration headline"] // . - . }
Berikut ini HTML yang muncul setelah memproses objek ini:
<div class="row"> <h1>Heading 1</h1> <p class="body-text body-text--md body-text--semibold">h1</p> <p class="body-text body-text--md body-text--semibold">Balboa Light, 30px</p> <p class="group body-text body-text--md body-text--semibold"> <span>Product title (once on a page)</span> <span>Illustration headline</span> </p> </div>
Sekarang perhatikan contoh yang lebih kompleks. Array mewakili kelompok anak-anak. Properti objek
classes
, yang merupakan objek, dapat menyimpan deskripsi kelas. Properti
base
dari objek
classes
berisi deskripsi kelas yang umum untuk semua node di sel. Setiap kelas yang ada di properti
variants
diterapkan ke elemen tunggal dalam grup.
{ "text": "Body Text - Large", "element": "p", "classes": { "base": "body-text body-text--lg",
Objek ini berubah menjadi HTML berikut:
<div class="row"> <p class="group"> <span class="body-text body-text--lg body-text--bold">Body Text - Large</span> <span class="body-text body-text--lg body-text--regular">Body Text - Large</span> </p> <p class="group body-text body-text--md body-text--semibold"> <span>body-text body-text--lg body-text--bold</span> <span>body-text body-text--lg body-text--regular</span> </p> <p class="body-text body-text--md body-text--semibold">Proxima Nova Bold and Regular, 20px</p> <p class="group body-text body-text--md body-text--semibold"> <span>Large button title</span> <span>Form label</span> <span>Large modal text</span> </p> </div>
Struktur dasar proyek
Kami memiliki komponen induk,
TypographyTable.vue
, yang berisi markup untuk membentuk tabel. Kami juga memiliki komponen
TypographyRow.vue
,
TypographyRow.vue
, yang bertanggung jawab untuk membuat baris tabel dan berisi fungsi render kami.
Saat membentuk baris tabel, array dengan data dilintasi. Objek yang menggambarkan baris tabel diteruskan ke komponen
TypographyRow
sebagai properti.
<template> <section> <div class="row"> <p class="body-text body-text--lg-bold heading">Hierarchy</p> <p class="body-text body-text--lg-bold heading">Element/Class</p> <p class="body-text body-text--lg-bold heading">Properties</p> <p class="body-text body-text--lg-bold heading">Usage</p> </div> <typography-row v-for="(rowData, index) in $options.typographyData" :key="index" :row-data="rowData" /> </section> </template> <script> import TypographyData from "@/data/typography.json"; import TypographyRow from "./TypographyRow"; export default {
Di sini saya ingin mencatat satu hal yang menyenangkan: data tipografi dalam instance Vue dapat direpresentasikan sebagai properti. Anda dapat mengaksesnya menggunakan konstruk
$options.typographyData
, karena tidak berubah dan tidak boleh reaktif (terima kasih kepada
Anton Kosykh ).
Membuat komponen fungsional
Komponen
TypographyRow
yang memproses data adalah komponen fungsional. Komponen fungsional adalah entitas yang tidak memiliki status dan instance. Ini berarti bahwa mereka tidak memiliki
this
, dan bahwa mereka tidak memiliki akses ke metode siklus hidup komponen Vue.
Ini adalah "kerangka" dari komponen yang sama dengan mana kita akan mulai mengerjakan komponen kita:
Metode komponen
render
mengambil argumen
context
yang memiliki properti
props
. Properti ini dapat dirusak dan digunakan sebagai argumen kedua.
Argumen pertama adalah
createElement
. Ini adalah fungsi yang memberitahu Vue simpul mana yang harus dibuat. Demi singkatnya dan standarisasi kode, saya menggunakan singkatan
h
untuk
createElement
. Anda dapat membaca tentang mengapa saya melakukan ini di
sini .
Jadi,
h
mengambil tiga argumen:
- Tag HTML (mis.
div
). - Objek data yang berisi atribut templat (misalnya,
{ class: 'something'}
). - String teks (jika kita hanya menambahkan teks) atau simpul anak yang dibuat menggunakan
h
.
Begini tampilannya:
render(h, { props }) { return h("div", { class: "example-class" }, "Here's my example text") }
Mari kita simpulkan apa yang sudah kita buat. Yaitu, kami sekarang memiliki yang berikut:
- File dengan data yang rencananya akan digunakan dalam pembentukan halaman.
- Komponen Vue reguler yang mengimpor file data.
- Kerangka kerja komponen fungsional yang bertanggung jawab untuk menampilkan baris tabel.
Untuk membuat baris tabel, data dari format JSON harus dikirimkan sebagai argumen ke
h
. Anda dapat mentransfer semua data tersebut dalam sekali jalan, tetapi dengan pendekatan ini Anda akan memerlukan sejumlah besar logika kondisional, yang akan menurunkan kelengkapan kode. Sebaliknya, saya memutuskan untuk melakukan ini:
- Ubah data menjadi format standar.
- Tampilan data yang diubah.
Transformasi data
Saya ingin data saya disajikan dalam format yang sesuai dengan argumen yang diterima oleh
h
. Tetapi sebelum mengonversinya, saya merencanakan struktur apa yang harus mereka miliki di file JSON:
// { tag: "", // HTML- cellClass: "", // . - null text: "", // , children: [] // , . , . }
Setiap objek mewakili satu sel tabel. Empat sel membentuk setiap baris tabel (mereka dikumpulkan dalam array):
// [ { cell1 }, { cell2 }, { cell3 }, { cell4 } ]
Titik input dapat berupa fungsi seperti berikut:
function createRow(data) {
Mari kita lihat tata letak lagi.
Tata Letak HalamanAnda dapat melihat bahwa pada kolom pertama elemen-elemennya ditata secara berbeda. Dan di kolom yang tersisa format yang sama digunakan. Jadi mari kita mulai dengan ini.
Biarkan saya mengingatkan Anda bahwa saya ingin menggunakan struktur JSON berikut sebagai model untuk menggambarkan setiap sel:
{ tag: "", cellClass: "", text: "", children: [] }
Dengan pendekatan ini, struktur seperti pohon akan digunakan untuk menggambarkan setiap sel. Ini justru karena beberapa sel mengandung kelompok anak-anak. Kami menggunakan dua fungsi berikut untuk membuat sel:
- Fungsi
createNode
mengambil setiap properti yang kami tertarik sebagai argumen. - Fungsi
createCell
memainkan peran pembungkus di sekitar createNode
, dengan bantuannya kami memeriksa apakah text
argumen text
array. Jika demikian, kami membuat array anak-anak.
Sekarang kita bisa melakukan sesuatu seperti ini:
function createRow(data) { let { text, element, classes = null, properties, usage } = data; let row = []; row[0] = "" row[1] = createCellData("p", ?????)
Saat membentuk kolom ketiga dan keempat, kami meneruskan
properties
dan
usage
sebagai argumen teks. Namun, kolom kedua berbeda dari kolom ketiga dan keempat. Di sini kami menampilkan nama-nama kelas, yang, dalam sumber data, disimpan dalam formulir ini:
"classes": { "base": "body-text body-text--lg", "variants": ["body-text--bold", "body-text--regular"] },
Selain itu, jangan lupa bahwa saat bekerja dengan header, kelas tidak digunakan. Oleh karena itu, kita perlu membuat nama tag header untuk baris yang sesuai (yaitu,
h1
,
h2
, dan sebagainya).
Kami membuat fungsi bantu yang memungkinkan kami mengubah data ini menjadi format yang memfasilitasi penggunaannya sebagai argumen
text
.
Sekarang kita bisa melakukan hal berikut:
function createRow(data) { let { text, element, classes = null, properties, usage } = data; let row = []; row[0] = "" row[1] = createCellData("p", displayClasses(element, classes))
Transformasi data yang digunakan untuk menunjukkan gaya
Kita perlu memutuskan apa yang harus dilakukan dengan kolom pertama dari tabel, menunjukkan contoh penerapan gaya. Kolom ini berbeda dari yang lain. Di sini kami menerapkan tag dan kelas baru untuk setiap sel alih-alih menggunakan kombinasi kelas yang digunakan oleh kolom yang tersisa:
<p class="body-text body-text--md body-text--semibold">
Alih-alih mencoba mengimplementasikan fungsi ini di
createCellData
atau
createNodeData
, saya mengusulkan untuk membuat fungsi baru yang akan mengambil keuntungan dari kemampuan fungsi-fungsi dasar ini yang melakukan transformasi data. Ini akan menerapkan mekanisme pemrosesan data baru:
function createDemoCellData(data) { let children; const classes = getClasses(data.classes);
Sekarang data string direduksi menjadi format yang dinormalisasi dan dapat diteruskan ke fungsi render:
function createRow(data) { let { text, element, classes = null, properties, usage } = data let row = [] row[0] = createDemoCellData(data) row[1] = createCellData("p", displayClasses(element, classes)) row[2] = createCellData("p", properties) row[3] = createCellData("p", usage) return row }
Render data
Berikut cara merender data yang ditampilkan di halaman:
Sekarang
semuanya sudah siap !
Di sini , sekali lagi, kode sumber.
Ringkasan
Perlu dikatakan bahwa pendekatan yang dipertimbangkan di sini adalah cara eksperimental untuk menyelesaikan masalah yang agak sepele. Saya yakin bahwa banyak orang akan mengatakan bahwa solusi ini sangat rumit dan berlebihan dengan kelebihan teknik. Mungkin saya akan setuju dengan itu.
Terlepas dari kenyataan bahwa pengembangan proyek ini memakan banyak waktu, data sekarang benar-benar terpisah dari presentasi. Sekarang, jika desainer kami masuk untuk menambahkan beberapa baris ke tabel, atau menghapus baris yang ada dari sana, saya tidak perlu menyapu kode HTML yang
membingungkan . Untuk melakukan ini, cukup bagi saya untuk mengubah beberapa properti di file JSON.
Apakah hasilnya sepadan dengan usaha? Saya pikir perlu melihat keadaan. Ini, bagaimanapun, sangat karakteristik pemrograman. Saya ingin mengatakan bahwa di kepala saya, dalam proses mengerjakan proyek ini, gambar berikut ini terus muncul.
Mungkin ini adalah jawaban untuk pertanyaan saya tentang apakah proyek ini sepadan dengan usaha yang dihabiskan untuk pengembangannya.
Pembaca yang budiman! ? ?
