VuePress 入门教程之二 Markdown 篇

前言

版本说明

本文的内容是基于 VuePress 1.x 讲解的,一切内容以 官方文档 为准。

教程大纲

Markdown 扩展

Header Anchors

所有的标题将会自动地应用 anchor 链接,anchor 的渲染可以通过 markdown.anchor 来配置。

链接

内部链接

网站内部的链接,将会被转换成 <router-link> 用于 SPA 导航。同时,站内的每一个文件夹下的 README.md 或者 index.md 文件都会被自动编译为 index.html,对应的链接将被视为 /

以如下的文件结构为例:

1
2
3
4
5
6
7
8
9
10
.
├─ README.md
├─ foo
│  ├─ README.md
│ ├─ one.md
│ └─ two.md
└─ bar
├─ README.md
├─ three.md
└─ four.md

假设你现在位于 foo/one.md 中:

1
2
3
4
5
[Home](/) <!-- 跳转到根部的 README.md -->
[foo](/foo/) <!-- 跳转到 foo 文件夹的 index.html -->
[foo heading](./#heading) <!-- 跳转到 foo/index.html 的特定标题位置 -->
[bar - three](../bar/three.md) <!-- 具体文件可以使用 .md 结尾(推荐) -->
[bar - four](../bar/four.html) <!-- 也可以用 .html -->

链接的重定向

VuePress 支持重定向到干净链接。如果一个链接 /foo 找不到,VuePress 会自行寻找一个可用的 /foo//foo.html。反过来,当 /foo//foo.html 中的一个找不到时,VuePress 也会尝试寻找另一个。借助这种特性,我们可以通过官方插件 vuepress-plugin-clean-urls 定制你的网站路径。

Tip 注意:无论是否使用了 permalink 和 clean-urls 插件,你的相对路径都应该依赖于当前的文件结构来定义。在上面的例子中,即使你将 /foo/one.md 的路径设为了 /foo/one/,你依然应该通过 ./two.md 来访问 /foo/two.md

页面后缀

默认情况下,页面和内部链接是以 .html 后缀生成,可以通过设置 config.markdown.pageSuffix 来自定义它。

外部链接

外部的链接将会被自动地设置为 target="_blank" rel="noopener noreferrer":

1
2
- [vuejs.org](https://vuejs.org)
- [VuePress on GitHub](https://github.com/vuejs/vuepress)

你可以自定义通过配置 config.markdown.externalLinks 来自定义外部链接的特性。

Front Matter

VuePress 提供了对 YAML Front Matter 开箱即用的支持:

1
2
3
4
---
title: Blogging Like a Hacker
lang: en-US
---

这些数据可以在当前 Markdown 的正文,或者是任意的自定义或主题组件中使用。想了解更多,请移步 Front Matter

Github 风格的表格

输入内容

1
2
3
4
5
| Tables        | Are           | Cool  |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |

输出效果

vuepress-tables

Emoji 表情

输入内容

1
:tada: :100:

输出效果

vuepress-emoji

你可以在这个列表找到所有可用的 Emoji 表情。

文档目录

输入内容

1
[[toc]]

输出效果

vuepress-toc

目录(Table of Contents)的渲染可以通过 markdown.toc 选项来配置。

自定义容器

Warning 注意:自定义容器只针对 VuePress 的默认主题有效。

输入内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
::: tip
这是一个提示
:::

::: warning
这是一个警告
:::

::: danger
这是一个危险警告
:::

::: details
这是一个详情块,在 IE / Edge 中不生效
:::

输出效果

vuepress-container

代码块中的语法高亮

VuePress 使用了 Prism 来为 Markdown 中的代码块实现语法高亮。Prism 支持大量的编程语言,你需要做的只是在代码块的开始倒勾中附加一个有效的语言别名:

输入内容

1
2
3
4
5
6
7
8
9
10
``` html
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
```

输出效果

vuepress-block-code

在 Prism 的网站上查看 合法的语言列表

代码块中的行高亮

输入内容

1
2
3
4
5
6
7
8
9
``` js {4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```

输出效果

vuepress-block-code-2


除了单行以外,你也可指定多行,行数区间,或是两者都指定。

  • 行数区间:例如 {5-8}, {3-10}, {10-17}
  • 多个单行:例如 {4,7,9}
  • 行数区间与多个单行:例如 {4,7-13,16,23-27,40}

输入内容

1
2
3
4
5
6
7
8
9
10
11
12
13
``` js{1,4,6-7}
export default { // Highlighted
data () {
return {
msg: `Highlighted!
This line isn't highlighted,
but this and the next 2 are.`,
motd: 'VuePress is awesome',
lorem: 'ipsum',
}
}
}
```

输出效果

vuepress-block-code-3

代码块行号显示

你可以通过配置来为每个代码块显示行号:

1
2
3
4
5
module.exports = {
markdown: {
lineNumbers: true
}
}

显示效果:

vuepress-block-line

代码块片段导入

你可以通过下述的语法,在 Markdown 文件中导入已经存在的其他文件中的代码段:

1
<<< @/filepath

它也支持 行高亮,语法如下:

1
<<< @/filepath{highlightLines}

输入内容

1
<<< @/../@vuepress/markdown/__tests__/fragments/snippet.js{2}

输出效果

vuepress-import-block-code

Tip 注意:由于代码段的导入将在 Webpack 编译之前执行,因此你无法使用 Webpack 中的路径别名,此处的 @ 默认值是 process.cwd()


为了只导入对应部分的代码,你也可运用 VS Code Region。你可以在文件路径后方的 # 紧接着提供一个自定义的区域名称(预设为 snippet

代码文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// #region snippet
function foo () {
return ({
dest: '../../vuepress',
locales: {
'/': {
lang: 'en-US',
title: 'VuePress',
description: 'Vue-powered Static Site Generator'
},
'/zh/': {
lang: 'zh-CN',
title: 'VuePress',
description: 'Vue 驱动的静态网站生成器'
}
},
head: [
['link', { rel: 'icon', href: `/logo.png` }],
['link', { rel: 'manifest', href: '/manifest.json' }],
['meta', { name: 'theme-color', content: '#3eaf7c' }],
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
['link', { rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png` }],
['link', { rel: 'mask-icon', href: '/icons/safari-pinned-tab.svg', color: '#3eaf7c' }],
['meta', { name: 'msapplication-TileImage', content: '/icons/msapplication-icon-144x144.png' }],
['meta', { name: 'msapplication-TileColor', content: '#000000' }]
]
})
}
// #endregion snippet

export default foo

输入内容

1
<<< @/../@vuepress/markdown/__tests__/fragments/snippet-with-region.js#snippet{1}

输出效果

vuepress-import-block-code-2

进阶配置

VuePress 使用 markdown-it 来渲染 Markdown,上述大多数的拓展也都是通过自定义的插件实现的。想要进一步的话,你可以通过 .vuepress/config.jsmarkdown 选项,来对当前的 markdown-it 实例做一些自定义的配置:

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
markdown: {
// markdown-it-anchor 的选项
anchor: { permalink: false },
// markdown-it-toc 的选项
toc: { includeLevel: [1, 2] },
extendMarkdown: md => {
     // 使用更多的 markdown-it 插件!
md.use(require('markdown-it-xxx'))
}
}
}

在 Markdown 中 使用 Vue

浏览器的 API 访问限制

当你在开发一个 VuePress 应用时,由于所有的页面在生成静态 HTML 时都需要通过 Node.js 服务端渲染,因此所有的 Vue 相关代码都应当遵循 编写通用代码 的要求。简而言之,请确保只在 beforeMount 或者 mounted 访问浏览器 DOM 的 API。


如果你正在使用,或者需要展示一个对于 SSR 不怎么友好的组件(比如包含了自定义指令),你可以将它们包裹在内置的 <ClientOnly> 组件中:

1
2
3
<ClientOnly>
<NonSSRFriendlyComponent/>
</ClientOnly>

请注意,这并不能解决一些组件或库在导入时就试图访问浏览器 API 的问题 —— 如果需要使用这样的组件或库,你需要在合适的生命周期钩子中动态导入它们:

1
2
3
4
5
6
7
8
9
<script>
export default {
mounted () {
import('./lib-that-access-window-on-import').then(module => {
// use code
})
}
}
</script>

如果你的模块通过 export default 导出一个 Vue 组件,那么你可以动态注册它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<component v-if="dynamicComponent" :is="dynamicComponent"></component>
</template>
<script>
export default {
data() {
return {
dynamicComponent: null
}
},
mounted () {
import('./lib-that-access-window-on-import').then(module => {
this.dynamicComponent = module.default
})
}
}
</script>

参考:

模板语法

插值

每一个 Markdown 文件将首先被编译成 HTML,接着作为一个 Vue 组件传入 vue-loader,这意味着你可以在文本中使用 Vue 风格的插值:

输入内容

1
{{ 1 + 1 }}

输出效果

1
2

指令

同样地,也可以使用指令:

输入内容

1
<span v-for="i in 3">{{ i }} </span>

输出效果

1
1 2 3

访问网站以及页面的数据

编译后的组件没有私有数据,但可以访问 网站的元数据,举例来说:

输入内容

1
{{ $page }}

输出效果

1
2
3
4
5
{
"path": "/using-vue.html",
"title": "Using Vue in Markdown",
"frontmatter": {}
}

Escaping

默认情况下,块级 (block) 的代码块将会被自动包裹在 v-pre 中。如果你想要在内联 (inline) 的代码块或者普通文本中显示原始的大括号,或者一些 Vue 特定的语法,你需要使用自定义容器 v-pre 来包裹:

输入内容

1
2
3
::: v-pre
`{{ This will be displayed as-is }}`
:::

输出效果

1
{{ This will be displayed as-is }}

使用组件

正常使用组件

所有在 .vuepress/components 中找到的 *.vue 文件将会自动地被注册为全局的异步组件,如:

1
2
3
4
5
6
7
.
└─ .vuepress
  └─ components
├─ demo-1.vue
    ├─ OtherComponent.vue
     └─ Foo
        └─ Bar.vue

你可以直接使用这些组件在任意的 Markdown 文件中(组件名是通过文件名取到的):

1
2
3
<demo-1/>
<OtherComponent/>
<Foo-Bar/>

vuepress-components

Warning 重要:请确保一个自定义组件的名字包含连接符或者是 PascalCase,否则,它将会被视为一个内联元素,并被包裹在一个 <p> 标签中,这将会导致 HTML 渲染紊乱,因为 HTML 标准规定, <p> 标签中不允许放置任何块级元素。

在标题中使用组件

你可以在标题中使用 Vue 组件,但是请留意以下两种方式的不同:

Markdown 输出的 HTML 解析后的标题
 # text <Tag/> 
<h1>text <Tag/></h1>text
 # text `<Tag/>` 
<h1>text <code>&lt;Tag/&gt;</code></h1>text <Tag/>

<code> 包装的 HTML 将按原样显示,只有未被包装的 HTML 才会被 Vue 解析。输出的 HTML 由 markdown-it 完成,而解析后的标题由 VuePress 完成,用于侧边栏以及文档的标题。

使用预处理器

VuePress 对以下预处理器已经内置相关的 Webpack 配置:sassscsslessstyluspug。要使用它们你只需要在项目中安装对应的依赖即可。例如,要使用 sass,需要安装:

1
$ yarn add -D sass-loader node-sass

然后你就可以在 Markdown 或是组件中使用如下代码:

1
2
3
4
<style lang="sass">
.title
font-size: 20px
</style>

要在组件中使用 <template lang="pug">,则需要安装 pugpug-plain-loader:

1
$ yarn add -D pug pug-plain-loader

需要指出的是,如果你是一个 stylus 用户,你并不需要在你的项目中安装 stylusstylus-loader,因为 VuePress 已经内置了它们。对于那些没有内置的预处理器,除了安装对应的依赖,你还需要 拓展内部的 Webpack 配置

脚本和样式提升

有时,你可以只想在当前页面应用一些 JavaScript 或者 CSS,在这种情况下,你可以直接在 Markdown 文件中使用原生的 <script> 或者 <style> 标签,它们将会从编译后的 HTML 文件中提取出来,并作为生成的 Vue 单文件组件的 <script><style> 标签。

输入内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<p class="demo" :class="$style.example"></p>

<style module>
.example {
color: #41b883;
}
</style>

<script>
export default {
props: ['slot-key'],
mounted () {
document.querySelector(`.${this.$style.example}`)
.textContent = '这个块是被内联的脚本渲染的,样式也采用了内联样式。'
}
}
</script>

输出效果

vuepress-scripts

内置的组件

OutboundLink 用来表明当前是一个外部链接,在 VuePress 中这个组件会紧跟在每一个外部链接后面。

ClientOnly

参考 浏览器的 API 访问限制

Content

  • Props:

    • pageKey - string, 要渲染的 page 的 hash key, 默认值是当前页面的 key.
    • slotKey - string, 页面的 markdown slot 的 key. 默认值是 default slot.
  • Usage

指定一个指定页面的特定 slot 用于渲染,当你使用 自定义布局 或者自定义主题时,这将非常有用。

1
<Content/>

参考:

Badge

注意: Badge 只针对 VuePress 的默认主题生效

  • Props:

    • text - string
    • type - string, 可选值: "tip"|"warning"|"error",默认值是: "tip"
    • vertical - string, 可选值: "top"|"middle",默认值是: "top"
  • Usage:

你可以在标题中,使用这个组件来为某些 API 添加一些状态。

输入内容

1
### Badge <Badge text="beta" type="warning"/> <Badge text="默认主题"/>

输入效果

vuepress-badge

参考: