从零开始创建一个简单博客

更新时间: 2021-02-03 09:17:53

# 创建简单VuePress应用

  1. 创建并进入一个新目录
    mkdir vuepress-starter && cd vuepress-starter
1
  1. 使用你喜欢的包管理器进行初始化
    yarn init # npm init
1
  1. 将VuePress安装为本地依赖 作者已经不再推荐全局安装VuePress
    yarn add -D vuepress # npm install -D vuepress
1

警告

如果你的现有项目依赖了 webpack 3.x,我们推荐使用 Yarn 而不是 npm 来安装 VuePress。因为在这种情形下,npm 会生成错误的依赖树。

  1. 创建你的第一篇文档
    mkdir docs && echo '# Hello VuePress' > docs/README.md
1

5.在package.json中添加一些scripts 这一步骤是可选的,但我们推荐你完成它。在下文中,我们会默认这些 scripts 已经被添加。

    {
      "scripts":{
        "dev":"vuepress dev docs",
        "docs:build":"vuepress build docs"
      }
    }
1
2
3
4
5
6
  1. 在本地启动服务器
    yarn dev # npm run dev
1

VuePress 会在 http://localhost:8080 (opens new window) 启动一个热重载的开发服务器。

# 目录结构

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

警告

请留意目录名的大写。

  • 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 SSRHTML 模板文件。
  • docs/.vuepress/config.js: 配置文件的入口文件,也可以是 YMLtoml
  • docs/.vuepress/enhanceApp.js: 客户端应用的增强。

大概过一下,现在看不懂没关系。

# 路由规则

事实上,一个 VuePress 网站是一个由 VueVue Routerwebpack 驱动的单页应用。如果你以前使用过 Vue 的话,当你在开发一个自定义主题的时候,你会感受到非常熟悉的开发体验,你甚至可以使用 Vue DevTools 去调试你的自定义主题。

对于上述的目录结构,默认页面路由地址如下:

文件的相对路径 页面路由地址
/README.md /
/guide/README.md /guide/
/config.md /config.html

vuepress的路由默认和根据文件结构一样的,READEME.md的路由为 '/'

# 配置主页

# 基础配置

一个 VuePress 网站必要的配置文件是 .vuepress/config.js

  1. docs目录下创建一个.vuepress的文件夹,文件夹中新建config.js,内容如下:
    module.exports = {
      title: 'Hello VuePress',
      description: 'Just playing around'
    }
1
2
3
4

对于上述的配置,如果你运行起 dev server,你应该能看到一个页面,它包含一个页头,里面包含一个标题和一个搜索框。

# 首页配置

docs/README.md是这个文档的首页,内容修改如下:

---
    home: true
    heroImage: /img/home.jpg
    heroText: 刀刀的知识积累
    tagline: 懒散二字,立身之贼也。千德万业,日怠废而无成;干罪万恶,日横恣而无制,皆此二字为之。西晋仇礼法而乐豪放,病本正在此安肆日偷。安肆,懒散之谓也。此圣贤之大成也。
    actionText: 立即学习 →
    actionLink: /config
    features:
    - title: 简洁至上
      details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
    - title: Vue驱动
      details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
    - title: 高性能
      details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
    footer: MIT Licensed | Copyright © 2021-present 刀刀
---
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

重新启动一下项目,仔细体会一下这些配置的作用。

heroImage的那张图片放在 .vuepress/public/img下,是上文介绍过的静态资源目录。

# 配置导航栏

现在首页配置完了,但是首页上面的导航栏只有一个搜索框。 配置导航栏在.vuepress/config.js中配置

// .vuepress/config.js
    module.exports = {
      themeConfig: {
        logo: '/img/home.jpg',
      }
    }
1
2
3
4
5
6

# 导航栏链接

    module.exports = {
      themeConfig: {
        logo: '/img/home.jpg',
        nav: [
            { text: '主页', link: '/' },
            {text: '前端学习',
                items: [
                    { 
                        text: '基础',
                        items:[
                            {text:'es6',link:'/es6/'},
                            {text:'ts',link:'/ts/'},
                            {text:'nodejs',link:'/nodejs'},
                            {text:'css',link:'/css/'}
                        ]
                    },
                    { 
                        text: '框架学习',
                        items:[
                            {text:'vue',link:'/vue/'},
                            {text:'react',link:'/react'},
                            {text:'vuePress',link:'/vuepress/'},
                            {text:'qiankun微前端',link:'/qiankun/'}
                        ]
                    },
                ]
            },
            { text: 'External', link: 'https://baidu.com', target:'_blank_', rel:'' }
        ],
      }
    }
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

导航可以是外部链接也可以是内部链接,外部链接可以加上targetrel属性,这是<a>标签的两个属性
当你提供了一个 items 数组而不是一个单一的 link 时,它将显示为一个 下拉列表
此外,你还可以通过嵌套的 items 来在 下拉列表 中设置分组

# 配置侧边栏

先添加几篇文档。目录结构如下:

.
├── docs
│   ├── .vuepress
│   │   ├── public
│   │   └── config.js
│   │ 
│   ├── README.md
│   ├── page
│   │   ├── page1.md
│   │   ├── page2.md
│   │   └── group1
│   │       ├── page3.md
│   │       └── page4.md
│   ├──  article
│   │   ├──  article1.md
│   │   ├──  article2.md
│   │   └── group2
│   │       ├──  article3.md
│   │       └──  article4.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

根据前面讲的路由规则,这几个页面的路由如下:

页面 路由
page1.md /page/page1
page2.md /page/page2
page3.md /page/group1/page3
page4.md /page/group1/page4
article1.md /article/article1
article2.md /article/article2
article3.md /article/group2/article3
article4.md /article/group2/article4

# 配置静态侧边栏

关于配置侧边栏可以参考VuePress文档 (opens new window)

我希望查看page下的文章时,侧边栏只展示page相关的链接,查看article下的文章时,侧边栏只展示article相关的链接。所以侧边栏的配置应该是这样的:

    // .vuepress/config.js
    module.exports = {
      themeConfig: {
        sidebar: {
          '/page/': [
            '/page/page1',
            '/page/page2',
            {
              title: 'group1',   // 必要的
              collapsable: false, // 可选的, 默认值是 true,
              sidebarDepth: 1,    // 可选的, 默认值是 1
              children: [
                '/page/group1/page3',
                '/page/group1/page4'
              ]
            }
          ],
          '/article/': [
            '/article/article1',
            '/article/article2',
            {
              title: 'group2',   // 必要的
              collapsable: false, // 可选的, 默认值是 true,
              sidebarDepth: 1,    // 可选的, 默认值是 1
              children: [
                '/article/group2/article3',
                '/article/group2/article4'
              ]
            }
          ],
        }
      }
    }
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

侧边栏就配置好了。

# 动态生成侧边栏

然鹅,我们写的是个小博客,可能每天都要写好几篇新的文章,写死的侧边栏显然不可取,我们码农当然希望根据文档结构自动生成侧边栏。稍微改一下配置,如下:

  1. 创建 .vuepress/utils/getSidebarData.js
    const fs = require("fs");
    const path = require("path");

    /**
    * string比较工具类
    */

    const str = {
      contains: function (string, substr, isIgnoreCase) {
        if (isIgnoreCase) {
          string = string.toLowerCase();
          substr = substr.toLowerCase();
        }
        var startChar = substr.substring(0, 1);
        var strLen = substr.length;
        for (var j = 0; j < string.length - strLen + 1; j++) {
          if (string.charAt(j) == startChar) {
            //如果匹配起始字符,开始查找
            if (string.substring(j, j + strLen) == substr) {
              //如果从j开始的字符与str匹配,那ok
              return true;
            }
          }
        }
        return false;
      }
    };

    /**

    * 文件助手: 主要用于读取当前文件下的所有目录和文件

    */

    const filehelper = {
      getAllFiles: function (rpath) {
        let filenames = []
        fs.readdirSync(rpath).forEach(file => {
          fullpath = rpath + "/" + file;
          var fileinfo = fs.statSync(fullpath);
          // 过滤 .DS_Store
          if (fileinfo.isFile() && !str.contains(file, "DS_Store", true)) {
            if (file === "README.md" || file === "readme.md") {
              file = "";
            } else {
              file = file.replace(".md", "");
            }
            filenames.push(file);
          }
        });
        filenames.sort();
        return filenames;
      },

      getAllDirs: function getAllDirs(mypath = "docs") {
        const items = fs.readdirSync(mypath);
        let result = []
          // 遍历当前目录中所有文件夹
          items.map(item => {
            let temp = path.join(mypath, item);
            // 过滤无关的文件夹
            if ( fs.statSync(temp).isDirectory() && !item.startsWith(".") && !str.contains(item, "DS_Store", true)) {
              let path = item;
              result.push(path)
            }
          });
        return result;
      }
    };

    // 侧边栏

    var sidebar = {};

    function genSideBar() {
      //查找docs目录下的文件夹,排除掉.vuepress之类的配置文件
      let firstFoder = filehelper.getAllDirs('docs')
      for(let i = 0;i< firstFoder.length; i++){
        //生成sidebar配置
        sidebar["/"+firstFoder[i]+"/"] = getFileOrFolder('docs',firstFoder[i])
      }
    }

    function getFileOrFolder(root,path){
      let arr = []
      let foders = filehelper.getAllDirs(root+'/'+path)
      let files = filehelper.getAllFiles(root+'/'+path)
      files.forEach(item => arr.push('/'+path+'/'+item))
      if(foders.length > 0){
        foders.forEach(folder => {
          arr.push({
            title:filterNumber(folder),
            sidebarDepth: 2,    // 可选的, 默认值是 1
            children:getFileOrFolder(root,path+"/"+folder)
          })
        })
      }
      return arr
    }

    /**
     * 过滤字符串中的数字
     * @param {字符串} str 
     */
    function filterNumber(str){
      const ret = /^[0-9]+./
      let txt = str.replace(ret,"")
      return txt
    }

    genSideBar()

    module.exports = sidebar;
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
  1. .vuepress/config.js中导入刚刚生成的sidebar
    const sidebar = require("./utils/getSidebarData")

    module.exports = {
        themeConfig: {
            sidebar:sidebar
        }
    }
1
2
3
4
5
6
7

重启项目,动态侧边栏就成功了。

# 结尾

现在这个简简单单的博客项目就启动起来了,可以开心写文章了,撒花!