Blogsystem mit Nuxtjs

Es war an der Zeit meine neue Webseite aufzubauen und es sollte unbedingt mit NuxtJS umgesetzt werden. Ich hatte bereits einige positive Tests mit NuxtJS gemacht und festgestellt, dass damit die Vuejs-Entwicklung produktiver geworden ist.

Was wollen wir erreichen

Für unser Blogsystem haben wir die Artikel als Markdowndateien vorliegen. Die Erstellung der Artikelseite und der Übersichtsseite sollen dynamisch erfolgen, d.h. mit einer neuen Markdowndatei soll auch automatisch der neue Artikel erscheinen. Unsere Artikel sollen Metadaten, wie Titel und Beschreibung, enthalten um die Artikelseiten entsprechen für SEO aufzubereiten.

Die Seiten sollen mit Nuxtjs statisch generiert werden. Das Hosting der Seiten vereinfacht sich dadurch extrem und die Search-Engine-Crawler können alle Artikel für ihren Suchindex indizieren.

Auf geht's!

Projekt erstellen und starten

Alle guten Dinge starten im Terminal. Mit folgendem Kommando erstellen wir unsere NuxtJS-Applikation.

npx create-nuxt-app nuxt-blog

Nach dem Aufruf werden einige Eigenschaften zum Projekt abgefragt. Ihr könnt alle Vorgaben übernehmen, achtet aber darauf, dass als Rendering Mode unbedingt universal gewählt wird. Damit ist sichergestellt, dass bei der Seitengenerierung für jede Seite eine HTML-Datei erzeugt wird.

Die Applikation wurde im neuen Verzeichnis nuxt-blog erstellt und dort kann die Anwendung mit folgendem Kommando gestartet werden.

npm run dev

Markdowndateien für Artikel erstellen

Unsere Markdowndateien sollen im neuen Verzeichnis post hinterlegt werden. Um sie besser zu strukturieren hinterlegen wir sie in Ordnern mit der entsprechenden Jahreszahl (/post/[year]/[title].md).

Als Beispiel erstellen wir die Datei /post/2019/nuxtjs-blog.md

---
title: Blogsystem mit Nuxtjs 
---

# Blogsystem mit Nuxtjs

Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel
Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel

## Zweite Überschrift

Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel
Dies ist ein Beispiel Dies ist ein Beispiel Dies ist ein Beispiel

``` html
<h1>Beispiel für Quelltext</h1>
<p>Dies ist ein Beispiel Dies ist ein Beispiel</p>
```

Diese Markdowndatei enthält am Anfang einen Metadatenteil (Frontmatter) mit dem Titel des Artikels. Auf diese Daten können wir dann später zugreifen. Und am Ende gibt es ein Quellcodebeispiel, da wir später noch Syntax-Highlighting hinzufügen wollen.

Markdowndatei lesen und darstellen

Wir benötigen noch einen Markdown-Reader. Den installieren wir mit folgendem Kommando:

npm i -D frontmatter-markdown-loader

Und konfigurieren ihn in der nuxt.config.js.

build: {
  /*
   ** You can extend webpack config here
   */
  extend (config, ctx) {
    config.module.rules.push({
      test: /\.md$/,
      loader: 'frontmatter-markdown-loader'
    });
  }
}

Der Markdown-Reader ist ein Webpack-Loader, d.h. wir können unsere Artikel einfach mit require oder import laden.

Und jetzt können wir die Seite erstellen, die uns den Blogartikel darstellt. Der Artikel soll dabei unter dem URL-Schema /[year]/[title] erreichbar sein. Also erstellen wir die Seite in /pages/_year/_title.vue.

<template>
  <div>
    <nuxt-link to="/">Zurück</nuxt-link>
    <p v-html="post.html"/>
  </div>
</template>

<script>
  export default {
    name: "Blogpost",
    async asyncData({ params }) {
      const post = await import(`../../post/${params.year}/${params.title}.md`);
      return { post }
    }
  }
</script>

Unser Artikel wird in der Methode asyncData geladen. Durch die Verwendung dieser Methode wird es dann auch später möglich sein, die Seiten statisch zu generieren.

Über params können wir auf die Variablen die durch die aufgerufene URL entstehen (dynamisches Routing) zugreifen. Außerdem benötigen wir eine async/await-Struktur, um die Promise von import aufzulösen.

Das Post-Objekt enthält nun unseren Artikel im HTML-Format.

Wir können nun unseren Artikel im Browser unter /2019/nuxtjs-blog aufrufen.

Liste aller Posts für die Startseite generieren

Die Startseite /pages/index.vue können wir durch folgenden Code ersetzen:

<template>
  <div>
    <h1>Liste der Blogbeiträge</h1>
    <ul>
      <li v-for="post in posts">
        <nuxt-link :to="post.link">{{ post.attributes.title }}</nuxt-link>
      </li>
    </ul>
  </div>
</template>

<script>

export default {
  asyncData() {
    const resolve = require.context('~/post', true, /\.md$/);
    const posts = resolve.keys().map((key) => {
      return {
        link: key.slice(0, -3),
        attributes: resolve(key).attributes
      }
    });
    return { posts }
  }
}
</script>

Um nun eine Liste aller Markdowndateien zu erhalten müssen wir auf require.context zurückgreifen. Damit kann man sich alle angeforderten Dateien als Pfad zurückgeben lassen.

Um aus dem Pfad ein Link zu erzeugen müssen wir die Endung .md mit key.slice(0, -3) abschneiden.

Durch resolve(key) erhalten wir das Post-Objekt und können über attributes auf die Metadaten zugreifen.

Die Liste der Artikel erscheint nun im Browser auf der Startseite.

Statische Seiten generieren

Um letztendlich die statischen Seiten generieren zu können, benötigt Nuxtjs noch die Information für welche URLs dies geschehen soll. Das ist notwendig, da wir dynamisches Routing verwenden (zur Erinnerung /_year/_title.vue).

Dazu erweitern wir die nuxt.config.js um die Definition der Routen.

import glob from "glob";

...

generate: {
  routes: function() {
    return glob
      .sync('**/*.md', { cwd: 'post' })
      .map(filepath => `/${filepath.slice(0, -3)}`)
    }
  }
}

Der erste Gedanke wäre wieder auf require.context zurückzugreifen, aber die Webpack-Umgebung steht uns in der Config-Datei nicht zur Verfügung. Stattdessen können wir direkt über das Dateisystem (glob Bibliothek für Wildcard-Suche) unsere Markdowndateien heraussuchen und die Liste der URLs erstellen.

Jetzt können wir die statischen Seiten erzeugen:

npm run generate

Anschließend liegen die erzeugten Dateien im Ordner dist. Um sich die Seiten anzuschauen empfiehlt es sich einen simplen HTTP-Server zu verwenden.

npm install -g live-server 

Anschließend können Sie die Dateien aus dist über den HTTP-Server zur Verfügung stellen. Es öffnet sich automatisch der Browser mit unserer Startseite.

live-server dist 

Syntax Highlighting

Zum Abschluss wollen wir unsere Codeblöcke in den Artikeln mit Syntax-Highlighting versehen. Dazu verwenden wir folgendes Paket:

npm install highlight.js --save

HighlightJS ist kein spezielles Vuejs-Paket, deshalb wenden wir es direkt auf den erzeugen HTML-Code in der Methode mountedan.

import hljs from 'highlight.js'
import 'highlight.js/styles/darcula.css'

...

export default {
  ...
  mounted () {
    document.querySelectorAll('pre code').forEach((block) => {
      hljs.highlightBlock(block)
    })
  }
}

Das verwendete Theme (Farbschema) wurde hier per import hinzugeladen.

Quellcode

Das ganze Projekt findet ihr auch auf GitHub github.com/urbansky/nuxtjs-blog


Anregungen und Hinweise sind herzlich willkommen, schreibt mir unter @code_hx


Büro

ImpactHub Dresden
Bayrische Straße 8
01069 Dresden

Post

Dr.-Ing. Stefan Urbansky
HxCode
Dresdner Landstr. 30e
01728 Bannewitz