Konteks Dan Variabel Dalam Generator Situs Statis Hugo

Diterbitkan: 2022-03-10
Ringkasan cepat Dalam artikel ini, kita melihat topik konteks dan variabel di Hugo, generator situs statis yang populer. Anda akan memahami konsep seperti konteks global, kontrol aliran, dan variabel dalam templat Hugo, serta aliran data dari file konten melalui templat ke parsial dan templat dasar.

Pada artikel ini, kita akan melihat lebih dekat bagaimana konteks bekerja di generator situs statis Hugo. Kami akan memeriksa bagaimana data mengalir dari konten ke template, bagaimana konstruksi tertentu mengubah data apa yang tersedia, dan bagaimana kami dapat meneruskan data ini ke template parsial dan dasar.

Artikel ini bukan pengantar Hugo . Anda mungkin akan mendapatkan hasil maksimal jika Anda memiliki pengalaman dengan Hugo, karena kami tidak akan membahas setiap konsep dari awal, melainkan fokus pada topik utama konteks dan variabel. Namun, jika Anda merujuk ke dokumentasi Hugo secara keseluruhan, Anda mungkin dapat mengikutinya bahkan tanpa pengalaman sebelumnya!

Kita akan mempelajari berbagai konsep dengan membuat halaman contoh. Tidak setiap file yang diperlukan untuk situs contoh akan dibahas secara mendetail, tetapi proyek lengkapnya tersedia di GitHub. Jika Anda ingin memahami bagaimana potongan-potongan itu menyatu, itu adalah titik awal yang baik. Harap perhatikan juga bahwa kami tidak akan membahas cara menyiapkan situs Hugo atau menjalankan server pengembangan — petunjuk untuk menjalankan contohnya ada di repositori.

Apa Itu Generator Situs Statis?

Jika konsep generator situs statis baru bagi Anda, inilah pengantar singkatnya! Generator situs statis mungkin paling baik dijelaskan dengan membandingkannya dengan situs dinamis. Situs dinamis seperti CMS umumnya mengumpulkan halaman dari awal untuk setiap kunjungan, mungkin mengambil data dari database dan menggabungkan berbagai template untuk melakukannya. Dalam praktiknya, penggunaan caching berarti halaman tidak terlalu sering dibuat ulang, tetapi untuk tujuan perbandingan ini, kita dapat menganggapnya seperti itu. Situs dinamis sangat cocok untuk konten dinamis : konten yang sering berubah, konten yang disajikan dalam banyak konfigurasi berbeda tergantung pada input, dan konten yang dapat dimanipulasi oleh pengunjung situs.

Sebaliknya, banyak situs yang jarang berubah dan menerima sedikit masukan dari pengunjung. Bagian "bantuan" untuk aplikasi, daftar artikel, atau eBook dapat menjadi contoh situs semacam itu. Dalam hal ini, lebih masuk akal untuk mengumpulkan halaman terakhir sekali ketika konten berubah, setelah itu menyajikan halaman yang sama untuk setiap pengunjung hingga konten berubah lagi.

Situs dinamis memiliki lebih banyak fleksibilitas, tetapi menempatkan lebih banyak permintaan pada server yang mereka jalankan. Mereka juga bisa sulit untuk didistribusikan secara geografis, terutama jika database terlibat. Generator situs statis dapat di-host di server mana pun yang mampu mengirimkan file statis, dan mudah didistribusikan.

Solusi umum saat ini, yang menggabungkan pendekatan ini, adalah JAMstack. “JAM” adalah singkatan dari JavaScript, API, dan markup dan menjelaskan blok bangunan aplikasi JAMstack: generator situs statis menghasilkan file statis untuk dikirim ke klien, tetapi tumpukan memiliki komponen dinamis dalam bentuk JavaScript yang berjalan di klien — komponen klien ini kemudian dapat menggunakan API untuk menyediakan fungsionalitas dinamis kepada pengguna.

Hugo

Hugo adalah generator situs statis yang populer. Itu ditulis dalam Go, dan fakta bahwa Go adalah bahasa pemrograman yang dikompilasi mengisyaratkan beberapa manfaat dan kekurangan Hugos. Pertama, Hugo sangat cepat , artinya ia menghasilkan situs statis dengan sangat cepat. Tentu saja, ini tidak berpengaruh pada seberapa cepat atau lambat situs yang dibuat menggunakan Hugo bagi pengguna akhir, tetapi bagi pengembang, fakta bahwa Hugo mengompilasi situs besar sekalipun dalam sekejap mata cukup berharga.

Namun, karena Hugo ditulis dalam bahasa yang dikompilasi, memperluasnya menjadi sulit . Beberapa generator situs lain memungkinkan Anda memasukkan kode Anda sendiri — dalam bahasa seperti Ruby, Python, atau JavaScript — ke dalam proses kompilasi. Untuk memperluas Hugo, Anda perlu menambahkan kode Anda ke Hugo sendiri dan mengkompilasi ulangnya — jika tidak, Anda terjebak dengan fungsi templat yang Hugo hadirkan dengan out-of-the-box.

Meskipun menyediakan beragam fungsi, fakta ini dapat membatasi jika pembuatan halaman Anda melibatkan logika yang rumit. Seperti yang kami temukan, memiliki situs yang awalnya dikembangkan berjalan pada platform dinamis, kasus di mana Anda telah mengambil kemampuan untuk memasukkan kode kustom Anda begitu saja cenderung menumpuk.

Tim kami mengelola berbagai situs web yang berkaitan dengan produk utama kami, klien Tower Git, dan kami baru-baru ini melihat untuk memindahkan beberapa di antaranya ke generator situs statis. Salah satu situs kami, situs "Belajar", tampak sangat cocok untuk proyek percontohan. Situs ini berisi berbagai materi pembelajaran gratis termasuk video, eBook, dan FAQ di Git, tetapi juga topik teknologi lainnya.

Kontennya sebagian besar bersifat statis, dan fitur interaktif apa pun yang ada (seperti pendaftaran buletin) sudah didukung oleh JavaScript. Pada akhir tahun 2020, kami mengonversi situs ini dari CMS kami sebelumnya ke Hugo , dan hari ini situs tersebut berjalan sebagai situs statis. Secara alami, kami belajar banyak tentang Hugo selama proses ini. Artikel ini adalah cara untuk membagikan beberapa hal yang kami pelajari.

Contoh Kami

Saat artikel ini berkembang dari pekerjaan kami dalam mengonversi halaman kami ke Hugo, tampaknya wajar untuk mengumpulkan halaman arahan hipotetis (sangat!) yang disederhanakan sebagai contoh. Fokus utama kami adalah template "daftar" yang dapat digunakan kembali.

Singkatnya, Hugo akan menggunakan templat daftar untuk halaman mana pun yang berisi subhalaman. Ada lebih banyak hierarki templat Hugos selain itu, tetapi Anda tidak harus mengimplementasikan setiap templat yang memungkinkan. Templat daftar tunggal sangat membantu. Ini akan digunakan dalam situasi apa pun yang meminta templat daftar di mana tidak ada lagi templat khusus yang tersedia.

Kasus penggunaan potensial termasuk halaman beranda, indeks blog, atau daftar FAQ. Template daftar kami yang dapat digunakan kembali akan berada di layouts/_default/list.html dalam proyek kami. Sekali lagi, sisa file yang diperlukan untuk mengkompilasi contoh kami tersedia di GitHub, di mana Anda juga bisa melihat lebih baik bagaimana potongan-potongan itu cocok satu sama lain. Repositori GitHub juga dilengkapi dengan template single.html — seperti namanya, template ini digunakan untuk halaman yang tidak memiliki subhalaman, tetapi bertindak sebagai satu bagian konten dengan haknya sendiri.

Sekarang setelah kita mengatur panggung dan menjelaskan apa yang akan kita lakukan, mari kita mulai!

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Konteks Atau “Titik”

Semuanya dimulai dengan titik. Dalam template Hugo, objek . — “titik” — mengacu pada konteks saat ini. Apa artinya ini? Setiap template yang dirender di Hugo memiliki akses ke kumpulan data — konteksnya . Ini awalnya diatur ke objek yang mewakili halaman yang sedang dirender, termasuk kontennya dan beberapa metadata. Konteksnya juga mencakup variabel di seluruh situs seperti opsi konfigurasi dan informasi tentang lingkungan saat ini. Anda akan mengakses bidang seperti judul halaman saat ini menggunakan .Title dan versi Hugo yang digunakan melalui .Hugo.Version — dengan kata lain, Anda mengakses bidang . struktur.

Yang penting, konteks ini bisa berubah, membuat referensi seperti `.Title` di atas menunjuk pada sesuatu yang lain atau bahkan membuatnya tidak valid. Ini terjadi, misalnya, saat Anda mengulang koleksi dari beberapa jenis menggunakan range , atau saat Anda membagi template menjadi sebagian dan template dasar . Kita akan melihat ini secara rinci nanti!

Hugo menggunakan paket "templat" Go, jadi ketika kita merujuk ke templat Hugo di artikel ini, kita benar-benar berbicara tentang templat Go. Hugo memang menambahkan banyak fungsi templat yang tidak tersedia di templat Go standar.

Menurut saya, konteks dan kemungkinan untuk rebind itu adalah salah satu fitur terbaik Hugos. Bagi saya, sangat masuk akal untuk selalu memiliki "titik" yang mewakili objek apa pun yang menjadi fokus utama templat saya pada titik tertentu, mengikatnya kembali seperlunya saat saya melanjutkan. Tentu saja, Anda juga dapat membuat diri Anda berantakan, tetapi sejauh ini saya senang dengan hal itu, sampai-sampai saya dengan cepat mulai melewatkannya di generator situs statis lain yang saya lihat.

Dengan ini, kami siap untuk melihat titik awal yang sederhana dari contoh kami — template di bawah ini, berada di lokasi layouts/_default/list.html di proyek kami:

 <html> <head> <title>{{ .Title }} | {{ .Site.Title }}</title> <link rel="stylesheet" href="/css/style.css"> </head> <body> <nav> <a class="logo" href="{{ "/" | relURL }}"> <img src="/img/tower-logo.svg"> <img src="/img/tower-claim.svg"> </a> <ul> <li><a href="/">Home</a></li> </ul> </nav> <section class="content"> <div class="container"> <h1>{{ .Title }}</h1> {{ .Content }} </div> </section> </body> </html>

Sebagian besar templat terdiri dari struktur HTML sederhana, dengan tautan lembar gaya, menu untuk navigasi, dan beberapa elemen dan kelas tambahan yang digunakan untuk penataan gaya. Hal yang menarik adalah di antara kurung kurawal , yang memberi isyarat kepada Hugo untuk masuk dan melakukan keajaibannya, mengganti apa pun yang ada di antara kurung kurawal dengan hasil mengevaluasi beberapa ekspresi dan berpotensi memanipulasi konteks juga.

Seperti yang dapat Anda tebak, {{ .Title }} dalam tag judul mengacu pada judul halaman saat ini, sedangkan {{ .Site.Title }} mengacu pada judul untuk seluruh situs, diatur dalam konfigurasi Hugo . Tag seperti {{ .Title }} hanya memberi tahu Hugo untuk mengganti tag itu dengan konten bidang Title dalam konteks saat ini.

Jadi, kami telah mengakses beberapa data milik halaman dalam template. Dari mana data ini berasal? Itulah topik bagian berikut.

Konten dan Materi Depan

Beberapa variabel yang tersedia dalam konteks secara otomatis disediakan oleh Hugo. Lainnya ditentukan oleh kami, terutama dalam file konten . Ada juga sumber data lain seperti file konfigurasi, variabel lingkungan, file data, dan bahkan API. Pada artikel ini fokus kita akan berada pada file konten sebagai sumber data.

Secara umum, satu file konten mewakili satu halaman. File konten tipikal mencakup konten utama halaman itu, tetapi juga metadata tentang halaman tersebut, seperti judulnya atau tanggal pembuatannya. Hugo mendukung beberapa format baik untuk konten utama maupun metadata. Dalam artikel ini kita akan menggunakan kombinasi yang mungkin paling umum: konten disediakan sebagai penurunan harga dalam file yang berisi metadata sebagai materi depan YAML.

Dalam praktiknya, itu berarti file konten dimulai dengan bagian yang dibatasi oleh garis yang berisi tiga tanda hubung di setiap ujungnya. Bagian ini merupakan bagian depan , dan di sini metadata didefinisikan menggunakan sintaks key: value (Seperti yang akan segera kita lihat, YAML juga mendukung struktur data yang lebih rumit). Hal depan diikuti oleh konten yang sebenarnya, ditentukan menggunakan bahasa markup Markdown.

Mari kita membuat hal-hal yang lebih konkret dengan melihat sebuah contoh. Berikut adalah file konten yang sangat sederhana dengan satu bidang materi depan dan satu paragraf konten:

 --- title: Home --- Home page of the Tower Git client. Over 100,000 developers and designers use Tower to be more productive!

(File ini berada di content/_index.md dalam proyek kami, dengan _index.md menunjukkan file konten untuk halaman yang memiliki subhalaman. Sekali lagi, repositori GitHub memperjelas ke mana file harus pergi.)

Dirender menggunakan template dari sebelumnya, bersama dengan beberapa gaya dan file periferal (semua ditemukan di GitHub), hasilnya terlihat seperti ini:

Halaman beranda klien Tower Git
(Pratinjau besar)

Anda mungkin bertanya-tanya apakah nama bidang di bagian depan file konten kami telah ditentukan sebelumnya, atau apakah kami dapat menambahkan bidang apa pun yang kami suka. Jawabannya adalah "keduanya". Ada daftar bidang yang telah ditentukan sebelumnya, tetapi kami juga dapat menambahkan bidang lain yang dapat kami buat. Namun, bidang ini diakses sedikit berbeda di template. Sementara bidang yang telah ditentukan sebelumnya seperti title diakses hanya sebagai .Title , bidang khusus seperti author diakses menggunakan .Params.author .

(Untuk referensi cepat tentang bidang yang telah ditentukan, bersama dengan hal-hal seperti fungsi, parameter fungsi, dan variabel halaman, lihat lembar contekan Hugo kami sendiri!)

Variabel .Content , digunakan untuk mengakses konten utama dari file konten di template Anda, adalah khusus. Hugo memiliki fitur “shortcode” yang memungkinkan Anda menggunakan beberapa tag tambahan di konten penurunan harga. Anda juga dapat menentukan sendiri. Sayangnya, shortcode ini hanya akan bekerja melalui variabel .Content — sementara Anda dapat menjalankan bagian lain dari data melalui filter Markdown, ini tidak akan menangani shortcode dalam konten.

Catatan di sini tentang variabel yang tidak ditentukan: mengakses bidang yang telah ditentukan sebelumnya seperti .Date selalu berfungsi, meskipun Anda belum menyetelnya — nilai kosong akan dikembalikan dalam kasus ini. Mengakses bidang khusus yang tidak ditentukan, seperti .Params.thisHasNotBeenSet , juga berfungsi, mengembalikan nilai kosong. Namun, mengakses bidang tingkat atas yang tidak ditentukan sebelumnya seperti .thisDoesNotExist akan mencegah situs dikompilasi.

Seperti yang ditunjukkan oleh .Params.author serta .Hugo.version dan .Site.title sebelumnya, pemanggilan berantai dapat digunakan untuk mengakses bidang yang bersarang di beberapa struktur data lainnya. Kita dapat mendefinisikan struktur seperti itu di materi depan kita. Mari kita lihat sebuah contoh, di mana kita mendefinisikan peta , atau kamus, menentukan beberapa properti untuk spanduk pada halaman dalam file konten kita. Berikut adalah content/_index.md :

 --- title: Home banner: headline: Try Tower For Free! subline: Download our trial to try Tower for 30 days --- Home page of the Tower Git client. Over 100,000 developers and designers use Tower to be more productive!

Sekarang, mari tambahkan banner ke template kita, mengacu pada data banner menggunakan .Params seperti yang dijelaskan di atas:

 <html> ... <body> ... <aside> <h2>{{ .Params.banner.headline }}</h2> <p>{{ .Params.banner.subline}}</p> </aside> </body> </html>

Berikut tampilan situs kami sekarang:

Halaman beranda klien Tower Git dengan spanduk bertuliskan Coba Menara Gratis
(Pratinjau besar)

Baik! Saat ini, kami mengakses bidang konteks default tanpa masalah. Namun, seperti yang disebutkan sebelumnya, konteks ini tidak tetap, tetapi dapat berubah.

Mari kita lihat bagaimana hal itu bisa terjadi.

Alur kontrol

Pernyataan kontrol aliran adalah bagian penting dari bahasa templating, memungkinkan Anda melakukan berbagai hal bergantung pada nilai variabel, mengulang data, dan banyak lagi. Templat Hugo menyediakan kumpulan konstruksi yang diharapkan, termasuk if/else untuk logika kondisional, dan range untuk perulangan. Di sini, kami tidak akan membahas kontrol aliran di Hugo secara umum (untuk lebih lanjut tentang ini, lihat dokumentasi), tetapi fokus pada bagaimana pernyataan ini memengaruhi konteks. Dalam hal ini, pernyataan yang paling menarik adalah with dan range .

Mari kita mulai with . Pernyataan ini memeriksa apakah beberapa ekspresi memiliki nilai "tidak kosong", dan, jika ada, mengikat ulang konteks untuk merujuk ke nilai ekspresi tersebut . Tag end menunjukkan titik di mana pengaruh pernyataan with berhenti, dan konteksnya kembali ke apa pun sebelumnya. Dokumentasi Hugo mendefinisikan nilai yang tidak kosong sebagai false, 0, dan array, irisan, peta, atau string dengan panjang nol.

Saat ini, template daftar kami tidak melakukan banyak daftar sama sekali. Mungkin masuk akal jika templat daftar benar-benar menampilkan beberapa subhalamannya dalam beberapa cara. Ini memberi kami kesempatan sempurna untuk contoh pernyataan kontrol aliran kami.

Mungkin kami ingin menampilkan beberapa konten unggulan di bagian atas halaman kami. Ini bisa berupa konten apa pun — posting blog, artikel bantuan, atau resep, misalnya. Saat ini, katakanlah situs contoh Tower kami memiliki beberapa halaman yang menyoroti fitur-fiturnya, kasus penggunaan, halaman bantuan, halaman blog, dan halaman “platform pembelajaran”. Ini semua terletak di direktori content/ . Kami mengonfigurasi bagian konten mana yang akan ditampilkan dengan menambahkan bidang di file konten untuk beranda kami, content/_index.md . Halaman dirujuk oleh jalurnya, dengan asumsi direktori konten sebagai root, seperti:

 --- title: Home banner: headline: Try Tower For Free! subline: Download our trial to try Tower for 30 days without limitations featured: /features.md ... --- ...

Selanjutnya, templat daftar kita harus dimodifikasi untuk menampilkan konten ini. Hugo memiliki fungsi template, .GetPage , yang memungkinkan kita merujuk ke objek halaman selain yang sedang kita render. Ingat bagaimana konteksnya, . , awalnya terikat ke objek yang mewakili halaman yang dirender? Menggunakan .GetPage dan with , kita dapat mengembalikan sementara konteks ke halaman lain, merujuk ke bidang halaman itu saat menampilkan konten unggulan kita:

 <nav> ... </nav> <section class="featured"> <div class="container"> {{ with .GetPage .Params.featured }} <article> <h2>{{ .Title }}</h2> {{ .Summary }} <p><a href="{{ .Permalink }}">Read more →</a></p> </article> {{ end }} </div> </section>

Di sini, {{ .Title }} , {{ .Summary }} dan {{ .Permalink }} antara tag with dan end merujuk ke bidang tersebut di halaman unggulan , dan bukan bidang utama yang dirender.

Selain memiliki konten unggulan, mari daftar beberapa konten lagi di bagian bawah. Sama seperti konten unggulan, bagian konten yang terdaftar akan ditentukan di content/_index.md , file konten untuk beranda kita. Kami akan menambahkan daftar jalur konten ke materi depan kami seperti ini (dalam hal ini juga menentukan judul bagian):

 --- ... listing_headline: Featured Pages listing: - /help.md - /use-cases.md - /blog/_index.md - /learn.md ---

Alasan halaman blog memiliki direktori sendiri dan file _index.md adalah karena blog akan memiliki subhalamannya sendiri — posting blog.

Untuk menampilkan daftar ini di template kita, kita akan menggunakan range . Tidak mengherankan, pernyataan ini akan mengulang daftar, tetapi juga akan mengubah konteks ke setiap elemen daftar secara bergantian. Ini sangat nyaman untuk daftar konten kami.

Perhatikan bahwa, dari perspektif Hugo, "daftar" hanya berisi beberapa string. Untuk setiap iterasi dari loop “rentang”, konteksnya akan terikat ke salah satu string tersebut . Untuk mendapatkan akses ke objek halaman yang sebenarnya, kami menyediakan string jalurnya (sekarang nilai . ) sebagai argumen ke .GetPage . Kemudian, kita akan menggunakan pernyataan with lagi untuk mengembalikan konteks ke objek halaman yang terdaftar daripada string jalurnya. Sekarang, mudah untuk menampilkan konten dari setiap halaman yang terdaftar secara bergantian:

 <aside> ... </aside> <section class="listing"> <div class="container"> <h1>{{ .Params.listing_headline }}</h1> <div> {{ range .Params.listing }} {{ with $.GetPage . }} <article> <h2>{{ .Title }}</h2> {{ .Summary }} <p><a href="{{ .Permalink }}">Read more →</a></p> </article> {{ end }} {{ end }} </div> </div> </section>

Berikut tampilan situs saat ini:

Halaman beranda klien Tower Git menampilkan empat halaman dan spanduk unggulan
(Pratinjau besar)

Tapi tunggu dulu, ada yang aneh dengan template di atas — daripada memanggil .GetPage , kita memanggil $.GetPage . Bisakah Anda menebak mengapa .GetPage tidak berfungsi?

Notasi .GetPage menunjukkan bahwa fungsi GetPage adalah metode konteks saat ini. Memang, dalam konteks default, ada metode seperti itu, tetapi kami baru saja melanjutkan dan mengubah konteksnya ! Saat kita memanggil .GetPage , konteksnya terikat ke string, yang tidak memiliki metode itu. Cara kita mengatasi ini adalah topik dari bagian berikutnya.

Konteks Global

Seperti yang terlihat di atas, ada situasi di mana konteksnya telah diubah, tetapi kami masih ingin mengakses konteks aslinya. Di sini, itu karena kita ingin memanggil metode yang ada dalam konteks aslinya — situasi umum lainnya adalah ketika kita ingin mengakses beberapa properti dari halaman utama yang sedang dirender. Tidak masalah, ada cara mudah untuk melakukannya.

Dalam templat Hugo, $ , yang dikenal sebagai konteks global , mengacu pada nilai asli konteks — konteks seperti saat pemrosesan templat dimulai. Di bagian sebelumnya, itu digunakan untuk memanggil metode .GetPage meskipun kami telah mengembalikan konteks ke string. Sekarang, kami juga akan menggunakannya untuk mengakses bidang halaman yang sedang dirender.

Di awal artikel ini, saya menyebutkan bahwa templat daftar kami dapat digunakan kembali. Sejauh ini, kami hanya menggunakannya untuk halaman beranda, merender file konten yang terletak di content/_index.md . Dalam repositori contoh, ada file konten lain yang akan dirender menggunakan template ini: content/blog/_index.md . Ini adalah halaman indeks untuk blog, dan seperti halaman beranda, ini menunjukkan konten unggulan dan mencantumkan beberapa lagi — posting blog, dalam hal ini.

Sekarang, katakanlah kita ingin menampilkan konten yang terdaftar sedikit berbeda di halaman beranda — tidak cukup untuk menjamin template terpisah, tetapi sesuatu yang bisa kita lakukan dengan pernyataan kondisional di template itu sendiri. Sebagai contoh, kami akan menampilkan konten yang terdaftar dalam kisi dua kolom, sebagai lawan dari daftar satu kolom, jika kami mendeteksi bahwa kami sedang merender halaman beranda.

Hugo hadir dengan metode halaman, .IsHome , yang menyediakan fungsionalitas yang kita butuhkan. Kami akan menangani perubahan aktual dalam presentasi dengan menambahkan kelas ke masing-masing bagian konten ketika kami menemukan kami berada di halaman beranda, memungkinkan file CSS kami melakukan sisanya.

Kita dapat, tentu saja, menambahkan kelas ke elemen body atau beberapa elemen yang mengandung sebagai gantinya, tetapi itu tidak akan memungkinkan demonstrasi konteks global yang baik. Pada saat kami menulis HTML untuk bagian konten yang terdaftar, . merujuk ke halaman yang terdaftar , tetapi IsHome perlu dipanggil pada halaman utama yang sedang dirender. Konteks global datang untuk menyelamatkan kita:

 <section class="listing"> <div class="container"> <h1>{{ .Params.listing_headline }}</h1> <div> {{ range .Params.listing }} {{ with $.GetPage . }} <article{{ if $.IsHome }} class="home"{{ end }}> <h2>{{ .Title }}</h2> {{ .Summary }} <p><a href="{{ .Permalink }}">Read more →</a></p> </article> {{ end }} {{ end }} </div> </div> </section>

Indeks blog terlihat seperti halaman beranda kami, meskipun dengan konten yang berbeda:

Halaman beranda klien Tower Git menampilkan empat halaman unggulan dan spanduk dengan konten berbeda
(Pratinjau besar)

…tetapi beranda kami sekarang menampilkan konten unggulannya dalam kotak:

Halaman beranda klien Tower Git menampilkan empat halaman unggulan dalam dua baris dengan dua kolom dan tidak keempatnya di atas satu sama lain seperti sebelumnya
(Pratinjau besar)

Template Sebagian

Saat membangun situs web nyata, akan berguna untuk membagi template Anda menjadi beberapa bagian dengan cepat. Mungkin Anda ingin menggunakan kembali beberapa bagian tertentu dari sebuah template, atau mungkin Anda hanya ingin membagi sebuah template yang besar dan berat menjadi beberapa bagian yang koheren. Untuk tujuan ini, templat parsial Hugo adalah cara yang harus dilakukan.

Dari perspektif konteks, hal yang penting di sini adalah ketika kami menyertakan sebagian template, kami secara eksplisit meneruskan konteks yang ingin kami sediakan untuknya. Praktik yang umum adalah meneruskan dalam konteks seperti saat parsial disertakan, seperti ini: {{ partial "my/partial.html" . }} {{ partial "my/partial.html" . }} . Jika titik di sini merujuk ke halaman yang sedang dirender, itulah yang akan diteruskan ke sebagian; jika konteksnya telah melambung ke sesuatu yang lain, itulah yang diturunkan.

Anda tentu saja dapat mengubah konteks dalam templat parsial seperti pada templat normal. Dalam hal ini, konteks global, $ , mengacu pada konteks asli yang diteruskan ke sebagian, bukan halaman utama yang dirender (kecuali itu yang diteruskan).

Jika kita ingin template parsial memiliki akses ke beberapa bagian data tertentu, kita mungkin mengalami masalah jika kita hanya meneruskan ini ke parsial. Ingat masalah kita sebelumnya dengan mengakses metode halaman setelah mengikat ulang konteks? Hal yang sama berlaku untuk partials , tetapi dalam kasus ini konteks global tidak dapat membantu kita — jika kita telah memasukkan, katakanlah, string ke templat parsial, konteks global dalam partial akan merujuk ke string itu, dan kita menang 't dapat memanggil metode yang ditentukan pada konteks halaman.

Solusi untuk masalah ini terletak pada melewatkan lebih dari satu bagian data saat memasukkan sebagian. Namun, kami hanya diperbolehkan memberikan satu argumen untuk panggilan parsial. Namun, kita dapat menjadikan argumen ini sebagai tipe data gabungan, umumnya peta (dikenal sebagai kamus atau hash dalam bahasa pemrograman lain).

Di peta ini, kita dapat, misalnya, memiliki kunci Page yang disetel ke objek halaman saat ini, bersama dengan kunci lain untuk data kustom apa pun untuk diteruskan. Objek halaman kemudian akan tersedia sebagai .Page di sebagian, dan yang lainnya nilai peta diakses dengan cara yang sama. Peta dibuat menggunakan fungsi templat dict , yang mengambil sejumlah argumen genap, ditafsirkan secara bergantian sebagai kunci, nilainya, kunci, nilainya, dan seterusnya.

Dalam contoh template kami, mari pindahkan kode untuk konten unggulan dan terdaftar kami menjadi sebagian. Untuk konten unggulan, cukup dengan memasukkan objek halaman unggulan. Konten yang terdaftar, bagaimanapun, memerlukan akses ke metode .IsHome selain konten terdaftar tertentu yang sedang dirender. Seperti disebutkan sebelumnya, sementara .IsHome tersedia di objek halaman untuk halaman yang terdaftar juga, itu tidak akan memberi kita jawaban yang benar — kita ingin tahu apakah halaman utama yang dirender adalah halaman beranda.

Sebagai gantinya, kita dapat meneruskan set boolean ke hasil pemanggilan .IsHome , tetapi mungkin sebagian akan memerlukan akses ke metode halaman lain di masa mendatang — mari kita lanjutkan dengan meneruskan objek halaman utama serta objek halaman yang terdaftar. Dalam contoh kami, halaman utama ditemukan di $ dan halaman yang terdaftar di . . Jadi, di peta yang diteruskan ke sebagian yang listed , Page kunci mendapatkan nilai $ sedangkan kunci "Terdaftar" mendapatkan nilai . . Ini adalah template utama yang diperbarui:

 <body> <nav> <a class="logo" href="{{ "/" | relURL }}"> <img src="/img/tower-logo.svg"> <img src="/img/tower-claim.svg"> </a> <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> </nav> <section class="featured"> <div class="container"> {{ with .GetPage .Params.featured }} {{ partial "partials/featured.html" . }} {{ end }} </div> </section> <section class="content"> <div class="container"> <h1>{{ .Title }}</h1> {{ .Content }} </div> </section> <aside> <h2>{{ .Params.banner.headline }}</h2> <p>{{ .Params.banner.subline}}</p> </aside> <section class="listing"> <div class="container"> <h1>{{ .Params.listing_headline }}</h1> <div> {{ range .Params.listing }} {{ with $.GetPage . }} {{ partial "partials/listed.html" (dict "Page" $ "Listed" .) }} {{ end }} {{ end }} </div> </div> </section> </body>

Konten sebagian "unggulan" kami tidak berubah dibandingkan dengan saat itu adalah bagian dari templat daftar:

 <article> <h2>{{ .Title }}</h2> {{ .Summary }} <p><a href="{{ .Permalink }}">Read more →</a></p> </article>

Namun, sebagian untuk konten terdaftar kami mencerminkan fakta bahwa objek halaman asli sekarang ditemukan di .Page sedangkan bagian konten yang terdaftar ditemukan di .Listed :

 <article{{ if .Page.IsHome }} class="home"{{ end }}> <h2>{{ .Listed.Title }}</h2> {{ .Listed.Summary }} <p><a href="{{ .Listed.Permalink }}">Read more →</a></p> </article>

Hugo juga menyediakan fungsionalitas templat dasar yang memungkinkan Anda memperluas templat dasar umum , bukan menyertakan subtemplat. Dalam hal ini, konteksnya bekerja dengan cara yang sama: saat memperluas template dasar, Anda memberikan data yang akan membentuk konteks asli dalam template tersebut.

Variabel Khusus

Dimungkinkan juga untuk menetapkan dan menetapkan kembali variabel khusus Anda sendiri dalam template Hugo. Ini akan tersedia di template di mana mereka dideklarasikan, tetapi tidak akan masuk ke sebagian atau template dasar kecuali kami secara eksplisit meneruskannya. Variabel khusus yang dideklarasikan di dalam "blok" seperti yang ditentukan oleh pernyataan if hanya akan tersedia di dalam blok itu — jika kita ingin merujuknya ke luar blok, kita perlu mendeklarasikannya di luar blok, lalu memodifikasinya di dalam blok sesuai kebutuhan.

Variabel khusus memiliki nama yang diawali dengan tanda dolar ( $ ). Untuk mendeklarasikan variabel dan memberikan nilai pada saat yang sama, gunakan operator := . Penugasan selanjutnya ke variabel menggunakan operator = (tanpa titik dua). Variabel tidak dapat ditetapkan sebelum dideklarasikan, dan tidak dapat dideklarasikan tanpa memberinya nilai.

Salah satu kasus penggunaan untuk variabel khusus adalah menyederhanakan panggilan fungsi yang panjang dengan menetapkan beberapa hasil antara ke variabel bernama yang tepat. Misalnya, kita dapat menetapkan objek halaman unggulan ke variabel bernama $featured dan kemudian menyediakan variabel ini ke pernyataan with . Kami juga dapat menempatkan data untuk dipasok ke parsial "terdaftar" dalam variabel dan memberikannya ke panggilan parsial.

Inilah yang akan terlihat seperti template kami dengan perubahan tersebut:

 <section class="featured"> <div class="container"> {{ $featured := .GetPage .Params.featured }} {{ with $featured }} {{ partial "partials/featured.html" . }} {{ end }} </div> </section> <section class="content"> ... </section> <aside> ... </aside> <section class="listing"> <div class="container"> <h1>{{ .Params.listing_headline }}</h1> <div> {{ range .Params.listing }} {{ with $.GetPage . }} {{ $context := (dict "Page" $ "Listed" .) }} {{ partial "partials/listed.html" $context }} {{ end }} {{ end }} </div> </div> </section>

Berdasarkan pengalaman saya dengan Hugo, saya akan merekomendasikan menggunakan variabel khusus secara bebas segera setelah Anda mencoba menerapkan beberapa logika yang lebih terlibat dalam sebuah template. Meskipun wajar untuk mencoba menjaga kode Anda tetap ringkas, ini dapat dengan mudah membuat segalanya menjadi kurang jelas daripada yang seharusnya, membingungkan Anda dan orang lain.

Sebagai gantinya, gunakan variabel bernama deskriptif untuk setiap langkah dan jangan khawatir tentang menggunakan dua baris (atau tiga, atau empat, dll.) di mana seseorang akan melakukannya.

.Menggores

Akhirnya, mari kita bahas mekanisme .Scratch . Di versi Hugo sebelumnya, variabel khusus hanya dapat ditetapkan satu kali; itu tidak mungkin untuk mendefinisikan kembali variabel khusus. Saat ini, variabel kustom dapat didefinisikan ulang, yang membuat .Scratch kurang penting, meskipun masih memiliki kegunaannya.

Singkatnya, .Scratch adalah area awal yang memungkinkan Anda untuk mengatur dan memodifikasi variabel Anda sendiri , seperti variabel khusus. Tidak seperti variabel khusus, .Scratch termasuk dalam konteks halaman, jadi meneruskan konteks itu ke sebagian, misalnya, akan membawa variabel awal bersamanya secara otomatis.

Anda dapat mengatur dan mengambil variabel pada .Scratch dengan memanggil metodenya Set dan Get . Ada lebih banyak metode daripada ini, misalnya untuk mengatur dan memperbarui tipe data gabungan, tetapi dua metode ini akan cukup untuk kebutuhan kita di sini. Set membutuhkan dua parameter : kunci dan nilai untuk data yang ingin Anda atur. Get hanya membutuhkan satu: kunci untuk data yang ingin Anda ambil.

Sebelumnya, kami menggunakan dict untuk membuat struktur data peta untuk meneruskan beberapa bagian data ke sebagian. Ini dilakukan agar sebagian untuk halaman yang terdaftar akan memiliki akses ke konteks halaman asli dan objek halaman terdaftar tertentu. Menggunakan .Scratch tidak selalu merupakan cara yang lebih baik atau lebih buruk untuk melakukan ini — mana yang lebih disukai mungkin bergantung pada situasinya.

Mari kita lihat seperti apa templat daftar kita menggunakan .Scratch alih-alih dict untuk meneruskan data ke parsial. Kami memanggil $.Scratch.Get (sekali lagi menggunakan konteks global) untuk mengatur variabel awal "terdaftar" ke . — dalam hal ini, objek halaman yang terdaftar. Kemudian kita hanya meneruskan objek halaman, $ , ke parsial. Variabel awal akan mengikuti secara otomatis.

 <section class="listing"> <div class="container"> <h1>{{ .Params.listing_headline }}</h1> <div> {{ range .Params.listing }} {{ with $.GetPage . }} {{ $.Scratch.Set "listed" . }} {{ partial "partials/listed.html" $ }} {{ end }} {{ end }} </div> </div> </section>

Ini akan memerlukan beberapa modifikasi pada bagian yang terdaftar.html juga — konteks halaman asli sekarang tersedia sebagai “titik” sementara halaman yang terdaftar diambil dari objek listed.html .Scratch Kami akan menggunakan variabel khusus untuk menyederhanakan akses ke halaman yang terdaftar:

 <article{{ if .IsHome }} class="home"{{ end }}> {{ $listed := .Scratch.Get "listed" }} <h2>{{ $listed.Title }}</h2> {{ $listed.Summary }} <p><a href="{{ $listed.Permalink }}">Read more →</a></p> </article>

Salah satu argumen untuk melakukan sesuatu dengan cara ini adalah konsistensi. Menggunakan .Scratch , Anda dapat membiasakan untuk selalu meneruskan objek halaman saat ini ke sebagian apa pun, menambahkan data tambahan apa pun sebagai variabel awal. Kemudian, setiap kali Anda menulis atau mengedit sebagian, Anda tahu bahwa . adalah objek halaman. Tentu saja, Anda dapat membuat konvensi untuk diri sendiri menggunakan peta yang diteruskan juga: selalu mengirimkan objek halaman sebagai .Page , misalnya.

Kesimpulan

Ketika datang ke konteks dan data, generator situs statis membawa manfaat dan keterbatasan. Di satu sisi, operasi yang terlalu tidak efisien saat dijalankan untuk setiap kunjungan halaman mungkin sangat baik jika dijalankan hanya sekali saat halaman dikompilasi. Di sisi lain, mungkin akan mengejutkan Anda seberapa sering akan berguna untuk memiliki akses ke beberapa bagian dari permintaan jaringan bahkan di situs yang sebagian besar statis.

Untuk menangani parameter string kueri , misalnya, di situs statis, Anda harus menggunakan JavaScript atau beberapa solusi eksklusif seperti pengalihan Netlify. Intinya di sini adalah bahwa meskipun lompatan dari situs dinamis ke situs statis sederhana secara teori, hal itu membutuhkan perubahan pola pikir. Pada awalnya, mudah untuk kembali ke kebiasaan lama Anda, tetapi latihan akan membuat Anda sempurna.

Dengan itu, kami menyimpulkan pandangan kami tentang manajemen data di generator situs statis Hugo. Even though we focused only on a narrow sector of its functionality, there are certainly things we didn't cover that could have been included. Nevertheless, I hope this article gave you some added insight into how data flows from content files, to templates, to subtemplates and how it can be modified along the way.

Note : If you already have some Hugo experience, we have a nice resource for you, quite appropriately residing on our aforementioned, Hugo-driven “Learn” site! When you just need to check the order of the arguments to the replaceRE function, how to retrieve the next page in a section, or what the “expiration date” front matter field is called, a cheat sheet comes in handy. We've put together just such a reference, so download a Hugo cheat sheet, in a package also featuring a host of other cheat sheets on everything from Git to the Visual Studio Code editor.

Bacaan lebih lanjut

If you're looking for more information on Hugo, here are some nice resources:

  • The official Hugo documentation is always a good place to start!
  • A great series of in-depth posts on Hugo on Regis Philibert's blog.