vuepress 设计目录及导航

官网文档

  • 官网:https://v2.vuepress.vuejs.org
  • 侧边栏(目录):https://v2.vuepress.vuejs.org/zh/reference/default-theme/config.html#sidebar
  • 导航:https://v2.vuepress.vuejs.org/zh/reference/default-theme/config.html#navbar

需求

  • 侧边栏显示目录,可以对目录顺序进行调整,目录的生成需要自动完成,对于素材文件、目录可以不显示
  • 发现的其他问题解决:中文目录不展开children项。

方案设计

  • 生成目录脚本获取目录结构,生成json,并建立一个用于记录隐藏文件的。
  • 生成目录脚本生成目录前读取json,对于已存在的文件和目录,按照json顺序,用来满足对目录顺序调整的需求。
  • 生成sidebar脚本根据目录json生成可直接用于sidebar的结构,被config.json直接引用。导航栏也可以根据目录的json进行生成。
  • 其他:每个目录下要有index.md或者readme.md,不然点击目录时会显示找不到。

代码

AutoCatalog.js:生成目录文件catalog.js,目前只做了两层目录,没有递归。需要手动运行,生成catalog.js后可手动调整顺序, 文件放在docs/.vuepress/util下

const fs = require('fs');
try {
    catalogOri = require('./catalog.js');  //记录手动修改过的顺序
} catch (error) {
    console.log("init catalogOri")
    catalogOri = {};
    
}

const ignoreList = ['datasource', 'hide', '.vuepress', 'Index.md']; //不在目录展示的文件
const root = '../../';

function isDirectory(item)
{
    var stats = fs.statSync(item);
    //console.log(item, "is Dir?",  stats.isDirectory());
    return stats.isDirectory();
}

function setOriItem(catalogMap, Path, List)
{
    if(Path in catalogOri && catalogOri[Path].length > 0){
        //console.log("List:", List, " |Path", Path, "| Ori:", catalogOri[Path]);
        var tempList = catalogOri[Path].filter(item => List.includes(item)); //去掉以及被删除的内容
        List.forEach(item => {
            if (!tempList.includes(item)) {
                console.log("new item:", item)
                tempList.push(item);
            }
        })
        catalogMap[Path] = tempList ;
    } else {
        catalogMap[Path] = List;
    }
}

console.log("Ori:", catalogOri)
fs.readdir(root, (err, files) => {
    if (err) {
        console.log("err:", err);
    } else {
        const catalogMap = {};
        const fileList = files.filter(item => !ignoreList.includes(item));
        //console.log("fileList:", fileList);
        setOriItem(catalogMap, '/', fileList)

        fileList.forEach(item => {
            if (isDirectory(root + item)) {
                var Path = '/' + item;
                var fileList2 = fs.readdirSync(root + item).filter(item => !ignoreList.includes(item));
                setOriItem(catalogMap, Path, fileList2)
            }
        });
    
        const content = `module.exports =${JSON.stringify(catalogMap, null, 2)}`;
        console.log("content:", content);
        fs.writeFile('./catalog.js', content, { encoding: 'utf8' }, err => { console.log(err);});
    }
})

catalogtranslate.js:记录目录名翻译的map,在GetBar.js种会用到

module.exports ={
  "TecNote": "技术笔记",
}

GetBar.js:读取目录json生成侧边栏和导航栏的结构体的函数,文件放在docs/.vuepress/util下

//const catalog = require('./catalog'); 

const translateMap = require('./catalogtranslate')

function CreateNote(item) {
    var newNode = {};
    if( item in translateMap) {
        newNode['text'] = translateMap[item];
    } else {
        newNode['text'] = item;
    }
    newNode['link'] = '/' + item;
    return newNode;
}

function GetSideBar(catalog) {
    //console.log(catalog);
    const sidebar = {};
    for (var root in catalog) {
        var List = [];
        var path = '';
        if (root == '/' ) {
            catalog[root].forEach(item => {
                List.push(CreateNote(item)); 
            });
        }
        else {
            path += root + '/';
            catalog['/'].forEach(item => {
                if(root == '/' + item) {
                    var newNode = CreateNote(item);
                    var myChildren = [];

                    catalog[root].forEach(item => {
                        myChildren.push(path + item);       
                    });
                    newNode['children'] = myChildren;
                    //console.log(myNode);
                    List.push(newNode);
                }
                else{
                    List.push(CreateNote(item)); 
                }
            });
        }
        sidebar[root] = List;  
    };
    console.log(JSON.stringify(sidebar));
    return sidebar;
}

function GetNavBar(catalog) {
    //console.log(catalog);
    const navbar = [];
    catalog['/'].forEach(item => {
        navbar.push(CreateNote(item)); 
    });
    console.log(JSON.stringify(navbar));
    return navbar;
}

export {
    GetSideBar,
    GetNavBar
}

修改config.js 加入引用

const catalog = require('./util/catalog');
import { GetSideBar,GetNavBar } from './util/GetBar';

在主题种配置navbar和sidebar

theme: defaultTheme({
    /*
    你的其他主题配置
    */
    navbar: [
      {
        text: '导航',
        children:GetNavBar(catalog),
      },
      {
        text: '关于我',
        link: '/hide/Myself'
      },
    ],
    sidebar:GetSideBar(catalog),
  }),

gitcommit.sh:提交git、更新目录,本着省事的方式做了这个方案,但第一步需要单独手动执行还不是很优雅,此文档是git管理,也需要一个自动提交git的脚本,就做一起了,然后把0 0 * * * cd ${你的脚本路径} && sh gitcommit.sh 加到crontab里,每天0点自动提交。想手动提交也可以手动执行sh gitcommit.sh "your commit" , 文件放在docs外。

echo $#
if [ $# -gt 0 ]; then
    message=$1
else
    message="auto update file"
fi
echo $message

if [ -n "$(git status -s)" ];then
    echo "new diff"
    git add .
    git commit -m "$message"
    git push
    cd docs/.vuepress/util/  && node AutoCatalog.js  # update catalog
fi

效果

目录

docs
├── hide
│   └── MySelf.md
├── Index.md
├── NAS
│   ├── 软件篇.md
│   ├── 硬件篇.md
│   ├── Index.md
│   └── NAS介绍.md
└── TecNote
    ├── datasource
    │   └── Linux下磁盘组阵列
    │       ├── blkid.png
    │       ├── fstab.png
    │       └── GParted.png
    ├── Index.md
    ├── Linux下磁盘组阵列.md
    └── vuepress设计目录及导航.md

生成的catalog.js

module.exports ={
  "/": [
    "NAS",
    "TecNote"
  ],
  "/NAS": [
    "NAS介绍.md",
    "硬件篇.md",
    "软件篇.md"
  ],
  "/TecNote": [
    "Linux下磁盘组阵列.md",
    "vuepress设计目录及导航.md"
  ]
}

页面效果: 效果展示

Last Updated:
Contributors: Tickell