/ NUXT, MARKDOWN, VUEJS

Create a Nuxt markdown based blog

Nuxt est un framework basé sur Vue.js qui permet de créer assez facilement des sites web modernes.

Nuxt.js presets all the configuration needed to make your development of a Vue.js application enjoyable.

Nuxt est un framework Jamstack parmi d’autres. D’ailleurs si le sujet vous intéresse je vous invite à visiter le site jamstack.org qui traite le thème.

Objectifs

Dans ce billet nous allons utiliser le framework Nuxt. Une connaissance de vuejs n’est pas absolument nécessaire mais peut aider à mieux saisir certaines parties de ce billet.

L’objectif est de créer un blog simple, qui aura comme source de données des fichiers markdown.

Pré-requis

Pour réaliser ce projet nous avons besoin de :

Dans ce billet j’utilise yarn mais les commandes sont équivalentes en utlisant npm.

Etape 1 : Création du projet

Créer le projet Nuxt en tapant la commande

yarn create nuxt-app nuxt-markdown-blog

Cette commande est interractive et permet d’initialiser et d’ajouter des composants optionnels au projet. Nous allons choisir les paramètres par défaut sauf pour l’étape UI framework ou nous allons sélectionner bulma, Une petite lib pure CSS pour avoir de jolies interfaces web.

Maintenant que tout est initialisé, on peut lancer notre appli en local

cd nuxt-markdown-blog
yarn run dev

Par dafut, le serveur local de dev est accessible à l’adresse http://localhost:3000

Etape 2: Ajout frontmatter-markdown-loader

Le frontmatter est une zone située en début du fichier markdown. Elle n’est pas visible lors du rendu html et permet de rajouter des métadonnées au fichier.

Le package frontmatter-markdown-loader qui s’intègre à webpack, déjà présent dans tout projet nuxt, va en gros permettre de charger des fichiers markdown et lire les attributs de la zone frontmatter.

markdown frontmatter

  • Installation
yarn add frontmatter-markdown-loader --dev
  • Configuration

Il faut pour cela modifier le fichier nuxt.config.js situé à la racine du projet et rajouter la configuration suivante :

import path from 'path'

export default {

  //... other configurations 

  build: {
    extend(config, ctx) {
      config.module.rules.push({
        test: /\.md$/,
        loader: 'frontmatter-markdown-loader',
        include: path.resolve(__dirname, 'content')
      })
    }
  }
}

Etape 3: Création des fichiers de contenu markdown

Les pages articles de notre blog seront construites à partir du contenu de fichiers markdown. :

Commençons par créer un fichier article-1.md que nous allons sauvegarder sous : content/posts/.

mkdir -p content/posts
touch content/posts/article-1.md

Dans le fichier article-1.md qui vient d’être créé, mettre du contenu markdown avec un frontmatter.

Exemple:

---
title: A propos de lorem Ipsum
date: 2019-05-10
cover: https://picsum.photos/1200/800?grayscale&random=1
---

## Qu'est-ce que le Lorem Ipsum ?

Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. 
Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500..

J’ai choisi de positionner 3 métadonnées dans le front-matter :

  1. title : Qui contiendra le titre de l’article
  2. date : La date de publication
  3. cover : Une image de couverture

Libre à vous d’en rajouter d’autres selon vos besoins.

Maintenant qu’on a du contenu, il va falloir demander à Nuxt de l’afficher dans une page. Dans un premier temps, on peut le faire “basiquement” en créant un composant vuejs dans le dossiers pages du projet.

Exemple : article-1.vue

<template>
  <div>
    <section class="hero is-medium is-dark is-bold" :style="{ background: 'url('+ attributes.cover +')' }">
      <div class="hero-body">
        <h1 class="title is-size-2">
          {{ attributes.title }}
        </h1>
        <h2 v-if="attributes.date" class="subtitle">
          {{ new Date(attributes.date).toLocaleDateString() }}
        </h2>
      </div>
    </section>
    <div class="container">
      <div class="content has-text-justified" v-html="content"/>
    </div>
  </div>
</template>

<script>
export default {
  async asyncData ({ params }) {
    const res = await import('~/content/posts/article-1.md')
    return {
      attributes: res.attributes,
      content: res.html
    }
  }
}
</script>

Explication

Chaque page nuxt est un composant vuejs composé entre autre de :

  • Une partie template html qui modélise notre page
  • Une partie script qui va récupérer les données injectées dans le template

Dans notre cas le script utilise le webpack loader nuxt-markdown-blog-frontmatter pour charger le fichier article-1.md via la commande import('~/content/posts/article-1.md').

On utilise le résultat de cet import pour alimenter le vue-model du composant. Ce dernier permet au template d’accéder au propriétés (title, date …) que nous affichons dans la page.

Cette approche marche bien pour des pages uniques. Pour les multiples posts d’un blog, il est plus efficace de créer une vue dynamique qui va s’ocupper de charger le contenu mardown à partir d’un paramètre du routage.

Etape 4: Création de la vue dynamique des articles

Nous allons utiliser les capacités du routage automatique de Nuxt pour créer une vue dynmique et afficher nos articles à partir de paramètres d’url. Je vais donc modifier le composant précédant article-1.vue pour le rendre dynamique et le renommer en _slug_.vue que je déplace dans un dossier posts à l’emplacement pages/posts/.

Ci-dessous, un apperçu de l’organisation des fichiers et dossiers :

├── content
│   └── posts
│       └── article-1.md
├── layouts
├── middleware
├── pages
│   │── posts
│   │   └── _slug.vue
│   ├── index.vue

Suite aux modifications, voici le contenu de la vue dynamique _slug.vue qui gère l’affichage de nos articles.

<template>
  <div :key="$route.params.slug">
    <section class="hero is-medium is-dark is-bold" :style="{ background: 'url('+ attributes.cover +')' }">
      <div class="hero-body">
        <h1 class="title is-size-2">
          {{ attributes.title }}
        </h1>
        <h2 v-if="attributes.date" class="subtitle">
          {{ new Date(attributes.date).toLocaleDateString() }}
        </h2>
      </div>
    </section>
    <div class="container">
      <div class="content has-text-justified" v-html="content"/>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.hero-body {
  display: inline-block;
  background: rgba(0, 0, 0, 0.3);
  margin: 0 3% 0 3%;
}
</style>

<script>
export default {
  async asyncData ({ params }) {
    const res = await import(`~/content/posts/${params.slug}.md`)
    return {
      attributes: res.attributes,
      content: res.html
    }
  }
}
</script>

On peut relancer le serveur avec yarn run dev et visiter la page de l’article à l’adresse http://localhost:3000/posts/article-1.

Ce qui change c’est la façon de charger les articles qui dépend maintenant d’un paramètre : ${params.slug}.

Le slug est automatiquement renseigné par le routeur sur la base le l’url de l’article qu’on va consulter. Par exemple, pour l’article à l’adresse : http://localhost/posts/article-1 le paramètre slug sera article-1.

On peut ajouter rapidement un article en faisant une copie du fichier content/posts/article-1.md qu’on nomme article-2.md. Ce nouvel artice est immédiatement disponible à l’adresse http://localhost:3000/posts/article-2 (Merci au live reload en dev).

Etape 5: Génération

Jusque là, nous avons travaillé en local avec le serveur de dev intégré à Nuxt. Pour aller plus loin et déployer notre site sur un FTP ou autre, il faut pouvoir faire la génération, une étape incontournable de la JamStack.

Un dernier point important

La commande nuxt generate qui permet de génerer la version statique du site ignore les pages dynamiques (type _slug.vue). Il va donc falloire indiquer au moteur de génération des pages, les routes qu’il doit générer correspondant aux fichiers de contenu markdown que nous avons créé. Ceci se fait assez simplement dans le fichier de configuration de Nuxt nuxt.config.js.

On va rajouter sous export default, si elle n’existe pas déjà, une propriété generate commme suit :

export default {
  //...

  generate: {
    routes() {
      return [
        '/posts/article-1',
        '/posts/article-2'
        
        //... other articles
      ]
    }
  }
}

Pour générer notre site afin de le déployer sur un herbegement static tel que les github pages

Il suffit de lancer la commande :

yarn run generate

Le site généré se trouve par défaut dans le dossier dist à la racine du projet. Ci-dessous, un apperçu de l’arborescence.

├── 200.html
├── favicon.ico
├── index.html
├── _nuxt
│   ├── 01a440daa4cf35731c9a.js
│   ├── 20e024130a1ae9e4ab54.js
│   ├── 25b801ba8abc58a520d7.js
│   ├── 39bbb41e169e6c27f4d3.js
│   ├── 4117f9cb90435abb1f20.js
│   ├── 7ba37a5cee133f9cb0b6.js
│   ├── b323de225f1a5ae4a7a9.js
│   ├── b5ac13780f283e98ec8a.js
│   ├── d28d76ded7db015e89df.js
│   └── LICENSES
├── posts
│   ├── article-1
│   │   └── index.html
│   ├── article-2
│   │   └── index.html
│   └── index.html
└── README.md

Voilà ! Vous savez maintenant créer un site static Nuxt alimenté avec du contenu markdown !