In diesem Artikel werden wir Next.js verwenden, um ein statisches Blog-Framework mit einem Design und einer Struktur zu erstellen, die von Jekyll inspiriert sind. Ich war schon immer ein großer Fan davon, wie Jekyll es Anfängern erleichtert, einen Blog einzurichten, und gleichzeitig auch fortgeschrittenen Benutzern ein hohes Maß an Kontrolle über jeden Aspekt des Blogs bietet.
Mit der Einführung von Next.js in den letzten Jahren, kombiniert mit der Popularität von React, gibt es einen neuen Weg, statische Blogs zu erkunden. Next.js macht es super einfach, statische Websites zu erstellen, die auf dem Dateisystem selbst basieren und wenig bis gar keine Konfiguration erfordern.
Die Verzeichnisstruktur eines typischen, minimalistischen Jekyll-Blogs sieht so aus
.
├─── _posts/ ...blog posts in markdown
├─── _layouts/ ...layouts for different pages
├─── _includes/ ...re-usable components
├─── index.md ...homepage
└─── config.yml ...blog config
Die Idee ist, unser Framework so weit wie möglich an dieser Verzeichnisstruktur auszurichten, damit es einfacher wird, einen Blog von Jekyll zu migrieren, indem einfach die Beiträge und Konfigurationen des Blogs wiederverwendet werden.
Für diejenigen, die Jekyll nicht kennen: Es ist ein statischer Seitengenerator, der Ihren einfachen Text in statische Websites und Blogs umwandeln kann. Beziehen Sie sich auf die Schnellstartanleitung, um mit Jekyll loszulegen.
Dieser Artikel setzt auch voraus, dass Sie über grundlegende Kenntnisse in React verfügen. Wenn nicht, ist die Startseite von React ein guter Ort, um zu beginnen.
Installation
Next.js wird von React angetrieben und in Node.js geschrieben. Wir müssen also zuerst npm installieren, bevor wir next, react und react-dom zum Projekt hinzufügen.
mkdir nextjs-blog && cd $_
npm init -y
npm install next react react-dom --save
Um Next.js-Skripte über die Befehlszeile auszuführen, müssen wir den Befehl next in den Abschnitt scripts unserer package.json einfügen.
"scripts": {
"dev": "next"
}
Wir können nun zum ersten Mal npm run dev in der Befehlszeile ausführen. Mal sehen, was passiert.
$ npm run dev
> [email protected] dev /~user/nextjs-blog
> next
ready - started server on https://:3000
Error: > Couldn't find a `pages` directory. Please create one under the project root
Der Compiler beschwert sich über ein fehlendes Verzeichnis pages im Stammverzeichnis des Projekts. Das Konzept der Seiten lernen wir im nächsten Abschnitt.
Konzept der Seiten
Next.js ist um das Konzept der Seiten aufgebaut. Jede Seite ist eine React-Komponente, die vom Typ .js oder .jsx sein kann und basierend auf dem Dateinamen einer Route zugeordnet wird. Zum Beispiel
File Route
---- -----
/pages/about.js /about
/pages/projects/work1.js /projects/work1
/pages/index.js /
Erstellen wir das Verzeichnis pages im Stammverzeichnis des Projekts und füllen unsere erste Seite, index.js, mit einer einfachen React-Komponente.
// pages/index.js
export default function Blog() {
return <div>Welcome to the Next.js blog</div>
}
Führen Sie npm run dev erneut aus, um den Server zu starten, und navigieren Sie zu https://:3000 im Browser, um Ihren Blog zum ersten Mal anzuzeigen.

Out of the box erhalten wir
- Hot Reloading, damit wir den Browser nicht bei jeder Codeänderung aktualisieren müssen.
- Statische Generierung aller Seiten im Verzeichnis
/pages/**. - Statisches Servieren von Dateien für Assets im Verzeichnis
/public/**. - 404-Fehlerseite.
Navigieren Sie zu einem zufälligen Pfad auf localhost, um die 404-Seite in Aktion zu sehen. Wenn Sie eine benutzerdefinierte 404-Seite benötigen, finden Sie in der Next.js-Dokumentation großartige Informationen.

Dynamische Seiten
Seiten mit statischen Routen sind nützlich für die Erstellung der Homepage, der Über-uns-Seite usw. Um jedoch alle unsere Beiträge dynamisch zu erstellen, werden wir die dynamische Routenfunktion von Next.js verwenden. Zum Beispiel
File Route
---- -----
/pages/posts/[slug].js /posts/1
/posts/abc
/posts/hello-world
Jede Route, wie /posts/1, /posts/abc usw., wird von /posts/[slug].js abgeglichen, und der Slug-Parameter wird als Query-Parameter an die Seite gesendet. Dies ist besonders nützlich für unsere Blogbeiträge, da wir nicht für jeden Beitrag eine eigene Datei erstellen möchten; stattdessen könnten wir den Slug dynamisch übergeben, um den entsprechenden Beitrag zu rendern.
Anatomie eines Blogs
Nachdem wir nun die grundlegenden Bausteine von Next.js verstanden haben, wollen wir die Anatomie unseres Blogs definieren.
.
├─ api
│ └─ index.js # fetch posts, load configs, parse .md files etc
├─ _includes
│ ├─ footer.js # footer component
│ └─ header.js # header component
├─ _layouts
│ ├─ default.js # default layout for static pages like index, about
│ └─ post.js # post layout inherts from the default layout
├─ pages
│ ├─ index.js # homepage
| └─ posts # posts will be available on the route /posts/
| └─ [slug].js # dynamic page to build posts
└─ _posts
├─ welcome-to-nextjs.md
└─ style-guide-101.md
Blog-API
Ein grundlegendes Blog-Framework benötigt zwei API-Funktionen:
- Eine Funktion zum Abrufen der Metadaten aller Beiträge im Verzeichnis
_posts - Eine Funktion zum Abrufen eines einzelnen Beitrags für einen gegebenen
slugmit dem vollständigen HTML und den Metadaten
Optional möchten wir auch, dass die gesamte Website-Konfiguration, die in config.yml definiert ist, in allen Komponenten verfügbar ist. Daher benötigen wir eine Funktion, die die YAML-Konfiguration in ein natives Objekt parst.
Da wir uns mit vielen Nicht-JavaScript-Dateien wie Markdown (.md), YAML (.yml) usw. beschäftigen werden, verwenden wir die raw-loader-Bibliothek, um solche Dateien als Strings zu laden, um die Verarbeitung zu erleichtern.
npm install raw-loader --save-dev
Als Nächstes müssen wir Next.js mitteilen, dass es raw-loader verwenden soll, wenn wir .md- und .yml-Dateiformate importieren, indem wir eine Datei next.config.js im Stammverzeichnis des Projekts erstellen (weitere Informationen dazu).
module.exports = {
target: 'serverless',
webpack: function (config) {
config.module.rules.push({test: /\.md$/, use: 'raw-loader'})
config.module.rules.push({test: /\.yml$/, use: 'raw-loader'})
return config
}
}
Next.js 9.4 führte Aliase für relative Imports ein, was hilft, die durch relative Pfade verursachten Import-Anweisungs-Spaghetti zu bereinigen. Um Aliase zu verwenden, erstellen Sie eine Datei jsconfig.json im Stammverzeichnis des Projekts, die den Basispfad und alle für das Projekt benötigten Modul-Aliase angibt.
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@includes/*": ["_includes/*"],
"@layouts/*": ["_layouts/*"],
"@posts/*": ["_posts/*"],
"@api": ["api/index"],
}
}
}
Zum Beispiel ermöglicht uns dies, unsere Layouts einfach durch die Verwendung von zu importieren
import DefaultLayout from '@layouts/default'
Alle Beiträge abrufen
Diese Funktion liest alle Markdown-Dateien im Verzeichnis _posts, parst den Front Matter am Anfang des Beitrags mit gray-matter und gibt das Array der Metadaten für alle Beiträge zurück.
// api/index.js
import matter from 'gray-matter'
export async function getAllPosts() {
const context = require.context('../_posts', false, /\.md$/)
const posts = []
for(const key of context.keys()){
const post = key.slice(2);
const content = await import(`../_posts/${post}`);
const meta = matter(content.default)
posts.push({
slug: post.replace('.md',''),
title: meta.data.title
})
}
return posts;
}
Ein typischer Markdown-Beitrag sieht so aus
---
title: "Welcome to Next.js blog!"
---
**Hello world**, this is my first Next.js blog post and it is written in Markdown.
I hope you like it!
Der durch --- abgegrenzte Abschnitt wird als Front Matter bezeichnet, der die Metadaten des Beitrags enthält, wie Titel, Permalink, Tags usw. Hier ist die Ausgabe
[
{ slug: 'style-guide-101', title: 'Style Guide 101' },
{ slug: 'welcome-to-nextjs', title: 'Welcome to Next.js blog!' }
]
Stellen Sie sicher, dass Sie die gray-matter-Bibliothek zuerst über npm mit dem Befehl npm install gray-matter --save-dev installieren.
Einen einzelnen Beitrag abrufen
Für einen gegebenen Slug sucht diese Funktion die Datei im Verzeichnis _posts, parst das Markdown mit der marked-Bibliothek und gibt das Ausgabe-HTML mit Metadaten zurück.
// api/index.js
import matter from 'gray-matter'
import marked from 'marked'
export async function getPostBySlug(slug) {
const fileContent = await import(`../_posts/${slug}.md`)
const meta = matter(fileContent.default)
const content = marked(meta.content)
return {
title: meta.data.title,
content: content
}
}
Beispielausgabe
{
title: 'Style Guide 101',
content: '<p>Incididunt cupidatat eiusmod ...</p>'
}
Stellen Sie sicher, dass Sie die marked-Bibliothek zuerst über npm mit dem Befehl npm install marked --save-dev installieren.
Konfiguration
Um die Jekyll-Konfiguration für unseren Next.js-Blog wiederzuverwenden, parsen wir die YAML-Datei mit der js-yaml-Bibliothek und exportieren diese Konfiguration, damit sie in den Komponenten verwendet werden kann.
// config.yml
title: "Next.js blog"
description: "This blog is powered by Next.js"
// api/index.js
import yaml from 'js-yaml'
export async function getConfig() {
const config = await import(`../config.yml`)
return yaml.safeLoad(config.default)
}
Stellen Sie sicher, dass Sie js-yaml zuerst über npm mit dem Befehl npm install js-yaml --save-dev installieren.
Includes
Unser Verzeichnis _includes enthält zwei einfache React-Komponenten, <Header> und <Footer>, die in den verschiedenen Layout-Komponenten im Verzeichnis _layouts verwendet werden.
// _includes/header.js
export default function Header() {
return <header><p>Blog | Powered by Next.js</p></header>
}
// _includes/footer.js
export default function Footer() {
return <footer><p>©2020 | Footer</p></footer>
}
Layouts
Wir haben zwei Layout-Komponenten im Verzeichnis _layouts. Eine ist <DefaultLayout>, die das Basislayout ist, auf dem alle anderen Layout-Komponenten aufgebaut werden.
// _layouts/default.js
import Head from 'next/head'
import Header from '@includes/header'
import Footer from '@includes/footer'
export default function DefaultLayout(props) {
return (
<main>
<Head>
<title>{props.title}</title>
<meta name='description' content={props.description}/>
</Head>
<Header/>
{props.children}
<Footer/>
</main>
)
}
Das zweite Layout ist die Komponente <PostLayout>, die den im <DefaultLayout> definierten Titel mit dem Beitragstitel überschreibt und den HTML des Beitrags rendert. Sie enthält auch einen Link zurück zur Homepage.
// _layouts/post.js
import DefaultLayout from '@layouts/default'
import Head from 'next/head'
import Link from 'next/link'
export default function PostLayout(props) {
return (
<DefaultLayout>
<Head>
<title>{props.title}</title>
</Head>
<article>
<h1>{props.title}</h1>
<div dangerouslySetInnerHTML={{__html:props.content}}/>
<div><Link href='/'><a>Home</a></Link></div>
</article>
</DefaultLayout>
)
}
next/head ist eine integrierte Komponente, um Elemente zum <head> der Seite hinzuzufügen. next/link ist eine integrierte Komponente, die clientseitige Übergänge zwischen den im Seitenverzeichnis definierten Routen handhabt.
Homepage
Als Teil der Index-Seite, auch bekannt als Homepage, werden wir alle Beiträge im Verzeichnis _posts auflisten. Die Liste wird den Beitragstitel und den Permalink zur einzelnen Beitragsseite enthalten. Die Index-Seite verwendet <DefaultLayout>, und wir importieren die Konfiguration auf der Homepage, um title und description an das Layout zu übergeben.
// pages/index.js
import DefaultLayout from '@layouts/default'
import Link from 'next/link'
import { getConfig, getAllPosts } from '@api'
export default function Blog(props) {
return (
<DefaultLayout title={props.title} description={props.description}>
<p>List of posts:</p>
<ul>
{props.posts.map(function(post, idx) {
return (
<li key={idx}>
<Link href={'/posts/'+post.slug}>
<a>{post.title}</a>
</Link>
</li>
)
})}
</ul>
</DefaultLayout>
)
}
export async function getStaticProps() {
const config = await getConfig()
const allPosts = await getAllPosts()
return {
props: {
posts: allPosts,
title: config.title,
description: config.description
}
}
}
getStaticProps wird zur Build-Zeit aufgerufen, um Seiten vorab zu rendern, indem props an die Standardkomponente der Seite übergeben werden. Wir verwenden diese Funktion, um die Liste aller Beiträge zur Build-Zeit abzurufen und das Beitragsarchiv auf der Homepage zu rendern.

Beitragsseite
Diese Seite rendert den Titel und den Inhalt des Beitrags für den slug, der als Teil des context übergeben wird. Die Beitragsseite verwendet die Komponente <PostLayout>.
// pages/posts/[slug].js
import PostLayout from '@layouts/post'
import { getPostBySlug, getAllPosts } from "@api"
export default function Post(props) {
return <PostLayout title={props.title} content={props.content}/>
}
export async function getStaticProps(context) {
return {
props: await getPostBySlug(context.params.slug)
}
}
export async function getStaticPaths() {
let paths = await getAllPosts()
paths = paths.map(post => ({
params: { slug:post.slug }
}));
return {
paths: paths,
fallback: false
}
}
Wenn eine Seite dynamische Routen hat, muss Next.js zur Build-Zeit alle möglichen Pfade kennen. getStaticPaths liefert die Liste der Pfade, die zur Build-Zeit als HTML gerendert werden müssen. Die Eigenschaft fallback stellt sicher, dass bei einem Besuch einer Route, die nicht in der Liste der Pfade vorhanden ist, eine 404-Seite zurückgegeben wird.

Produktionsreif
Fügen Sie die folgenden Befehle für build und start in package.json im Abschnitt scripts hinzu und führen Sie dann npm run build gefolgt von npm run start aus, um das statische Blog zu erstellen und den Produktionsserver zu starten.
// package.json
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
Der gesamte Quellcode in diesem Artikel ist auf diesem GitHub-Repository verfügbar. Klonen Sie es gerne lokal und spielen Sie damit herum. Das Repository enthält auch einige grundlegende Platzhalter zum Anwenden von CSS auf Ihren Blog.
Verbesserungen
Der Blog ist zwar funktionsfähig, aber für die meisten durchschnittlichen Fälle vielleicht zu einfach. Es wäre schön, das Framework zu erweitern oder einen Patch einzureichen, um einige weitere Funktionen hinzuzufügen, wie z. B.:
- Pagination (Seitennummerierung)
- Syntax-Hervorhebung
- Kategorien und Tags für Beiträge
- Styling
Insgesamt scheint Next.js sehr vielversprechend für die Erstellung statischer Websites wie eines Blogs zu sein. Gepaart mit seiner Fähigkeit, statisches HTML zu exportieren, können wir eine wirklich eigenständige App ohne Server erstellen!
Es ist ein großartiges Werkzeug, um Ihre nächste Website zu erstellen. Es hat viele großartige Funktionen und Vorteile, die Nextjs zu Ihrer ersten Wahl für den Aufbau Ihrer nächsten Webanwendung machen können.
Und das ist ein großartiger Blog für Software-Ingenieure. Sie experimentieren gerne mit Ideen mit React-Atomsymbolen und Node.js. Next.js ist ein fantastisches neues Framework zum Erstellen universeller React-Anwendungen. Danke, dass Sie einen so großartigen Artikel geteilt haben.
Hallo, ich folge diesem Tutorial. Ich bin bis zum Ende gekommen, leider kann ich meine HTML-Tags nicht richtig als Element rendern. Ich sehe
und zum Beispiel in meinen Blogbeiträgen. Der gesamte Code ist im Wesentlichen derselbe, außer dass ich kein YAML verwende. Danke
Ich möchte dies verwenden, bin mir aber nicht sicher, was der Pfad zur „…js-Datei“ sein soll. Ich habe npm zum Installieren verwendet. Die Konsole wirft einen Fehler aus, dass der Pfad zur .js-Datei falsch ist.
Ich bin auf ein Problem mit der Funktion getAllPosts gestoßen. Ich glaube, es gibt ein Problem. Ich konnte require.context nicht verwenden, da die Funktion serverseitig läuft. Ich habe stattdessen readdir verwendet. Hier ist meine Lösung
Ihr Code läuft immer noch serverseitig, weil Sie fs nicht auf der Clientseite verwenden können. Die obige Lösung funktioniert vielleicht, Sie müssen @types/webpack-env hinzufügen.
Bitte, helfen Sie.
Fehler: Modul „./osts/style-guide-101.md“ kann nicht gefunden werden
api\index.js (10:24) @ async getAllPosts
8 | for(const key of context.keys()){
9 | const post = key.slice(2);