V poslední době jsem si hrál s Nuxtem při vytváření prototypů nápadů pro sebe a pro klienty. Opravdu se mi líbilo mít možnost bootstrap aplikace z příkazového řádku s hrstkou opravdu užitečných základních nástrojů. Nuxt má několik vychytávek, které mohu využít hned na začátku:
- Je to rámec pro vytváření aplikací Vue, který abstrahuje složitosti klient/server. To znamená, že jej můžeme použít jako výchozí bod pro novou aplikaci nebo pro připojení ke stávajícím rozhraním API.
- Příkazový řádek, který generuje shellovou aplikaci ze startovací šablony, kde je integrováno vykreslování na straně serveru pro SEO a rychlé načítání.
- Zavedení rámce na straně serveru (je-li to nutné, ale není to vždy), rámce uživatelského rozhraní, testovací rámce, linting a prettifying, knihovna (Axios) pro vytváření požadavků HTTP.
Pro tento projekt jsem chtěl základní zkušenosti s blogem Markdown s Vue a Nuxt, abych mohl mít hřiště pro oba.
Zde je návod, jak to dopadlo a jak si také můžete udělat svůj vlastní. Projdeme si tyto kroky:
- Vytvořte aplikaci Shell
- Načíst soubory Markdown
- Zobrazit příspěvek na blogu
- Zobrazit seznam příspěvků
- Generovat dynamické trasy pro statický web
A skončete s tím.
Nebo pokud jste netrpěliví, stáhněte si to z úložiště GitHub zde.
Vytvořte aplikaci Shell
Vytvořte shellovou aplikaci z výchozí spouštěcí šablony Nuxt spuštěním následujícího z příkazového řádku:
yarn create nuxt-app starter-for-nuxt-markdown-blog
Takto vypadá výstup:
➜ examples yarn create nuxt-app starter-for-nuxt-markdown-blog
yarn create v1.17.3
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Installed "[email protected]" with binaries:
- create-nuxt-app
[#################################################################################################################################################################################################] 373/373
create-nuxt-app v2.10.1
✨ Generating Nuxt.js project in starter-for-nuxt-markdown-blog
? Project name starter-for-nuxt-markdown-blog
? Project description Starter for a Nuxt Markdown Blog
? Author name Jenna Pederson
? Choose the package manager Yarn
? Choose UI framework Bulma
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose linting tools ESLint
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
yarn run v1.17.3
$ eslint --ext .js,.vue --ignore-path .gitignore . --fix
✨ Done in 3.35s.
🎉 Successfully created project starter-for-nuxt-markdown-blog
To get started:
cd starter-for-nuxt-markdown-blog
yarn dev
To build & start for production:
cd starter-for-nuxt-markdown-blog
yarn build
yarn start
✨ Done in 191.25s.
Po vytvoření aplikace se podívejte, jak vypadá výchozí startovací šablona Nuxt tím, že ji spustíte pomocí:
yarn dev
Poté přejděte na http://localhost:3000.
Načíst soubory Markdown
Dále použijeme frontmatter-markdown-loader
balíček pro stažení souborů markdown z adresáře s názvem content
a získat přístup k markdown frontmatter (metadata o souboru markdown, v tomto případě metadata příspěvku, jako je název, štítky, obrázek hrdiny) pro každý příspěvek.
Přidejte balíček:
yarn add frontmatter-markdown-loader
Vytvořte adresář obsahu:
mkdir -P content/blog
Chcete-li vytvořit první příspěvek, vložte tento soubor do content/blog
.
Poté vytvořte přidružený adresář pro obrazové prostředky:
mkdir -P assets/images/blog
A přidejte tento obrázek do assets/images/blog
.
Nyní, když máme nějaký obsah, můžeme rozšířit konfiguraci webpack přidáním frontmatter-markdown-loader
ke kroku sestavení v nuxt.config.js
:
build: {
...
extend(config, ctx) {
config.module.rules.push(
{
test: /\.md$/,
include: path.resolve(__dirname, "content"),
loader: "frontmatter-markdown-loader",
}
);
}
}
Zobrazit příspěvek na blogu
Nemusíme vytvářet statické stránky pro každý příspěvek, který máme, takže místo toho použijeme dynamické směrování k načtení souboru markdown. Zvažte následující cesty URL:
/blog/2019-09-22-veggies
/blog/:blog_post_title
nebo
/users/jenna-pederson
/users/:username
V obou těchto příkladech :blog_post_title
a :username
představují dynamickou část trasy neboli slimáka.
Vytvořte adresář blogu:
mkdir pages/blog
Vytvoříme blog
adresář a přidejte _slug.vue
soubor. Toto _slug.vue
soubor bude šablonou Vue pro náš blogový příspěvek. V pages/blog/_slug.vue
, přidejte následující základní šablonu:
<template>
<div class="container">
<h1 class="title">
{{ post.attributes.title }}
</h1>
<h2 class="subtitle">
{{ post.attributes.date }}
</h2>
<div class="columns">
<div class="column is-half is-offset-one-quarter">
<figure class="image">
<img :src="imgSrc">
</figure>
</div>
</div>
<!-- eslint-disable-next-line -->
<div class="content" v-html="post.html" />
</div>
</template>
<script>
export default {
computed: {
imgSrc () {
return require(`~/assets/images/blog/${this.post.attributes.hero}`)
}
},
async asyncData ({ params }) {
try {
const post = await import(`~/content/blog/${params.slug}.md`)
return {
post
}
} catch (error) {
return false
}
},
head () {
return {
title: this.post.attributes.title
}
}
}
</script>
V asyncData
importovali jsme soubor markdown na základě hodnoty slug, kterou získáme z params
. Slug je opět definován adresou URL. Například slimák pro naši adresu URL http://localhost:3000/blog/2019-09-22-veggies je 2019-09-22-veggies
, takže to importuje 2019-09-22-veggies.md
soubor a přiřaďte objekt příspěvku k datům komponenty.
Používáme v-html
za účelem vykreslení raw HTML z našeho markdown. To způsobí eslintovo varování:
9:26 warning 'v-html' directive can lead to XSS attack vue/no-v-html
Více o zranitelnostech XSS si můžete přečíst zde a zde. Ujistěte se, že víte, odkud váš obsah pochází – pokud jej píšete, vězte, že dokonce i knihovny uživatelského rozhraní třetích stran mohou způsobit zranitelnosti zabezpečení. Tohoto varování se můžeme zbavit tím, že jej budeme ignorovat pomocí eslint-disable-next-line
řádek přímo nahoře.
Nyní můžeme nasměrovat náš prohlížeč na http://localhost:3000/blog/2019-09-22-veggies a zobrazit příspěvek!
Zobrazit seznam příspěvků
Dalším krokem je možnost zobrazit seznam blogových příspěvků z naší domovské stránky a možnost přejít na každý jednotlivý příspěvek.
Abychom mohli v našem seznamu příspěvků na blogu zobrazit více než jeden příspěvek, přidejte tento příspěvek do content/blog
a je to obrázek na assets/images/blog
.
V pages/index.vue
, budeme používat asyncData
společnosti Nuxt metoda znovu načíst všechny blogové příspěvky, abychom je mohli zobrazit na stránce. V budoucnu bychom je mohli stránkovat nebo načítat pouze doporučené příspěvky, které by se zobrazovaly na domovské stránce webu. Poté přidáme v-for
smyčka v šabloně pro zobrazení příspěvků.
<template>
<div class="container">
<h1 class="title">
Blog Posts
</h1>
<section class="posts">
<div v-for="post in posts" :key="post.attributes.title" class="columns">
<div class="column is-one-quarter">
<figure class="image">
<img :src="imgSrc(post)" :alt="post.attributes.title">
</figure>
</div>
<div class="column is-three-quarters">
<p class="title is-4">
<nuxt-link :to="post._path">
{{ post.attributes.title }}
</nuxt-link>
</p>
<p class="subtitle is-6">
{{ post.attributes.tags }}
</p>
<div class="content">
<p>{{ post.attributes.excerpt }}</p>
<p>{{ post.attributes.date }}</p>
<nuxt-link :to="post._path">
Read
</nuxt-link>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
async asyncData () {
const context = await require.context('~/content/blog', true, /\.md$/)
const posts = await context.keys().map(key => ({
...context(key),
_path: `/blog/${key.replace('.md', '').replace('./', '')}`
}))
return { posts: posts.reverse() }
},
methods: {
imgSrc (post) {
return require(`~/assets/images/blog/${post.attributes.hero}`)
}
}
}
</script>
Zde načítáme všechny soubory markdown v content/blog
adresář a všechny podadresáře (jak je označeno true
). Potom mapujeme každý klíč (název souboru) na jeho kontext a vše, co chceme. V tomto případě také mapujeme _path
na cestu URL k příspěvku, abychom mohli později vytvořit odkazy. Kontext končí tím, co načte frontmatter-markdown-loader:atributy (přední část souboru markdown) a html (markdown zkompilovaný do HTML).
Nyní, když nasměrujeme náš prohlížeč zpět na http://localhost:3000/, měli bychom vidět toto:
Generování dynamických tras pro statický web
Stále nám zbývá jeden krok, a to nastavení dynamických tras pro práci s yarn generate
, krok, který generuje statický web pro produkci. V nuxt.config.js
, budeme generovat trasy na základě souborů značek, které máme v content
adresář.
Nejprve přidejte const glob = require('glob')
v horní části souboru a poté definujte markdownPaths
tam také:
const markdownPaths = ['blog']
Toto bude pole... cest k našim souborům markdown. V našem případě máme pouze jeden, ale můžete jej rozšířit na ['blog', 'portfolio', 'photos', 'recipes']
nebo cokoli potřebujete.
Potom na konec tohoto souboru přidáme tuto funkci:
function dynamicMarkdownRoutes() {
return [].concat(
...markdownPaths.map(mdPath => {
return glob.sync(`${mdPath}/*.md`, { cwd: 'content' })
.map(filepath => `${mdPath}/${path.basename(filepath, '.md')}`);
})
);
}
Tuto funkci budeme volat v generate.routes
blok. Toto lze přidat na stejné úrovni jako modules
nebo build
:
generate: {
routes: dynamicMarkdownRoutes()
},
Abychom to otestovali, vrátíme se zpět do příkazového řádku a spustíme yarn generate
, který by měl vytvořit tento výstup:
➜ starter-for-nuxt-markdown-blog git:(master) ✗ yarn generate
yarn run v1.17.3
$ nuxt generate
ℹ Production build 16:54:52
✔ Builder initialized 16:54:52
✔ Nuxt files generated 16:54:52
✔ Client
Compiled successfully in 6.85s
✔ Server
Compiled successfully in 2.18s
Hash: edf5326aac7133378e50
Version: webpack 4.40.2
Time: 6853ms
Built at: 2019-09-25 16:55:01
Asset Size Chunks Chunk Names
../server/client.manifest.json 7.26 KiB [emitted]
125f300a35d8d87618b7.js 2.08 KiB 2 [emitted] [immutable] pages/blog/_slug
2eef474de7f0fce0b490.js 2.29 KiB 7 [emitted] [immutable]
47f38e821f6391ec3abe.js 2.38 KiB 4 [emitted] [immutable] runtime
50c6bbcdbcd3e3f623ea.js 34.9 KiB 0 [emitted] [immutable] app
72339ed6891dc9a5bab0.js 192 KiB 5 [emitted] [immutable] vendors.app
LICENSES 389 bytes [emitted]
d6bf890be21b759c97e5.js 3.38 KiB 6 [emitted] [immutable]
dc728afc9091988c21a1.js 8.63 KiB 3, 6, 7 [emitted] [immutable] pages/index
fc1ca6aa66dbc344a014.js 152 KiB 1 [emitted] [immutable] commons.app
img/8c66f4e.jpg 5.78 MiB [emitted] [big]
img/ca9c582.jpg 1.03 MiB [emitted] [big]
+ 2 hidden assets
Entrypoint app = 47f38e821f6391ec3abe.js fc1ca6aa66dbc344a014.js 72339ed6891dc9a5bab0.js 50c6bbcdbcd3e3f623ea.js
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
img/8c66f4e.jpg (5.78 MiB)
img/ca9c582.jpg (1.03 MiB)
Hash: 898a2ef2951dc7e6c3b6
Version: webpack 4.40.2
Time: 2180ms
Built at: 2019-09-25 16:55:03
Asset Size Chunks Chunk Names
461c3c4ac5f760555a13.js 1.67 KiB 1 [emitted] [immutable] pages/blog/_slug
8ca9a115422e5af94cd9.js 2.32 KiB 4 [emitted] [immutable]
abf1051240f49f9b6062.js 3.41 KiB 3 [emitted] [immutable]
ec1f17082565c8004784.js 7.71 KiB 2, 3, 4 [emitted] [immutable] pages/index
server.js 214 KiB 0 [emitted] app
server.manifest.json 603 bytes [emitted]
+ 5 hidden assets
Entrypoint app = server.js server.js.map
ℹ Generating pages 16:55:03
WARN Cannot stringify POJOs with symbolic keys Symbol(Symbol.toStringTag) 16:55:03
WARN Cannot stringify POJOs with symbolic keys Symbol(Symbol.toStringTag) (repeated 1 times) 16:55:03
✔ Generated / 16:55:04
✔ Generated blog/2019-09-25-cupcake 16:55:04
✔ Generated blog/2019-09-22-veggies 16:55:04
✨ Done in 16.11s.
Tím se vygeneruje vaše stránka v dist
adresář. Chcete-li to vyzkoušet (a pravděpodobně byste měli!) před nasazením naživo, můžete také spustit yarn build
a poté yarn start
ke spuštění HTTP serveru statického webu v tomto adresáři.
Doufejme, že vám to pomůže začít s budováním blogu pomocí souborů Nuxt a markdown! Zde si můžete stáhnout tuto verzi kódu. Budu pokračovat v aktualizaci tohoto repo, jak ho buduji více. Možná se příště pustíme do těch varování o tom, že „nelze řetězit POJO pomocí symbolických klíčů“ nebo formátování zobrazení data pomocí Moment.js nebo dokonce připojení k bezhlavému CMS.
Jste připraveni začít s tím jako svým startérem na Netlify právě teď? Můžete to udělat taky!