- 由於不需要后端響應,所以應該是預渲染而不是服務端渲染
介紹
- VuePress 由兩部分組成:
- 第一部分是一個極簡靜態網站生成器,它包含由 Vue 驅動的主題系統和插件 API(主題系統,應該是符合某些標准的組件庫?插件?)
- 另一個部分是為書寫技術文檔而優化的默認主題,它的誕生初衷是為了支持 Vue 及其子項目的文檔需求
Features
- 內置的 Markdown 拓展,在 Markdown 中 使用 Vue
- Vue驅動的自定義主題系統,默認主題,博客主題
- Plugin
為什么不是...?
- Hexo 主題系統太過於靜態以及過度地依賴純字符串,而我們十分希望能夠好好地利用 Vue 來處理我們的布局和交互
- GitBook 當文件很多時,每次編輯后的重新加載時間長得令人無法忍受
——————————————————————————————————————————————————————————————————————
快速上手
- 如果你的現有項目依賴了 webpack 3.x,我們推薦使用 Yarn 而不是 npm 來安裝 VuePress。因為在這種情形下,npm 會生成錯誤的依賴樹。
- 指定依賴的文檔目錄
{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}
——————————————————————————————————————————————————————————————————————
目錄結構
- VuePress 遵循 “約定優於配置” 的原則
├── docs
│ ├── .vuepress (可選的) 用於存放全局的配置、組件、靜態資源等。
│ │ ├── components (可選的) 該目錄中的 Vue 組件將會被自動注冊為全局組件。
│ │ ├── theme (可選的) 用於存放本地主題。(多個組件的組合?)
│ │ │ └── Layout.vue
│ │ ├── public (可選的) 靜態資源目錄。
│ │ ├── styles (可選的) 用於存放樣式相關的文件。
│ │ │ ├── index.styl 將會被自動應用的全局樣式文件,會生成在最終的 CSS 文件結尾,具有比默認樣式更高的優先級。
│ │ │ └── palette.styl 用於重寫默認顏色常量,或者設置新的 stylus 顏色常量。
│ │ ├── templates (可選的, 謹慎配置) 存儲 HTML 模板文件。(模板?vue根節點?)
│ │ │ ├── dev.html 用於開發環境的 HTML 模板文件。
│ │ │ └── ssr.html 構建時基於 Vue SSR(服務端渲染) 的 HTML 模板文件。(為什么要有不同?)
│ │ ├── config.js (可選的) 配置文件的入口文件,也可以是 YML 或 toml。
│ │ └── enhanceApp.js (可選的) 客戶端應用的增強。?
│ │
│ ├── README.md 請留意目錄名的大寫
│ ├── guide
│ │ └── README.md
│ └── config.md
│
└── package.json
默認的頁面路由
- 文件的相對路徑對應的頁面路由地址
- /README.md 對應 /
- /guide/README.md 對應 /guide/
- /config.md 對應 /config.html
——————————————————————————————————————————————————————————————————————
基本配置
配置文件
- .vuepress/config.js 不支持熱更新,不依賴於特定主題,主題和插件可以提供額外的配置項
module.exports = {
title: 'Hello VuePress',
description: 'Just playing around'
}
- VuePress 內置了基於 headers 的搜索 —— 它會自動為所有頁面的標題、h2 和 h3 構建起一個簡單的搜索索引。
主題配置
- 一個 VuePress 主題應該負責整個網站的布局和交互細節。
應用級別的配置
- 通過創建一個 .vuepress/enhanceApp.js 文件來做一些應用級別的配置,當該文件存在的時候,會被導入到應用內部。
- enhanceApp.js 應該 export default 一個鈎子函數,並接受一個包含了一些應用級別屬性的對象作為參數。你可以使用這個鈎子來安裝一些附加的 Vue 插件、注冊全局組件,或者增加額外的路由鈎子等
// 使用異步函數也是可以的
export default ({
Vue, // VuePress 正在使用的 Vue 構造函數
options, // 附加到根實例的一些選項
router, // 當前應用的路由實例
siteData, // 站點元數據?
isServer // 當前應用配置是處於 服務端渲染 或 客戶端
}) => {
// ...做一些其他的應用級別的優化
}
——————————————————————————————————————————————————————————————————————
靜態資源
相對路徑
- 所有的 Markdown 文件都會被 webpack 編譯成 Vue 組件,因此你可以,並且應該更傾向於使用相對路徑(Relative URLs)來引用所有的靜態資源。在 *.vue 文件的模板中一樣可以工作,圖片將會被 url-loader 和 file-loader 處理,在運行生成靜態文件的構建任務時,文件會被復制到正確的位置。

- 使用 ~ 前綴來明確地指出這是一個 webpack 的模塊請求,這將允許你通過 webpack 別名來引用文件或者 npm 的依賴


- Webpack 的別名可以通過 .vuepress/config.js 中 configureWebpack 來配置
module.exports = {
configureWebpack: {
resolve: {
alias: {
'@alias': 'path/to/some/dir'
}
}
}
}
公共文件
- 可能需要提供一個靜態資源,但是它們並不直接被你的任何一個 markdown 文件或者主題組件引用 —— 舉例來說,favicons 和 PWA 的圖標,在這種情形下,你可以將它們放在 .vuepress/public 中, 它們最終會被復制到生成的靜態文件夾中。
- 注釋:如果 URL 是一個絕對路徑 (例如 /images/foo.png),它將會被保留不變。
基礎路徑
- 如果你的網站會被部署到一個非根路徑,你將需要在 .vuepress/config.js 中設置 base,舉例來說,如果你打算將你的網站部署到 https://foo.github.io/bar/,那么 base 的值就應該被設置為 "/bar/" (應當總是以斜杠開始,並以斜杠結束)。
- 有了基礎路徑(Base URL),如果你希望引用一張放在 .vuepress/public 中的圖片,你需要使用這樣路徑:/bar/image.png
- VuePress 提供了內置的一個 helper $withBase(它被注入到了 Vue 的原型上),可以幫助你生成正確的路徑:
<img :src="$withBase('/foo.png')" alt="foo">
- 不僅可以在你的 Vue 組件中使用上述的語法,在 Markdown 文件中亦是如此。
- 一個 base 路徑一旦被設置,它將會自動地作為前綴插入到 .vuepress/config.js 中所有以 / 開始的資源路徑中。
——————————————————————————————————————————————————————————————————————
Markdown 拓展
Header Anchors 錨點
- 所有的標題將會自動地應用 anchor 鏈接,anchor 的渲染可以通過 markdown.anchor 來配置。
鏈接
內部鏈接
- 網站內部的鏈接,將會被轉換成 <router-link> 用於 SPA 導航
- 站內的每一個文件夾下的 README.md 或者 index.md 文件都會被自動編譯為 index.html
.
├─ README.md
├─ foo
│ ├─ README.md
│ ├─ one.md
│ └─ two.md
└─ bar
├─ README.md
├─ three.md
└─ four.md
[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 -->
鏈接的重定向
- 如果一個鏈接 /foo 找不到,VuePress 會自行尋找一個可用的 /foo/ 或 /foo.html
- 注釋:靜態文件服務器當 /foo 無法返回資源時,會請求 /foo/ 地址。 /foo/ 等價於 /foo/index.html
- 注釋:vue-router 的路由匹配默認會把 /foo 和 /foo/ 匹配到同一組件
- 注釋:VuePress 必須實現 build 后文檔分布和 url 相匹配,因為它會為每個路由進行預渲染生成靜態文件,在客戶端通過 url 要能找到對應的資源
- 注釋:所以 markdown 文件中的跳轉路徑,是文件的實際路徑,和它 build 后的路徑或 url 路徑無關。
- 注釋:這里的鏈接重定向是指 VuePress 中的 vue-router 支持默認的匹配方式(即 /foo 和 /foo/ 匹配同一組件),同時支持在組件加載失敗時跳轉到 /foo.html 的操作(重新加載 document) - 借助這種特性,我們可以通過官方插件 vuepress-plugin-clean-urls 定制你的網站路徑
- 無論是否使用了 permalink 和 clean-urls 插件,你的相對路徑都應該依賴於當前的文件結構來定義。
外部鏈接
- 外部的鏈接將會被自動地設置為 target="_blank" rel="noopener noreferrer"
- noopener:指示瀏覽器打開鏈接而不授予新的瀏覽上下文對打開它的文檔的訪問權限-通過在打開的窗口中不設置Window.opener屬性(返回null)
- noreferrer:阻止瀏覽器導航到另一個頁面時,通過Referer:HTTP header將該頁面地址或任何其他值作為Referrer發送。 - 可以自定義通過配置 config.markdown.externalLinks 來自定義外部鏈接的特性
Front Matter
- VuePress 提供了對 YAML front matter 開箱即用的支持。這些數據可以在當前 markdown 的正文,或者是任意的自定義或主題組件中使用。
- 注釋:YAML front matter:在字符串前面解析 yaml 或 json,返回一個對象
- 注釋:YAML:一種標記語言 - 注釋:vuepress dev 添加 YAML 不支持熱更新
---
title: Blogging Like a Hacker
lang: en-US
---
GitHub 風格的表格
:-------------:
th\td 的 text-align:center-----:
th\td 的 text-align:right
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |
Emoji 表情
目錄
- 目錄(Table of Contents)的渲染可以通過 markdown.toc 選項來配置
- 注釋:例子中渲染位目錄 ul>li 列表
[[toc]]
自定義容器
- 注釋:例子中渲染為含有標題的色塊,有4種樣式
- tip:綠色提示色塊
- warning:黃色警告色塊
- danger:紅色危險色塊
- details:灰色可展開色塊
::: tip
這是一個提示
:::
::: warning
這是一個警告
:::
::: danger
這是一個危險警告
:::
::: details
這是一個詳情塊,在 IE / Edge 中不生效
:::
- 可以自定義塊中的標題:
::: danger STOP
危險區域,禁止通行
:::
::: details 點擊查看代碼
這是一段代碼
:::
- 注釋:可以通過插件 vuepress-plugin-container 插件注冊自定義色塊
代碼塊中的語法高亮
- VuePress 使用了 Prism 來為 markdown 中的代碼塊實現語法高亮。Prism 支持大量的編程語言,你需要做的只是在代碼塊的開始倒勾中附加一個有效的語言別名:
``` js
export default {
name: 'MyComponent',
// ...
}
```
- 在 Prism 的網站上查看 合法的語言列表
代碼塊中的行高亮
- 注釋:語法高亮依然會有,只是某一行背景有特殊樣式
``` js {4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
- 除了單行以外,你也可指定多行,行數區間,或是兩者都指定。
- 行數區間: 例如 {5-8}, {3-10}, {10-17}
- 多個單行: 例如 {4,7,9}
- 行數區間與多個單行: 例如 {4,7-13,16,23-27,40}
行號
- 可以通過配置來為每個代碼塊顯示行號:
module.exports = {
markdown: {
lineNumbers: true
}
}
導入代碼段
- 由於代碼段的導入將在 webpack 編譯之前執行,因此你無法使用 webpack 中的路徑別名,此處的 @ 默認值是 process.cwd()
- 注釋:process.cwd() 方法會返回 Node.js 進程的當前工作目錄。(一般是 package.json 所在的目錄)
// 語法
<<< @/filepath{highlightLines}
// 實例
<<< @/docs/cs.js{2}
<<< @/./docs/cs.js{2}
<<< @/../vue-press/docs/cs.js{2}
- 為了只導入對應部分的代碼,可以在文件路徑后方的 # 緊接着提供一個自定義的區域名稱
export default{
czb:'123'
}
// #region snippet
const czb = 2
// #endregion snippet
<<< @/docs/cs.js#snippet{1}
// 只會輸出如下代碼塊
const czb = 2
進階配置
- VuePress 使用 markdown-it 來渲染 Markdown,上述大多數的拓展也都是通過自定義的插件實現的。插件是指 markdown-it 的插件?
- 可以通過 .vuepress/config.js 的 markdown 選項,來對當前的 markdown-it 實例做一些自定義的配置
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 訪問限制
-
所有的 Vue 相關代碼都應當遵循 編寫通用代碼 的要求。簡而言之,請確保只在 beforeMount 或者 mounted 訪問瀏覽器 / DOM 的 API。
-
由於沒有動態更新,所有的生命周期鈎子函數中,只有 beforeCreate 和 created 會在服務器端渲染 (SSR) 過程中被調用。
-
應該避免在 beforeCreate 和 created 生命周期時產生全局副作用的代碼,例如在其中使用 setInterval 設置 timer。在純客戶端 (client-side only) 的代碼中,我們可以設置一個 timer,然后在 beforeDestroy 或 destroyed 生命周期時將其銷毀。但是,由於在 SSR 期間並不會調用銷毀鈎子函數,所以 timer 將永遠保留下來。
-
對於共享於服務器和客戶端,但用於不同平台 API 的任務(task),建議將平台特定實現包含在通用 API 中,或者使用為你執行此操作的 library。例如,axios 是一個 HTTP 客戶端,可以向服務器和客戶端都暴露相同的 API。
-
大多數自定義指令直接操作 DOM,因此會在服務器端渲染 (SSR) 過程中導致錯誤。有兩種方法可以解決這個問題:
- 推薦使用組件作為抽象機制,並運行在「虛擬 DOM 層級(Virtual-DOM level)」(例如,使用渲染函數(render function))。
- 如果你有一個自定義指令,但是不是很容易替換為組件,則可以在創建服務器 renderer 時,使用 directives 選項所提供"服務器端版本(server-side version)"。 -
如果你正在使用,或者需要展示一個對於 SSR 不怎么友好的組件(比如包含了自定義指令),你可以將它們包裹在內置的 <ClientOnly> 組件中
<ClientOnly>
<NonSSRFriendlyComponent/>
</ClientOnly>
- 這並不能解決一些組件或庫在導入時就試圖訪問瀏覽器 API 的問題 —— 如果需要使用這樣的組件或庫,你需要在合適的生命周期鈎子中動態導入它們
<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 }}
指令
- 同樣地,也可以使用指令:
<span v-for="i in 3">{{ i }} </span>
訪問網站以及頁面的數據
- 編譯后的組件沒有私有數據,但可以訪問 網站的元數據
- 整個網站以及特定頁面的元數據將分別暴露為 this.$site 和 this.$page 屬性,它們將會被注入到每一個當前應用的組件中。
- $site 中 title, description 和 base 會從 .vuepress/config.js 中對應的的字段復制過來
{
"title": "VuePress",
"description": "Vue 驅動的靜態網站生成器",
"base": "/",
"pages": [
{
"lastUpdated": 1524027677000,
"path": "/",
"title": "VuePress",
"frontmatter": {}
},
...
]
}
- pages 是一個包含了每個頁面元數據對象的數據,包括它的路徑、頁面標題(明確地通過 YAML front matter 指定,或者通過該頁面的第一個標題取到),以及所有源文件中的 YAML front matter 的數據。
{
"lastUpdated": 1524847549000,
"path": "/custom-themes.html",
"title": "自定義主題",
"headers": [/* ... */],
"frontmatter": {}
}
- 如果用戶在 .vuepress/config.js 配置了 themeConfig,你將可以通過 $site.themeConfig 訪問到它。如此一來,你可以通過它來對用戶開放一些自定義主題的配置 —— 比如指定目錄或者頁面的順序,你也可以結合 $site.pages 來動態地構建導航鏈接。
- 作為 Vue Router API 的一部分,this.$route 和 this.$router 同樣可以使用。
- lastUpdated 是這個文件最后一次 git 提交的 UNIX 時間戳
Escaping
- 默認情況下,塊級 (block) 的代碼塊將會被自動包裹在 v-pre 中。
- 注釋:塊級代碼應該是的 html 標簽
- 如果你想要在內聯 (inline) 的代碼塊或者普通文本中顯示原始的大括號,或者一些 Vue 特定的語法,你需要使用自定義容器 v-pre 來包裹
::: v-pre
`{{ This will be displayed as-is }}`
<div>{{ This will be displayed as-is }}</div>
:::
使用組件
- 所有在 .vuepress/components 中找到的 *.vue 文件將會自動地被注冊為全局的異步組件
- 可以直接使用這些組件在任意的 Markdown 文件中(組件名是通過文件名取到的)
- 請確保一個自定義組件的名字包含連接符或者是 PascalCase,否則,它將會被視為一個內聯元素,並被包裹在一個 <p> 標簽中,這將會導致 HTML 渲染紊亂,因為 HTML 標准規定, <p> 標簽中不允許放置任何塊級元素。
<demo-1/>
<OtherComponent/>
<Foo-Bar/>
在標題中使用組件
- 可以在標題中使用 Vue 組件,但是請留意以下兩種方式的不同
# text <Tag/> // 實際渲染了組件 Tag
# text `<Tag/>` // 編譯為 <h1>text <code><Tag/></code></h1>,實際不是兩種方式,`...` 在 Markdown 中被編譯為 <code> 標簽包裹的 html
使用預處理器
- VuePress 對以下預處理器已經內置相關的 webpack 配置:sass、scss、less、stylus 和 pug。要使用它們你只需要在項目中安裝對應的依賴即可。
yarn add -D sass-loader node-sass
- 注釋:pug 模板引擎 - 可以在 Markdown 或是組件中使用如下代碼
<style lang="sass">
.title
font-size: 20px
</style>
<template lang="pug">
- 使用 config.configureWebpack 拓展內部的 Webpack 配置
腳本和樣式提升
- 直接在 Markdown 文件中使用原生的 <script> 或者 <style> 標簽,它們將會從編譯后的 HTML 文件中提取出來,並作為生成的 Vue 單文件組件的 <script> 和 <style> 標簽。
內置的組件
OutboundLink
- 注釋:一個圖標
- 用來表明當前是一個外部鏈接。在 VuePress 中這個組件會緊跟在每一個外部鏈接后面。
ClientOnly
- 該組件內的子組件只在客戶端渲染
Content
- Markdown 文件是元數據(頁面內容、配置等)的提供者,而布局組件負責消費他們。我們可以通過 frontmatter 來定義一些普通數據類型的元數據,但對於 Markdown / HTML 這種涉及到編譯前后差異的復雜元數據,frontmatter 卻無能能力
- 可以通過下述的語法來定義一個具名 Markdown 插槽:在布局組件中利用 Content 組件來使用該插槽
::: slot name
:::
<Content slot-key="name"/>
- 默認情況下,一個 Markdown 文件中的普通內容將會成為 Markdown 插槽的默認內容,你可以直接使用 Content 組件來訪問它
- 和 Vue 本身提供的 slot 機制不太相同,每個 Content 分發的內容都會被一個 div 所包裹,其 class 是 content 和 slot 的名字。
- 請確保所定義的 slot 的唯一性。
- Props:
- pageKey - string, 要渲染的 page 的 hash key, 默認值是當前頁面的 key.
- slotKey - string, 頁面的 markdown slot 的 key. 默認值是 default slot.
Badge
- 可以在標題中,使用這個組件來為某些 API 添加一些狀態
- 注釋:就是一個色塊組件,用哪都行
- Props:
- text - string
- type - string, 可選值: "tip"|"warning"|"error",默認值是: "tip"
- vertical - string, 可選值: "top"|"middle",默認值是: "top"
### Badge <Badge text="beta" type="warning"/> <Badge text="默認主題"/>
——————————————————————————————————————————————————————————————————————
多語言支持
站點多語言配置
- 要啟用 VuePress 的多語言支持,首先需要使用如下的文件結構:
docs
├─ README.md
├─ foo.md
├─ nested
│ └─ README.md
└─ zh
├─ README.md
├─ foo.md
└─ nested
└─ README.md
- 然后,在 .vuepress/config.js 中提供 locales 選項:
module.exports = {
locales: {
// 鍵名是該語言所屬的子路徑
// 作為特例,默認語言可以使用 '/' 作為其路徑。
'/': {
lang: 'en-US', // 將會被設置為 <html> 的 lang 屬性
title: 'VuePress',
description: 'Vue-powered Static Site Generator'
},
'/zh/': {
lang: 'zh-CN',
title: 'VuePress',
description: 'Vue 驅動的靜態網站生成器'
}
}
}
- 如果一個語言沒有聲明 title 或者 description,VuePress 將會嘗試使用配置頂層的對應值。如果每個語言都聲明了 title 和 description,則頂層的這兩個值可以被省略
默認主題多語言配置
- 默認主題也內置了多語言支持,可以通過 themeConfig.locales 來配置。該選項接受同樣的 { path: config } 格式的值。每個語言除了可以配置一些站點中用到的文字之外,還可以擁有自己的 導航欄 和 側邊欄 配置:
module.exports = {
locales: { /* ... */ },
themeConfig: {
locales: {
'/': {
selectText: 'Languages',
label: 'English',
ariaLabel: 'Languages',
editLinkText: 'Edit this page on GitHub',
serviceWorker: {
updatePopup: {
message: "New content is available.",
buttonText: "Refresh"
}
},
algolia: {},
nav: [
{ text: 'Nested', link: '/nested/', ariaLabel: 'Nested' }
],
sidebar: {
'/': [/* ... */],
'/nested/': [/* ... */]
}
},
'/zh/': {
// 多語言下拉菜單的標題
selectText: '選擇語言',
// 該語言在下拉菜單中的標簽
label: '簡體中文',
// 編輯鏈接文字
editLinkText: '在 GitHub 上編輯此頁',
// Service Worker 的配置
serviceWorker: {
updatePopup: {
message: "發現新內容可用.",
buttonText: "刷新"
}
},
// 當前 locale 的 algolia docsearch 選項
algolia: {},
nav: [
{ text: '嵌套', link: '/zh/nested/' }
],
sidebar: {
'/zh/': [/* ... */],
'/zh/nested/': [/* ... */]
}
}
}
}
}