# 如何配置文档? Important

提示

配置文档的细节, 如何方便快捷的配置文档

# Vuepress 目录结构

VuePress 遵循 “约定优于配置” 的原则,推荐的目录结构如下:

.
├── docs
│   ├── .vuepress (可选的)
│   │   ├── components (可选的)
│   │   ├── theme (可选的)
│   │   │   └── Layout.vue
│   │   ├── public (可选的)
│   │   ├── styles (可选的)
│   │   │   ├── index.styl
│   │   │   └── palette.styl
│   │   ├── templates (可选的, 谨慎配置)
│   │   │   ├── dev.html
│   │   │   └── ssr.html
│   │   ├── config.js (可选的)
│   │   └── enhanceApp.js (可选的)
│   │ 
│   ├── README.md
│   ├── guide
│   │   └── README.md
│   └── config.md
│ 
└── package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

警告

注意

请留意目录名的大写。

  • docs/.vuepress: 用于存放全局的配置、组件、静态资源等。
  • docs/.vuepress/components: 该目录中的 Vue 组件将会被自动注册为全局组件。
  • docs/.vuepress/theme: 用于存放本地主题。
  • docs/.vuepress/styles: 用于存放样式相关的文件。
  • docs/.vuepress/styles/index.styl: 将会被自动应用的全局样式文件,会生成在最终的 CSS 文件结尾,具有比默认样式更高的优先级。
  • docs/.vuepress/styles/palette.styl: 用于重写默认颜色常量,或者设置新的 stylus 颜色常量。
  • docs/.vuepress/public: 静态资源目录。
  • docs/.vuepress/templates: 存储 HTML 模板文件。
  • docs/.vuepress/templates/dev.html: 用于开发环境的 HTML 模板文件。
  • docs/.vuepress/templates/ssr.html: 构建时基于 Vue SSR 的 HTML 模板文件。
  • docs/.vuepress/config.js: 配置文件的入口文件,也可以是 YML 或 toml。
  • docs/.vuepress/enhanceApp.js: 客户端应用的增强。

警告

注意

当你想要去自定义 templates/ssr.htmltemplates/dev.html 时,最好基于 默认的模板文件 来修改,否则可能会导致构建出错。

同时阅读:

Vuepress 文档

# 配置文件

const path = require('path');
const plugins = require('./utils/plugins');
const { sidebarHelper, sortSidebar } = require('./utils/sidebarHelper');
const nav = require('./utils/nav');
const slugify = require('@vuepress/shared-utils').slugify

/**
 * sortSidebar 根据 alias 设置顺序,默认顺序为 文件夹名字的顺序
 */

const sidebar = sortSidebar(sidebarHelper());

const SPECIAL_HEADINGS = {
  '!': 'exclamation',
  '?': 'question',
  '+ -': 'plus-and-minus',
};

module.exports = {
  // 替换成你的仓库名
  base: '/vuepress-docs-template/',
  title: 'Vuepress Doc Template',
  description: 'Welcome to Vuepress Doc Template',
  port: 9527,
  // dest: 'dist',
  // head 配置
  head: [
    ['link', { rel: 'icon', href: '/logo.png' }],
    ['link', { rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css' }],
    ['link', { rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.10.0/github-markdown.min.css' }],
    ['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' }],
  ],
  // 别名配置
  configureWebpack: {
    resolve: {
      alias: {
        '@images': path.join(__dirname, '../..'),
      }
    }
  },
  // chainWebpack:(config,isServer) =>{
  //   config.resolve.alias.set('@images',path.resolve(__dirname, '../../'))
  // },
  locales: {
    '/': {
      lang: 'zh-CN',
    }
  },
  // markdown
  markdown: {
    lineNumbers: true,
		/**
		 * 解决一个 "无实质内容的标题导致 permalink 出错" 的问题,
		 * 目前发现有若干个有点问题的, 如 `!`
		 *
		 * @todo: 如果开始有多类似的个例, 可以引入特殊的 "heading anchor" 格式, 并统一处理
		 *
		 * 参考 : http://caibaojian.com/vuepress/config/#markdown-slugify
		 *
		 * @param {string} heading
		 * @return {string|*|string}
		 */
		slugify: heading => {
				const originResult = slugify(heading)

			const trimmedHeading = (heading || '').trim()
			if (trimmedHeading in SPECIAL_HEADINGS) {
				return SPECIAL_HEADINGS[(heading || '').trim()] || ''
			}
			return originResult
		},
    anchor: {
      permalink: true,
    },
    toc: {
      includeLevel: [1, 2],
    },
    extendMarkdown: md => {
      md.set({ html: true });
      md.use(require('markdown-it-katex'));
      md.use(require('markdown-it-task-lists'));
      md.use(require('markdown-it-imsize'), { autofill: true });
    }
  },
  // 主题配置
  themeConfig: {
    theme: 'vue',
    repo: 'https://github.com/Rain120/vuepress-docs-template',
    // repoLabel: 'Repo',

    // displayAllHeaders: true,
    sidebar,
    nav,

    // polyfill IE
    evergreen: true,

    // search
    search: true,
    searchMaxSuggestions: 10,
    // 申请
    // https://docsearch.algolia.com/apply/
    // algolia: {
    //   apiKey: '',
    //   indexName: ''
    // },

    // PWA
    serviceWorker: true,

    displayAllHeaders: true,

    smoothScroll: true,

    // footer
    date_format: 'yyyy-MM-dd',
    lastUpdated: 'Last Updated',
    repoLabel: '查看源码',
    docsDir: 'docs',
    docsBranch: 'master',
    editLinks: true,
    editLinkText: '帮助我们改善此页面!'
  },
  plugins
};
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

util path

docs/.vuepress/utils
├── alias.json // 别名
├── copy.js // 复制添加著作信息
├── nav.js // navbar
├── plugins.js // 插件
└── sidebarHelper.js // 自动生成sidebar
1
2
3
4
5
6

# 别名配置

# 简约配置方式

提示

通过使用 新建文件 的脚本来动态配置 alias, 妈妈再也不用担心我找不到配置了。

我们不支持中文名文件夹, 所以你需要给文件名配置别名, 只需要在docs/.vuepress/utils/alias.json中配置 key-value值即可

提示

alias 的顺序就是文档的顺序

Eg:
  p2: p2
  p1: p1
  p1-c2: p1-c2
  p1-c1: p1-c1
  -> 菜单顺序是
  p2
  p1
    c2
    c1
1
2
3
4
5
6
7
8
9
10
{
  "guide": "介绍",
  "how-to-config-docs": "如何配置文档? ",
  "how-to-write-docs": "如何写文档? "
}
1
2
3
4
5

SidebarHelper Note

我们通过SidebarHelper中的 prefixPath来自动生成sidebar侧边栏菜单, 如果出现侧边栏问题,请检查这个地址对不对。











 































































































/*
 * @Author: Rainy
 * @Date: 2020-02-27 16:38:27
 * @LastEditors: Rainy
 * @LastEditTime: 2020-07-11 17:12:45
 */

const fs = require('fs');
const path = require('path');

const language = 'zh';
const filePath = path.join(__dirname, `../../${language}`);
const ignore = ['images', '.vuepress', '.DS_Store'];
const README_REG = /README/;

// INFO: 从 package.json 获取你的文档名字, 主要为了配置多层级子目录
const docsPath = process.cwd();
const pkg = require(`${docsPath}/package.json`);
const pkgName = pkg.name || docsPath.trim().split('/').slice(-1).toString();

/**
 * @description 特殊处理文档顺序
 * alias 的顺序决定了文档菜单的目录顺序,子菜单亦可
 * Eg:
 * p2: p2
 * p1: p1
 * p1-c2: p1-c2
 * p1-c1: p1-c1
 * -> 菜单顺序是
 * p2
 * p1
 *   c2
 *   c1
 */
const alias = require('./alias.json');

const mapper = code => {
  return alias[code];
}

const isFile = ({ dir = filePath, fPath }) =>
  fs.statSync(path.join(dir, fPath)).isFile();

function syncDirPath(file = filePath) {
  return fs.readdirSync(file) || [];
}

function helper({ dir, fPath }) {
  const prefixPath = dir.split(`${pkgName}/docs`)[1];
  const currentPath = path.join(dir, fPath);

  const children = syncDirPath(currentPath)
    .filter(fPath => !ignore.includes(fPath))
    .map((sub, index) => {
      const fsStats = fs.statSync(path.join(currentPath, sub));

      if (fsStats.isDirectory()) {
        return helper({ dir: currentPath, fPath: sub });
      } else if (fsStats.isFile() && !README_REG.test(sub)) {
        const name = sub.replace('\.md', '');
        return {
          title: mapper(name) || `${name}`,
          key: name,
          path: `${prefixPath}/${fPath}/${name}`,
        }
      }
    }).filter(Boolean);

  return {
    title: mapper(fPath) || `${fPath}`,
    key: fPath,
    path: `${prefixPath}/${fPath}/`,
    collapsable: true,
    children: Array.isArray(children) ? children : [children],
  }
}

function sidebarHelper(dir = filePath) {
  return syncDirPath(dir)
    .filter(fPath => !isFile({ fPath }) && !ignore.includes(fPath))
    .map((fPath, dirIndex) => {
      return helper({ dir, fPath }) || [];
    });
}

function findIndex(value) {
  return Object.keys(alias).findIndex(item => item === value)
}

function sortSidebar(sidebar) {
  sidebar.sort((a, b) => {
    return findIndex(a.key) - findIndex(b.key);
  });
  for (const item of sidebar) {
    if (item && Array.isArray(item.children)) {
      item.children = sortSidebar(item.children);
    }
  }
  return sidebar;
}

module.exports = {
  sidebarHelper,
  sortSidebar,
};
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

# 配置Gitalk

Gitalk OAuth App Setting

gitalk-setting-2

gitalk-setting-1

module.exports = {
  plugins: [
    [
      'vuepress-plugin-awesome-gitalk', {
        log: true,
        enable: true,
        // 挂载节点
        root: "gitalk-container",
        //最大重试次数
        maxRetryCount: 5,
        // 默认是检查时间
        defaultCheckMinutes: 500,
        // 是否开启首页评论
        home: false,
        // 关闭 Gitalk 评论页面, 正则匹配, eg: /docs/
        ignorePaths: ['/'],
        // gitalk 配置
        gitalk: {
          clientID: 'your clientID',
          clientSecret: 'your clientSecret',
          repo: 'your repo name',
          owner: 'your owner',
          admin: ['your admin'],
          language: 'zh-CN',
        }
      }
    ],
  ]
}
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

# 已配置插件

# 外部插件

  • Gitalk

  • Back-to-top

# 内置插件

  • Google Analytics

  • PWA

  • Blog

  • Last Updated

  • Medium Zoom