Fermer

février 18, 2021

Contexte et variables dans le générateur de site statique Hugo


À propos de l'auteur

Kristian fait du développement Web et de l'écriture dans le cadre de l'équipe derrière le client Tower Git . Basé sur une île du sud-ouest de la Finlande, il aime courir, lire…
En savoir plus sur
Kristian

Dans cet article, nous examinons le sujet du contexte et des variables dans Hugo, un générateur de site statique populaire. Vous comprendrez des concepts tels que le contexte global, le contrôle de flux et les variables dans les modèles Hugo, ainsi que le flux de données des fichiers de contenu aux modèles en passant par les modèles partiels et les modèles de base.

Dans cet article, nous examinerons de plus près comment le contexte fonctionne dans le générateur de site statique Hugo . Nous examinerons comment les données circulent du contenu aux modèles, comment certaines constructions modifient les données disponibles et comment nous pouvons transmettre ces données aux partiels et aux modèles de base.

Cet article n'est pas une introduction à Hugo . Vous en tirerez probablement le meilleur parti si vous avez une certaine expérience avec Hugo, car nous ne passerons pas en revue chaque concept à partir de zéro, mais nous nous concentrerons plutôt sur le sujet principal du contexte et des variables. Cependant, si vous vous référez à la documentation de Hugo tout au long, vous pourrez peut-être suivre même sans expérience préalable!

Nous étudierons différents concepts en créant une page d'exemple. Tous les fichiers requis pour l'exemple de site ne seront pas traités en détail, mais le projet complet est disponible sur GitHub . Si vous voulez comprendre comment les pièces s’emboîtent, c’est un bon point de départ. Veuillez également noter que nous ne couvrirons pas comment configurer un site Hugo ou exécuter le serveur de développement – les instructions pour exécuter l'exemple se trouvent dans le référentiel.

Qu'est-ce qu'un générateur de site statique? [19659009] Si le concept de générateurs de sites statiques est nouveau pour vous, voici une brève introduction! Les générateurs de sites statiques sont peut-être mieux décrits en les comparant à des sites dynamiques. Un site dynamique comme un CMS assemble généralement une page à partir de zéro pour chaque visite, peut-être en récupérant des données à partir d'une base de données et en combinant divers modèles pour ce faire. En pratique, l'utilisation de la mise en cache signifie que la page n'est pas régénérée assez souvent, mais aux fins de cette comparaison, nous pouvons la penser de cette façon. Un site dynamique est bien adapté au contenu dynamique : contenu qui change souvent, contenu qui est présenté dans de nombreuses configurations différentes selon l'entrée, et contenu qui peut être manipulé par le visiteur du site.

En revanche , de nombreux sites changent rarement et acceptent peu de commentaires des visiteurs. Une section «aide» pour une application, une liste d'articles ou un livre électronique pourraient être des exemples de tels sites. Dans ce cas, il est plus judicieux d'assembler les pages finales une fois lorsque le contenu change, puis de servir les mêmes pages à chaque visiteur jusqu'à ce que le contenu change à nouveau.

Les sites dynamiques ont plus de flexibilité, mais place plus de demande sur le serveur sur lequel ils fonctionnent. Ils peuvent également être difficiles à distribuer géographiquement, surtout si des bases de données sont impliquées. Les générateurs de sites statiques peuvent être hébergés sur n'importe quel serveur capable de livrer des fichiers statiques, et sont faciles à distribuer.

Une solution courante aujourd'hui, qui mélange ces approches, est le JAMstack . «JAM» signifie JavaScript, API et balisage et décrit les éléments constitutifs d'une application JAMstack: un générateur de site statique génère fichiers statiques pour livraison au client, mais la pile a un composant dynamique sous la forme de JavaScript s'exécutant sur le client – ce composant client peut ensuite utiliser des API pour fournir des fonctionnalités dynamiques à l'utilisateur.

Hugo

Hugo est un générateur de site statique populaire. Il est écrit en Go, et le fait que Go soit un langage de programmation compilé fait allusion à certains des avantages et inconvénients de Hugos. D'une part, Hugo est très rapide ce qui signifie qu'il génère des sites statiques très rapidement. Bien sûr, cela n'a aucune incidence sur la vitesse ou la lenteur des sites créés avec Hugo pour l'utilisateur final, mais pour le développeur, le fait qu'Hugo compile même de grands sites en un clin d'œil est assez précieux.

, comme Hugo est écrit dans un langage compilé, l'étendre est difficile . Certains autres générateurs de site vous permettent d'insérer votre propre code – dans des langages comme Ruby, Python ou JavaScript – dans le processus de compilation. Pour étendre Hugo, vous auriez besoin d'ajouter votre code à Hugo lui-même et de le recompiler – sinon, vous êtes coincé avec les fonctions de modèle qu'Hugo est livré avec.

Pendant qu'il fournit une riche variété de fonctions, ce fait peut devenir limitant si la génération de vos pages implique une logique compliquée. Comme nous l'avons constaté, ayant un site développé à l'origine sur une plate-forme dynamique, les cas où vous avez pris la possibilité de déposer votre code personnalisé pour acquis ont tendance à s'accumuler.

Notre équipe gère une variété de sites Web relatifs à notre produit principal, le client Tower Git et nous avons récemment envisagé de déplacer certains d'entre eux vers un générateur de site statique. L'un de nos sites, le site «Learn» semblait particulièrement bien adapté à un projet pilote. Ce site contient une variété de matériel d'apprentissage gratuit, y compris des vidéos, des livres électroniques et des FAQ sur Git, mais aussi d'autres sujets techniques.

Son contenu est en grande partie de nature statique et les fonctionnalités interactives (comme les inscriptions à la newsletter) étaient déjà alimenté par JavaScript. Fin 2020, nous avons converti ce site de notre précédent CMS en Hugo et aujourd'hui il fonctionne comme un site statique. Naturellement, nous avons beaucoup appris sur Hugo au cours de ce processus. Cet article est une façon de partager certaines des choses que nous avons apprises.

Notre exemple

Comme cet article est né de notre travail sur la conversion de nos pages en Hugo, il semble naturel de mettre en place une hypothétique (très!) Simplifiée page de destination comme exemple. Notre objectif principal sera un modèle de «liste» réutilisable.

En bref, Hugo utilisera un modèle de liste pour toute page contenant des sous-pages. La hiérarchie des modèles Hugos va bien au-delà de cela, mais vous n’avez pas à mettre en œuvre tous les modèles possibles. Un modèle de liste unique va très loin. Il sera utilisé dans toutes les situations nécessitant un modèle de liste où aucun modèle plus spécialisé n'est disponible.

Les cas d'utilisation potentiels incluent une page d'accueil, un index de blog ou une liste de FAQ. Notre modèle de liste réutilisable résidera dans layouts / _default / list.html dans notre projet. Encore une fois, le reste des fichiers nécessaires pour compiler notre exemple sont disponibles sur GitHub où vous pouvez également avoir un meilleur aperçu de la façon dont les pièces s'emboîtent. Le référentiel GitHub est également livré avec un modèle single.html – comme son nom l'indique, ce modèle est utilisé pour les pages qui n'ont pas de sous-pages, mais agissent comme des éléments uniques de contenu à part entière.

Maintenant que nous avons préparé le terrain et expliqué ce que nous allons faire, commençons!

Le contexte ou «le point»

Tout commence par le point. Dans un modèle Hugo, l'objet . – «le point» – fait référence au contexte actuel. Qu'est-ce que ça veut dire? Chaque modèle rendu dans Hugo a accès à un ensemble de données – son ​​contexte . Ceci est initialement défini sur un objet représentant la page en cours de rendu, y compris son contenu et certaines métadonnées. Le contexte comprend également des variables à l'échelle du site telles que les options de configuration et des informations sur l'environnement actuel. Vous accéderiez à un champ comme le titre de la page actuelle en utilisant .Title et la version de Hugo utilisée via .Hugo.Version – en d'autres termes, vous accédez à champs du .

Surtout, ce contexte peut changer, faisant une référence comme `.Title` ci-dessus pointant vers autre chose ou même le rendant invalide. Cela se produit, par exemple, lorsque vous bouclez sur une collection quelconque en utilisant range ou lorsque vous ** divisez les modèles en partiels et modèles de base **. Nous y reviendrons en détail plus tard!

Hugo utilise le package "templates" Go. Ainsi, lorsque nous faisons référence aux modèles Hugo dans cet article, nous parlons vraiment de modèles Go. Hugo ajoute beaucoup de fonctions de modèle non disponibles dans les modèles standard de Go.

À mon avis, le contexte et la possibilité de le relier est l'une des meilleures fonctionnalités de Hugos. Pour moi, il est très logique de toujours avoir «le point» qui représente l'objet principal de mon modèle à un moment donné, en le reliant si nécessaire au fur et à mesure. Bien sûr, il est également possible de se retrouver dans un désordre enchevêtré, mais j'en ai été satisfait jusqu'à présent, dans la mesure où j'ai rapidement commencé à le manquer dans n'importe quel autre générateur de site statique que j'ai regardé.

Avec ceci , nous sommes prêts à regarder l'humble point de départ de notre exemple – le modèle ci-dessous, résidant à l'emplacement layouts / _default / list.html dans notre projet:


  
     {{.Title} } | {{.Site.Title}} 
    
  
  
    
    

{{.Title}}

{{ .Teneur }}

La plupart du modèle se compose d'une structure HTML simple, avec un lien de feuille de style, un menu pour la navigation et quelques éléments et classes supplémentaires utilisés pour le style. Le truc intéressant se trouve entre les accolades qui signalent à Hugo d'intervenir et de faire sa magie, remplaçant tout ce qui se trouve entre les accolades avec le résultat d'évaluer une expression et potentiellement de manipuler le contexte.

Comme vous pouvez le deviner, {{.Title}} dans la balise de titre fait référence au titre de la page actuelle, tandis que {{.Site.Title}} fait référence à le titre de l'ensemble du site, défini dans la configuration Hugo. Une balise comme {{.Title}} dit simplement à Hugo de remplacer cette balise par le contenu du champ Title dans le contexte actuel.

Nous avons donc accédé à certains données appartenant à la page dans un modèle. D'où viennent ces données? C'est le sujet de la section suivante.

Contenu et Front Matter

Certaines des variables disponibles dans le contexte sont automatiquement fournies par Hugo. D'autres sont définis par nous, principalement dans les fichiers de contenu . Il existe également d'autres sources de données telles que les fichiers de configuration, les variables d'environnement, les fichiers de données et même les API. Dans cet article, nous nous concentrerons sur les fichiers de contenu comme source de données.

En général, un seul fichier de contenu représente une seule page. Un fichier de contenu typique comprend le contenu principal de cette page mais également des métadonnées sur la page, comme son titre ou la date de création. Hugo prend en charge plusieurs formats à la fois pour le contenu principal et pour les métadonnées . Dans cet article, nous utiliserons peut-être la combinaison la plus courante: le contenu est fourni sous forme de Markdown dans un fichier contenant les métadonnées en tant que frontal YAML.

En pratique, cela signifie que le fichier de contenu commence par une section délimitée par une ligne contenant trois tirets à chaque extrémité. Cette section constitue le premier sujet et ici les métadonnées sont définies à l'aide d'une syntaxe key: value (comme nous le verrons bientôt, YAML prend également en charge des structures de données plus élaborées). Le début est suivi du contenu réel, spécifié en utilisant le langage de balisage Markdown.

Rendons les choses plus concrètes en regardant un exemple. Voici un fichier de contenu très simple avec un champ d’introduction et un paragraphe de contenu:

 ---
title: Accueil
---

Page d'accueil du client Tower Git. Plus de 100 000 développeurs et designers utilisent Tower pour être plus productifs!

(Ce fichier réside dans content / _index.md dans notre projet, avec _index.md désignant le fichier de contenu d'une page comportant des sous-pages. Là encore, le Le référentiel GitHub indique clairement où le fichier est censé aller.)

Rendu en utilisant le modèle précédent, avec quelques styles et fichiers périphériques (tous trouvés sur GitHub), le résultat ressemble à ceci:

( Grand aperçu )

Vous vous demandez peut-être si les noms de champ dans l'avant-propos de notre fichier de contenu sont prédéterminés ou si nous pouvons ajouter un champ que nous aimons. La réponse est «les deux». Il existe une liste de champs prédéfinis mais nous pouvons également ajouter tout autre champ que nous pouvons trouver. Cependant, ces champs sont accessibles un peu différemment dans le modèle. Alors qu'un champ prédéfini comme title est accessible simplement comme .Title un champ personnalisé comme author est accessible en utilisant .Params.author .

(Pour une référence rapide sur les champs prédéfinis, ainsi que des choses comme les fonctions, les paramètres de fonction et les variables de page, voir notre propre aide-mémoire Hugo !)

The .Content ]utilisée pour accéder au contenu principal à partir du fichier de contenu de votre modèle, est spéciale. Hugo a une fonction «shortcode» vous permettant d'utiliser des balises supplémentaires dans votre contenu Markdown. Vous pouvez également définir le vôtre. Malheureusement, ces codes courts ne fonctionneront qu'avec la variable .Content – bien que vous puissiez exécuter n'importe quelle autre donnée via un filtre Markdown, cela ne gérera pas les codes courts dans le contenu.

Une note ici à propos de variables non définies: accéder à un champ prédéfini comme .Date fonctionne toujours, même si vous ne l'avez pas défini – une valeur vide sera renvoyée dans ce cas. L'accès à un champ personnalisé non défini, comme .Params.thisHasNotBeenSet fonctionne également, en renvoyant une valeur vide. Cependant, accéder à un champ de premier niveau non prédéfini comme .thisDoesNotExist empêchera la compilation du site.

Comme indiqué par .Params.author ainsi que . Hugo.version et .Site.title plus tôt, les invocations chaînées peuvent être utilisées pour accéder à un champ imbriqué dans une autre structure de données. Nous pouvons définir de telles structures dans notre premier plan. Prenons un exemple, où nous définissons une carte ou un dictionnaire, en spécifiant certaines propriétés pour une bannière sur la page dans notre fichier de contenu. Voici le contenu mis à jour / _index.md :

 ---
title: Accueil
bannière:
  headline: Essayez Tower gratuitement!
  subline: Téléchargez notre version d'essai pour essayer Tower pendant 30 jours
---

Page d'accueil du client Tower Git. Plus de 100 000 développeurs et designers utilisent Tower pour être plus productifs!

Maintenant, ajoutons une bannière à notre modèle, faisant référence aux données de la bannière en utilisant .Params de la manière décrite ci-dessus:


   ...
  
    ...
    
  

Voici à quoi ressemble notre site maintenant:

( Grand aperçu )

Très bien! Pour le moment, nous accédons aux champs du contexte par défaut sans aucun problème. Cependant, comme mentionné précédemment, ce contexte n'est pas fixe, mais peut changer. Voyons comment cela peut se produire.

Contrôle de flux

Les instructions de contrôle de flux sont une partie importante d'un langage de création de modèles, vous permettant de faire différentes choses en fonction de la valeur des variables, de parcourir les données en boucle, etc. Les modèles Hugo fournissent l'ensemble attendu de constructions y compris if / else pour la logique conditionnelle et range pour le bouclage. Ici, nous ne couvrirons pas le contrôle de flux dans Hugo en général (pour plus d'informations à ce sujet, voir la documentation ), mais nous nous concentrerons sur la façon dont ces déclarations affectent le contexte. Dans ce cas, les déclarations les plus intéressantes sont avec et range .

Commençons par par . Cette instruction vérifie si une expression a une valeur «non vide» et, si c'est le cas, relie le contexte pour faire référence à la valeur de cette expression . Une balise end indique le point où l'influence de l'instruction with s'arrête, et le contexte est rebondi à ce qu'il était auparavant. La documentation Hugo définit une valeur non vide comme false, 0, et tout tableau, tranche, carte ou chaîne de longueur nulle.

Actuellement, notre modèle de liste ne fait pas beaucoup de listage. Il pourrait être judicieux pour un modèle de liste de présenter en fait certaines de ses sous-pages d'une manière ou d'une autre. Cela nous donne une opportunité parfaite pour des exemples de nos instructions de contrôle de flux.

Peut-être que nous voulons afficher du contenu en vedette en haut de notre page. Cela peut être n'importe quel élément de contenu – un article de blog, un article d'aide ou une recette, par exemple. Pour le moment, imaginons que notre site d'exemple Tower comporte des pages mettant en évidence ses fonctionnalités, ses cas d'utilisation, une page d'aide, une page de blog et une page de "plateforme d'apprentissage". Ils se trouvent tous dans le répertoire content / . Nous configurons le contenu à présenter en ajoutant un champ dans le fichier de contenu de notre page d'accueil, content / _index.md . La page est désignée par son chemin, en supposant que le répertoire de contenu est root, comme ceci:

 ---
title: Accueil
bannière:
  headline: Essayez Tower gratuitement!
  subline: Téléchargez notre version d'essai pour essayer Tower pendant 30 jours sans limitation
en vedette: /features.md
...
---
...

Ensuite, notre modèle de liste doit être modifié pour afficher ce contenu. Hugo a une fonction de modèle, .GetPage qui nous permettra de faire référence à des objets de page autres que celui que nous sommes en train de rendre. Rappelez-vous comment le contexte, . était initialement lié à un objet représentant la page en cours de rendu? En utilisant .GetPage et avec nous pouvons relier temporairement le contexte à une autre page, en se référant aux champs de cette page lors de l'affichage de notre contenu vedette:



Ici, {{.Title}} {{.Summary}} et {{.Permalink}} entre le avec et les balises end font référence à ces champs dans la page sélectionnée et non au principal en cours de rendu.

En plus d'avoir un élément de contenu en vedette, listons quelques autres éléments de contenu plus bas. Tout comme le contenu présenté, les éléments de contenu répertoriés seront définis dans content / _index.md le fichier de contenu de notre page d'accueil. Nous allons ajouter une liste de chemins de contenu à notre introduction comme ceci (dans ce cas, en spécifiant également le titre de la section):

 ---
...
listing_headline: Pages en vedette
référencement:
  - /help.md
  - /use-cases.md
  - /blog/_index.md
  - /learn.md
---

La raison pour laquelle la page de blog a son propre répertoire et un fichier _index.md est que le blog aura des sous-pages de ses propres articles de blog.

Pour afficher cette liste dans notre modèle, nous utiliserons la plage . Sans surprise, cette instruction bouclera sur une liste, mais elle reliera également le contexte à chaque élément de la liste à son tour. Ceci est très pratique pour notre liste de contenu.

Notez que, du point de vue d'Hugo, «lister» ne contient que quelques chaînes. Pour chaque itération de la boucle «range», le contexte sera lié à l'une de ces chaînes . Pour accéder à l'objet de page réel, nous fournissons sa chaîne de chemin (maintenant la valeur de . ) comme argument de .GetPage . Ensuite, nous utiliserons à nouveau l'instruction with pour relier le contexte à l'objet de page répertorié plutôt qu'à sa chaîne de chemin. Désormais, il est facile d'afficher successivement le contenu de chaque page répertoriée:


{{.Params.listing_headline}}

{{range .Params.listing}} {{avec $ .GetPage. }} {{end}} {{ finir }}

Voici à quoi ressemble le site à ce stade:

( Grand aperçu )

Mais attendez, il y a quelque chose de bizarre dans le modèle ci-dessus – plutôt que d'appeler .GetPage nous appelons $. GetPage . Pouvez-vous deviner pourquoi .GetPage ne fonctionnerait pas?

La notation .GetPage indique que la fonction GetPage est une méthode du contexte actuel. En effet, dans le contexte par défaut, il existe une telle méthode, mais nous venons d’aller de l’avant et a changé le contexte ! Lorsque nous appelons .GetPage le contexte est lié à une chaîne, qui n'a pas cette méthode. La manière de contourner ce problème est le sujet de la section suivante.

Le contexte global

Comme vu ci-dessus, il y a des situations où le contexte a été changé, mais nous aimerions quand même accéder au contexte d'origine. Ici, c'est parce que nous voulons appeler une méthode existant dans le contexte d'origine – une autre situation courante est lorsque nous voulons accéder à une propriété de la page principale en cours de rendu. Pas de problème, il existe un moyen simple de le faire.

Dans un modèle Hugo, $ connu sous le nom de contexte global se réfère à la valeur originale du contexte – le contexte tel qu'il était lorsque le traitement du modèle a commencé. Dans la section précédente, elle était utilisée pour appeler la méthode .GetPage même si nous avions rebondi le contexte sur une chaîne. Maintenant, nous allons également l'utiliser pour accéder à un champ de la page en cours de rendu.

Au début de cet article, j'ai mentionné que notre modèle de liste est réutilisable. Jusqu'à présent, nous ne l'avons utilisé que pour la page d'accueil, en affichant un fichier de contenu situé à content / _index.md . Dans l'exemple de référentiel, il existe un autre fichier de contenu qui sera rendu à l'aide de ce modèle: content / blog / _index.md . Ceci est une page d'index pour le blog, et tout comme la page d'accueil, il montre un élément de contenu en vedette et en énumère quelques autres – des articles de blog, dans ce cas.

Maintenant, disons que nous voulons afficher le contenu répertorié légèrement différemment sur la page d'accueil – pas assez pour justifier un modèle séparé, mais quelque chose que nous pouvons faire avec une instruction conditionnelle dans le modèle lui-même. Par exemple, nous afficherons le contenu répertorié dans une grille à deux colonnes, par opposition à une liste à une seule colonne, si nous détectons que nous sommes en train de rendre la page d'accueil.

Hugo est livré avec une méthode de page, .IsHome qui fournit exactement les fonctionnalités dont nous avons besoin. Nous allons gérer le changement réel de présentation en ajoutant une classe aux éléments de contenu individuels lorsque nous nous trouvons sur la page d'accueil, permettant à notre fichier CSS de faire le reste.

Nous pourrions, bien sûr, ajouter la classe à l'élément body ou à un élément contenant à la place, mais cela ne permettrait pas une aussi bonne démonstration du contexte global. Au moment où nous écrivons le code HTML du contenu répertorié, . fait référence à la page répertoriée mais IsHome doit être appelé sur la page principale en cours de rendu. Le contexte global vient à notre secours:

{{.Params.listing_headline}}

{{range .Params.listing}} {{avec $ .GetPage. }}

{{.Title}}

{{ .Résumé }}

En savoir plus →

{{end}} {{ finir }}

L'index du blog ressemble exactement à notre page d'accueil, mais avec un contenu différent:

( Grand aperçu )

… mais notre page d'accueil affiche désormais son contenu en vedette dans une grille: [19659102] ( Grand aperçu )

Modèles partiels

Lors de la création d'un vrai site Web, il devient rapidement utile de diviser vos modèles en parties. Vous souhaitez peut-être réutiliser une partie particulière d'un modèle, ou peut-être voulez-vous simplement diviser un modèle énorme et peu maniable en éléments cohérents. Pour cela, les modèles partiels d'Hugo sont la voie à suivre.

D'un point de vue contextuel, l'important ici est que lorsque nous incluons un modèle partiel, nous lui passons explicitement le contexte que nous voulons créer à sa disposition. Une pratique courante est de passer dans le contexte tel qu'il est lorsque le partiel est inclus, comme ceci: {{partial "my / partial.html". }} . Si le point ici fait référence à la page en cours de rendu, c'est ce qui sera passé au partiel; si le contexte a été rebondi vers autre chose, c’est ce qui est transmis.

Vous pouvez, bien sûr, relier le contexte dans des modèles partiels comme dans les modèles normaux. Dans ce cas, le contexte global, $ fait référence au contexte d'origine passé au partiel, et non à la page principale en cours de rendu (sauf si c'est ce qui a été passé).

Si nous voulons un modèle partiel pour avoir accès à une donnée particulière, nous pourrions rencontrer des problèmes si nous ne transmettons cela qu'au partiel. Rappelez-vous notre problème plus tôt avec l'accès aux méthodes de page après la reliure du contexte? Il en va de même pour partiels mais dans ce cas, le contexte global ne peut pas nous aider – si nous avons passé, par exemple, une chaîne à un modèle partiel, le contexte global dans le partiel se référera à cette chaîne, et nous ne pourrons pas appeler les méthodes définies dans le contexte de la page.

La solution à ce problème réside dans en passant plus d'une donnée en incluant le partiel. Cependant, nous ne sommes autorisés à fournir qu’un seul argument à l’appel partiel. Nous pouvons, cependant, faire de cet argument un type de données compund, communément une carte (connue sous le nom de dictionnaire ou de hachage dans d'autres langages de programmation).

Dans cette carte, nous pouvons, par exemple, avoir une Page défini sur l'objet de page actuel, ainsi que d'autres clés pour toute donnée personnalisée à transmettre. L'objet de page sera alors disponible sous la forme .Page dans le partiel, et les autres valeurs de la carte sont accédé de la même manière. Une carte est créée en utilisant la fonction de modèle dict qui prend un nombre pair d'arguments, interprété alternativement comme une clé, sa valeur, une clé, sa valeur, etc. [19659006] Dans notre exemple de modèle, déplaçons le code de notre contenu présenté et répertorié dans des partiels. Pour le contenu en vedette, il suffit de transmettre l'objet de page en vedette. Le contenu répertorié, cependant, doit accéder à la méthode .IsHome en plus du contenu répertorié particulier en cours de rendu. Comme mentionné précédemment, si .IsHome est également disponible sur l'objet de page de la page répertoriée, cela ne nous donnera pas la bonne réponse – nous voulons savoir si la page principale en cours de rendu est la page d'accueil.

Nous pourrions plutôt passer un ensemble booléen au résultat de l'appel .IsHome mais peut-être que le partiel aura besoin d'accéder à d'autres méthodes de page à l'avenir – allons-y avec en passant l'objet page principale ainsi que l'objet page répertorié. Dans notre exemple, la page principale se trouve dans $ et la page répertoriée dans . . Ainsi, dans la carte passée au partiel listé la clé Page obtient la valeur $ tandis que la clé «Listed» obtient la valeur . . Voici le modèle principal mis à jour:


  
  
  

{{.Title}}

{{ .Teneur }}

{{.Params.listing_headline}}

{{range .Params.listing}} {{avec $ .GetPage. }} {{partial "partials / lists.html" (dict "Page" $ "Listed".)}} {{ finir }} {{ finir }}

Le contenu de notre partiel "vedette" ne change pas par rapport à l'époque où il faisait partie du modèle de liste:


Notre partial pour le contenu répertorié, cependant, reflète le fait que l'objet de page d'origine se trouve maintenant dans .Page tandis que l'élément de contenu répertorié se trouve dans .Listed : [19659124] {{.Listed.Title}}

{{.Listed.Summary}}

En savoir plus →

Hugo fournit également la fonctionnalité de modèle de base qui vous permet d'étendre un modèle de base commun au lieu d'inclure des sous-modèles. Dans ce cas, le contexte fonctionne de la même manière: lors de l'extension d'un modèle de base, vous fournissez les données qui constitueront le contexte d'origine dans ce modèle.

Variables personnalisées

Il est également possible d'attribuer et de réaffecter vos propres variables personnalisées dans un modèle Hugo. Ceux-ci seront disponibles dans le modèle dans lequel ils sont déclarés, mais ne seront pas intégrés à des partiels ou des modèles de base à moins que nous ne les transmettions explicitement. Une variable personnalisée déclarée à l'intérieur d'un «bloc» comme celle spécifiée par une instruction if ne sera disponible qu'à l'intérieur de ce bloc – si nous voulons y faire référence en dehors du bloc, nous avons besoin pour le déclarer en dehors du bloc, puis le modifier à l'intérieur du bloc si nécessaire.

Les variables personnalisées ont des noms préfixés par un signe dollar ( $ ). Pour déclarer une variable et lui donner une valeur en même temps, utilisez l'opérateur : = . Les affectations ultérieures à la variable utilisent l'opérateur = (sans deux-points). Une variable ne peut pas être affectée avant d'être déclarée, et elle ne peut pas être déclarée sans lui donner une valeur.

Un cas d'utilisation des variables personnalisées simplifie les longs appels de fonction en attribuant un résultat intermédiaire à une variable nommée de manière appropriée. Par exemple, nous pourrions affecter l'objet de page en vedette à une variable nommée $ vedette et ensuite fournir cette variable à l'instruction avec . Nous pourrions également placer les données à fournir au partiel «listé» dans une variable et les donner à l'appel partiel.

Voici à quoi ressemblerait notre modèle avec ces changements:


...

{{ .Params.listing_headline }}

{{ range .Params.listing }} {{ with $.GetPage . }} {{ $context := (dict "Page" $ "Listed" .) }} {{ partial "partials/listed.html" $context }} {{ end }} {{ end }}

Based on my experience with Hugo, I’d recommend using custom variables liberally as soon as you’re trying to implement some more involved logic in a template. While it’s natural to try to keep your code concise, this may easily make things less clear than they could be, confusing you and others.

Instead, use descriptively named variables for each step and don’t worry about using two lines (or three, or four, etc.) where one would do.

.Scratch

Finally, let’s cover the .Scratch mechanism. In earlier versions of Hugo, custom variables could only be assigned to once; it was not possible to redefine a custom variable. Nowadays, custom variables can be redefined, which makes .Scratch less important, though it still has its uses.

In short, .Scratch is a scratch area allowing you to set and modify your own variableslike custom variables. Unlike custom variables, .Scratch belongs to the page context, so passing that context on to a partial, for example, will bring the scratch variables along with it automatically.

You can set and retrieve variables on .Scratch by calling its methods Set and Get. There are more methods than thesefor example for setting and updating compound data types, but these two ones will suffice for our needs here. Set takes two parameters: the key and the value for the data you want to set. Get only takes one: the key for the data you want to retrieve.

Earlier, we used dict to create a map data structure to pass multiple pieces of data to a partial. This was done so that the partial for a listed page would have access to both the original page context and the particular listed page object. Using .Scratch is not necessarily a better or worse way to do this — whichever is preferrable may depend on the situation.

Let’s see what our list template would look like using .Scratch instead of dict to pass data to the partial. We call $.Scratch.Get (again using the global context) to set the scratch variable “listed” to . — in this case, the listed page object. Then we pass in just the page object, $to the partial. The scratch variables will follow along automatically.

{{ .Params.listing_headline }}

{{ range .Params.listing }} {{ with $.GetPage . }} {{ $.Scratch.Set "listed" . }} {{ partial "partials/listed.html" $ }} {{ end }} {{ end }}

This would require some modification to the listed.html partial as well — the original page context is now available as “the dot” while the listed page is retrieved from the .Scratch object. We’ll use a custom variable to simplify access to the listed page:


  {{ $listed := .Scratch.Get "listed" }}
  

{{ $listed.Title }}

{{ $listed.Summary }}

Read more →

One argument for doing things this way is consistency. Using .Scratchyou can make it a habit to always pass in the current page object to any partial, adding any extra data as scratch variables. Then, whenever you write or edit your partials, you know that . is a page object. Of course, you can establish a convention for yourself using a passed-in map as well: always sending along the page object as .Pagefor example.

Conclusion

When it comes to context and data, a static site generator brings both benefits and limitations. On one hand, an operation that is too inefficient when run for every page visit may be perfectly good when run only once as the page is compiled. On the other hand, it may surprise you how often it would be useful to have access to some part of the network request even on a predominantly static site.

To handle query string parametersfor example, on a static site, you’d have to resort to JavaScript or some proprietary solution like Netlify’s redirects. The point here is that while the jump from a dynamic to a static site is simple in theory, it does take a shift in mindset. In the beginning, it’s easy to fall back on your old habits, but practice will make perfect.

With that, we conclude our look at data management in the Hugo static site generator. 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 sheetin a package also featuring a host of other cheat sheets on everything from Git to the Visual Studio Code editor.

Further Reading

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

Smashing Editorial" width="35" height="46" loading="lazy" decoding="async(vf, il)

Le meilleur outil 2023 pour ta croissance Instagram !



Source link

Partager :

Revenir vers le haut