当前位置: 首页 > news >正文

vue进阶04-vue文档生成工具vuepress2

介绍

VuePress 是一个以 Markdown 为中心的静态网站生成器。你可以使用 Markdown 来书写内容(如文档、博客等),然后 VuePress 会帮助你生成一个静态网站来展示它们。

VuePress 诞生的初衷是为了支持 Vue.js 及其子项目的文档需求,但是现在它已经在帮助大量用户构建他们的文档、博客和其他静态网站

它是如何工作的?

一个 VuePress 站点本质上是一个由 Vue 和 Vue Router 驱动的单页面应用 (SPA)。

路由会根据你的 Markdown 文件的相对路径来自动生成。每个 Markdown 文件都通过 markdown-it 编译为 HTML ,然后将其作为 Vue 组件的模板。因此,你可以在 Markdown 文件中直接使用 Vue 语法,便于你嵌入一些动态内容。

在开发过程中,我们启动一个常规的开发服务器 (dev-server) ,并将 VuePress 站点作为一个常规的 SPA。如果你以前使用过 Vue 的话,你在使用时会感受到非常熟悉的开发体验。

在构建过程中,我们会为 VuePress 站点创建一个服务端渲染 (SSR) 的版本,然后通过虚拟访问每一条路径来渲染对应的 HTML 。这种做法的灵感来源于 Nuxt 的 nuxt generate 命令,以及其他的一些项目,比如 Gatsby。

为什么不是 …?

Nuxt

Nuxt 是一套出色的 Vue SSR 框架, VuePress 能做的事情,Nuxt 实际上也同样能够胜任。但 Nuxt 是为构建应用程序而生的,而 VuePress 则更为轻量化并且专注在以内容为中心的静态网站上。

VitePress

VitePress 是 VuePress 的孪生兄弟,它同样由 Vue.js 团队创建和维护。 VitePress 甚至比 VuePress 要更轻更快,但它在灵活性和可配置性上作出了一些让步,比如它不支持插件系统。当然,如果你没有进阶的定制化需求, VitePress 已经足够支持你将你的内容部署到线上。

这个比喻可能不是很恰当,但是你可以把 VuePress 和 VitePress 的关系看作 Laravel 和 Lumen 。

Docsify / Docute

这两个项目同样都是基于 Vue,然而它们都是完全的运行时驱动,因此对 SEO 不够友好。如果你并不关注 SEO,同时也不想安装大量依赖,它们仍然是非常好的选择!

Hexo

Hexo 一直驱动着 Vue 2.x 的文档。Hexo 最大的问题在于他的主题系统太过于静态以及过度地依赖纯字符串,而我们十分希望能够好好地利用 Vue 来处理我们的布局和交互。同时,Hexo 在配置 Markdown 渲染方面的灵活性也不是最佳的。

GitBook

过去我们的子项目文档一直都在使用 GitBook 。 GitBook 最大的问题在于当文件很多时,每次编辑后的重新加载时间长得令人无法忍受。它的默认主题导航结构也比较有限制性,并且,主题系统也不是 Vue 驱动的。GitBook 背后的团队如今也更专注于将其打造为一个商业产品而不是开源工具。

快速上手

环境要求

Node.js v14.18.0+
Yarn v1 classic (尽量用yarn安装依赖,npm尝试n多次各种兼容问题)

hellworld

步骤1: 创建并进入一个新目录

mkdir vuepress-starter
cd vuepress-starter

步骤2: 初始化项目

yarn init

步骤3: 将 VuePress 安装为本地依赖

yarn add -D vuepress@next

步骤4: 在 package.json 中添加一些 scripts

{
  "scripts": {
    "docs:dev": "vuepress dev docs",
    "docs:build": "vuepress build docs"
  }
}

安装本地vuepress后可使用命令行运行

.\node_modules\.bin\vuepress dev docs
# 分析vuepress.cmd后发现执行的js文件是
node .\node_modules\vuepress\bin\vuepress.js dev docs
# vuepress1.x md文件过多,存内存溢出的增加内存执行方式
node --max_old_space_size=7096 ./node_modules/vuepress/cli.js build
# vuepress2.x md文件过多,内存溢出的增加内存执行方式 vuepress.1.x
node --max_old_space_size=7096 ./node_modules/vuepress\bin\vuepress.js build

步骤5: 将默认的临时目录和缓存目录添加到 .gitignore 文件中

echo 'node_modules' >> .gitignore
echo '.temp' >> .gitignore
echo '.cache' >> .gitignore

步骤6: 创建你的第一篇文档

mkdir docs
echo '# Hello VuePress' > docs/README.md

步骤7: 在本地启动服务器来开发你的文档网站

yarn docs:dev

VuePress 会在 http://localhost:8080 启动一个热重载的开发服务器。当你修改你的 Markdown 文件时,浏览器中的内容也会自动更新。

增强指南

config.js 配置

通用配置

以下是一份大而全的配置,包括国际化,导航栏,侧边栏,插件

import { defineUserConfig } from 'vuepress'
import { defaultTheme } from '@vuepress/theme-default'
import { searchPlugin } from '@vuepress/plugin-search'
//自定义插件
import sideBarPlugin  from './plugins/sidebar'
export default {
    base: '/jiedoc/',
    cmd: 'node ./node_modules/vuepress/bin/vuepress.js build',
    plugins:[
        sideBarPlugin, //自定义的插件
        searchPlugin({
            // 搜索插件
          }),
        'vuepress-plugin-mermaidjs',  //支持各种图形(流程图)等的插件
        '@maginapp/vuepress-plugin-flowchart', //支持各种图形(mermaid流程图)等的插件
        {
            openMarker:'```mermaid',
            closeMarker:'```',
            scondMarker:'flowchat',
            ignoreSecondLine:false
        }
    ],
    markdown:{
        anchor:{ permalink:false },
        toc:{ includeLevel:[1, 2] },
        extendMarkdown:md => {
            md.use(require('markdown-it-katex'))
                .use(require('markdown-it-footnote'))
                .use(require('markdown-it-ins'))
                .use(require('markdown-it-mark'))
                .use(require('markdown-it-sub'))
                .use(require('markdown-it-sup'))
                .use(require('markdown-it-abbr'))
        },
        lineNumbers: true, // 显示代码行号
    },
    head: [
        //引用一些静态的资源
        ['link', { rel: 'shortcut icon', type: "image/x-icon", href: "/assets/img/favicon.ico" }],
        ['link', { rel: 'apple-touch-icon', type: "image/x-icon", href: "/assets/img/apple-touch-icon.png" }],
        ['link', { rel: 'icon', type: "image/png", sizes: "32x32", href: "/assets/img/favicon-32x32.png" }],
        ['link', { rel: 'icon', type: "image/png", sizes: "16x16", href: "/assets/img/favicon-16x16.png" }],
        ['link', { rel: 'manifest', href: "/assets/img/site.webmanifest" }],
        ['script', { src: '/assets/js/jquery/3.3.1/jquery.slim.min.js' }],
        ['script', { src: '/assets/js/fancybox/3.5.2/jquery.fancybox.min.js' }],
        ['link', { rel: 'stylesheet', type: 'text/css', href: '/assets/css/fancybox/3.5.2/jquery.fancybox.min.css' }]
    ],
    theme:defaultTheme({
        locales: {
            '/':
            {
                selectText: '选择语言',
                selectLanguageName: '简体中文',
                logo: '/assets/img/logo.png',
                lastUpdated: '上次更新',
                smoothScroll: true,
                navbar: 
              [
                   { text: 使用指引', 
                    ariaLabel: 'Apos micro-mall operation guidelines',
                    children:
                        [
                          { text: '基本功能流程指引', link: '/zh-cn/ttttttt.html'},
                          { text: '商城', link: '/zh-cn/t.html'},
                            ]
                         },
                   { text: '业务系统使用指引', 
                    ariaLabel: 'Apos cashier operation guidelines',
                    children:
                        [
                          { text:'商品管理',link: '/zh-cn/gg.html'},
                          { text: '库存管理', link: '/zh-cn/gg1.html'}
                            ]
                         }
                    ],
                sidebar: 'auto'
            },
            '/en-us/':
            {
                selectText: 'Languages',
                selectLanguageName: 'English',
                logo: '/assets/img/logo.png',
                lastUpdated: 'Last Updated',
                smoothScroll: true,
                navbar:
                    [
                        { text: 'Doc Guide', link: '/en-us/product/guide/' }
                    ],
                sidebar: 'auto'
            },
        }
    }),
    locales:  //注意一定要在最外层定义了locales才会显示出选择语言
    {
        '/':
        {
            lang: 'zh-CN',
            title: '测试帮助中心',
            description: ''
        },
        '/en-us/':
        {
            lang: 'en-US',
            title: 'Helper Center',
            description: ' '
        },
    }
}

sidebar

全局sidebar

在config.js中配置sidebar:auto将自动将markdown的2级和3级标题显示在sidebar上,如果需要自定义sidebar可配置为(config.js):

sidebar:[
						{
							text:'新手入门',
							sidebarDepth:1, 
							collapsible:true,
							children:[
								{text:'新手入门指导', link:'/hello/first.md'},
							]
						},
						{
							text:'店铺认证',
							sidebarDepth:1, 
							collapsible:true,
							children:[
								{text:'主题认证教程', link:'/auth/body'},
								{text:'品牌认证教程', link:'/auth/pp'},
								{text:'入驻资质教程', link:'/auth/rz'},
								{text:'店铺命名指引', link:'/auth/dp'},
							]
						},
					],

link表示跳转的markdown文件

注意如果跳转到其他的markdown了,其他markdown没有配置页面级别sidebar将引用全局config.js的sidebar。

页面sidebar

也可以在单独的markdown使用yaml配置

---
 sidebar: 
  - text: "Group"
    children: 
    - text: "SubGroup"
      children: 
        - text: "hello1"
          link: "/group/a/"
        - text: "hello2"
          link: "/group/b/"
  - text: "Group 2"
    children: 
    - text: "gg1"
      link: "/group/a/"
    - text: "gg2"
      link: "/hello/second/"
---
# ttt
## aaa


## bbb

当浏览器访问该页面时自动引用当前页面的sidebar,同时也可以指定sidebarDepth来当前页面sidebar激活的标题显示深度,0表示不显示markdown文件的标题,1表示显示2级标题,2表示显示3级标题,1级标题永远不显示。

布局继承

需求:
所有的markdown页面拓展底部新增一个好用和不好用的评价按钮
效果:
请添加图片描述
vuepress在markdown布局时提供了一些插槽可以用于新增内容,比如page-content-bottom就是内容底部:https://v2.vuepress.vuejs.org/zh/reference/default-theme/extending.html#%E5%B8%83%E5%B1%80%E6%8F%92%E6%A7%BD
.vuepress下新建目录和文件layouts/Layout.vue

<template>
  <ParentLayout>
    <template #page-content-bottom>

    <div class="dialog_backdrop" v-show="dialogTableVisible">
    </div>
    <div class="dialog-wrap" v-show="dialogTableVisible">
      <div class="dialog" style="min-width: 450px; max-width: 75%; transform-origin: 712.5px 465px 0px;">
        您接的没用的原因是:<br/>
        <textarea style="width:80%;height:100px" v-model="useMsg"></textarea><br/>
        <button @click="submitIssue">提交</button>&nbsp;&nbsp;<button @click="dialogTableVisible=false">关闭</button>
      </div>
    </div>

    <div class="divlast">
      <button @click="usefullFun" class="btn" style="border:1px solid red;color:red">
           <span class="btnicon" style="background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAu9JREFUWAnFV81LG0EU/43JpodqjWigiOClFBSpBwkUvVgwNKYg9ODF/gXtSYu30oIfBU+Kl/Yv0IuHQKAaUMhFUMitBaGligexQhClUqTxY/vezG6yxsyukbX7YDKbfb95v7fvzccbgRuKOTwcwdHRM4IPUeuk1mo16rBvtS3qM2hqyomlpSIrvER4AcxU6iGKxQ8Q4hVM84EXXuqF+E3YBUQik2J5+cBtjNYBc3DwHs7P39Hgt2TsvpsRrU6IP6SbRTj8Uays/K2Gq+qA/OqzszQRP602qOZ3QmzCMF5Wi8Y1B8xk8gkuLr4QeVvNRG4DhNgjmymxtvbNCbvigPXled/JbUZ2wjDizkjU2TqZcxV2f7/cJuCeo0ockst6X3JATji/cu4krXxmDjW5pUamwAr9T/LwdrO9ksTrP68Ow3jEqVAR4HX+v8jZOeZiThIhd7jj4wK9rL7JxOPA2Bhjgbk5IJ9Xz7rfm+J5s4pGY8IcGHhOtrI6e1hcBGIxpS4UgJERLVQqasMnOQW8twclQ+wAHyx64bDzl3PjZy+pDd/JKfhONh972b0j/Q+OAB+rQUmrWoZB0dOCZAe4mAhKfgXtwD47wGWUu0xPqxXQ0OCOY20oBExMAPPzQJ1nhrcYkfG0Wl8PdHUBMzNAS4sebhjA+DjQ2wuqC4HLSz1WaTLeWzEDo1EVgTY6qU9OgHQaWF8HDqjcM02guRno6aEtjfa09nbg9BQYHQV2dvQO2FsxI2gv+ETdaz2aNI2N6kzo63OFYXdXRWp72x0HfKbq6E3tx3F3N5BIAB0d5XQcHkIS53LAxgbovHcndxzHpZLMTCQmKZzv3Uf6pBViSqyuyuO4PE2pdKbaf9MnCr0Z5mAuS0oR4P+BFqXsgKxWQ6EXFIk9/u+r2GV5xU2pnAKLTWSzX7l09jUd6mISr7wTMOU1B/iljEQ43E9OTFHj69XthMeyDbLlvAs4jV2ZA06F/SznRRCXU9sBu7+r6/k/b6kihzk07qQAAAAASUVORK5CYII=)"></span>
           有用
      </button>&nbsp;&nbsp;&nbsp;
      <button @click="noGoodFun" class="btn" style="border:1px solid black;margin-left:50px">
        <span class="btnicon" style="background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA0JJREFUWAnFV01IG1EQnt00bUGIB4mWKKSB0kPRCmpMEaEGlDYGCj3oQe9ii3gI9FRooZJDLzm21yD0JBhpCenN60rMpYVASsEEJakpIkhySMSk3zyyIdrdzQ+uebD7fubnm52Z996sRC22hYWF25lMxntxcfFCkqRHEHNUq1UHi2OeRZfFPGmxWL46nc7dra2tMtOaNakZg9vtvlepVN4BZBkAtmb8TAfvGXi/yLL8IR6P/zGS0TXA5/PdyefzbyEcwNNjpMSAVgQt1N/fH4zFYiUtPk0Dal8dgcATLaEO1hR446WWN/4zwOPxPEaco3DhUAdAuiIIyxF0zicSiZ+NTJcM4C8HU/y6wVVANgKPu9ETskrkmCPZImaBMw7rZgzGUnEt6qC3t/c9xsvq3MR+qFgsVnO53C5jiBDUku435p1me7v2FpGUDzgUIgS8z28QnI3tqWGSzCccEkPX9VNTUxSNRsXD42atVX7GFNiTk5PPsO2+6ylm8IGBAUE+Pj4mv9+vxyrW2+HHsf1c5rPdUKOJRMa2DA4OvgHGfT2cdDpNY2NjhMylYDBIh4eHeqxivR1+hKEoTUxMpLA/HxpqNYkIA37JABdXqkkYhmoZu34SGnKaRIQHqjJeXEx0q+XYA900IHsLcUjCiJlWXIB9S16vl6anp2lkZIT6+vrIarXS6ekppVIpUhSFUHhQoVBoRR1fTkmp2UGkapqdnaX19XVyOIxzlrfr5uYmhcNhwj5XxTV7PogkPg4PDg7+whrdei8QCNDS0pJQAl7a3t4mXCSEG43Oz8/JbrfT6OgoLS4u0vDwsOBjb6ytrWkC8yJy78zlctnFbTg+Pv4Ja6/0uHd2doS7Q6EQRSJcqem3ubk5WllZoZOTE1pdXdVnJPqM6uh1S9exzWYj3F4tx9YItUarX8eiIMlmswXE9i6IT7WES6USlctlLVKnax/39/e/sXD9IOLSGXOlU41tyCk1LCEiQqAKd7UoZSO4RMLW8CNDj1Sjrqtnndhp840VMeuuh0AF2tvb+wFmN+bXGQ6FdV79J9A0gBfZSsRpBsMNPPx71Wlj2Q3WdfXLVYWXckBdbOxrFfPN/5w2GsFjs37P/wFZa428Xb42JwAAAABJRU5ErkJggg==)"/>
        没用</button>
    </div>
    </template>
  </ParentLayout>
</template>
<script>
import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
import { usePageData } from '@vuepress/client'

export default {
  name:"layout",
  data(){
    return {
      dialogTableVisible:false,
      usePreKey:"use_",
      useMsg:"",
    }
  },
  components: {
    ParentLayout
  },
  setup() {
    const page = usePageData()
    const frontmatter=page.frontmatter;//这里就可以获取到页面定义的frontmatter
  },
  methods:{
    //如果用后台存储只需要实现重写ifClick和storeKey方法即可
    //判断用户是否点击过
    ifClick(key){
      if(localStorage.getItem(key)!=null){
         alert("您已经赞过了")
         return true;
      }
      return false;
    },
    //用于点击后存储的key
    storeKey(key,msg){  
       localStorage.setItem(key,msg)
    },

    usefullFun(){
      let key=this.usePreKey+window.location.href;
      if(!this.ifClick(key)){
        this.storeKey(key,"")
      }
    },
    noGoodFun(){
      let key=this.usePreKey+window.location.href;
      if(!this.ifClick(key)){
         this.dialogTableVisible = true
      }
    },
    submitIssue(){
      let key=this.usePreKey+window.location.href;
      if(!this.ifClick(key)){
        if(this.useMsg=="" ||this.useMsg.trim()==""){
          alert("请输入您的意见")
          return;
        }
        this.storeKey(key,this.useMsg)
        this.dialogTableVisible = false;
      }
    }
  }
}
</script>
<style scoped>
  .divlast{
    margin-top: 50px;
  }
  .spanbg{
      
  }
  .btnicon{
    display: inline-block;
    width:16px;
    height:16px;
    background-size: 16px 16px;
  }
  .btn{
     vertical-align: middle;
     width:120px;
     height:60px;
     cursor: pointer;
     background-color:white;
     border-radius:15px;
  }
  .dialog_backdrop{
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.6);
    height: 100%;
    z-index: 1050;
  }
  .dialog-wrap {
    position: fixed;
    overflow: auto;
    top: 30%;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1050;
    -webkit-overflow-scrolling: touch;
    outline: 0;
    text-align: center;
    font-size: 0;
    white-space: nowrap;
}
.dialog {
    position: relative;
    display: inline-block;
    vertical-align: middle;
    text-align: initial;
    background-color: #fff;
    border-radius: 4px;
    padding: 20px;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    font-size: 14px;
    white-space: normal;
}

</style>

新增.vuepress/client.js

import { defineClientConfig } from '@vuepress/client'
import Layout from './layouts/Layout.vue'
export default defineClientConfig({
  enhance({ app, router, siteData }){
    
  },
  setup(){
    
  },
  layouts: {
    Layout
  }
})

插件开发

需求:
需要开发一个插件,该插件可以定义一批页面的侧边栏菜单都是同一个,支持设置页面扫描标题深度.

  1. group1下自定义两个标题,这两个标题自动扫描2级别标题。
  2. group3直接指向一个markdown扫描2-3级别标题
  3. 中文文件名这个兼容中文,能扫描到2级别标题。

注意在页面设置sidebar点击有link的地址是会跳转的,如果浏览器的访问路径和你导航栏的link一致的事情,会自动高亮,才会显示2-3级别标题。

效果如下:
请添加图片描述

vuepress架构

在开发插件前需要了解vuepress整个构建的生命周期
在这里插入图片描述
上图展示了 VuePress 的简要架构:

  • Node App 会生成临时文件,包括页面、路由等。

  • Bundler 会将 Client App 和临时文件一起进行打包,就像处理一个普通的 Vue SPA 一样。
    作为开发者,你必须要意识到 VuePress 分为两个主要部分: Node App 和 Client App ,这一点对于开发插件和主题来说都十分重要。

  • 插件或者主题的入口文件会在 Node App 中被加载。

  • 客户端文件会在 Client App 中被加载,也就是会被 Bundler 处理。比如组件、客户端配置文件等。

核心流程与 Hooks

在这里插入图片描述
上图展示了 VuePress 的核心流程以及 插件 API 的 Hooks :

在 init 阶段:

主题和插件会被加载。这意味着插件需要在初始化之前使用。
由于我们要使用 markdown-it 来解析 Markdown 文件,因此需要在加载页面文件之前创建 markdown-it 实例:

  • extendsMarkdownOptions Hook 会被调用,用以创建 markdown-it 实例。
  • extendsMarkdown Hook 会被调用,用以扩展 markdown-it 实例。
    页面文件会被加载:
  • extendsPageOptions Hook 会被调用,用以创建页面。
  • extendsPage Hook 会被调用,用以扩展页面对象。

在 prepare 阶段:

  • 临时文件会被生成,因此所有和客户端文件相关的 Hooks 会在此处调用。

在 dev / build 阶段:

  • Bundler 会被加载:
  • extendsBundlerOptions Hook 会被调用,用以生成 Bundler 的配置。
  • alias Hook 和 define Hook 会被用在 Bundler 的配置中,所以它们会在此处调用。

具体参见api请参考官网:
https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html
因为我们需要在编译文件之前需要插入sidebar,所以可以在onInitialized注入
在.vuepress下新建plugins目录新建文件sidebar.js

//自定义了一个.vuepress/sidebar.json,用来定义sidebar
import sidebar from "../sidebar.json"
/**
 * 对配置文件中文链接进行转码,否则有匹配问题
 * @param {*} sidebar 
 */
function encodeLink(sidebar){
    for(let sb of sidebar){
        if(sb.link){
            console.log(sb.link+"--->")
            sb.link=encodeURI(sb.link)
            console.log(sb.link)
        }
        if(sb.children && sb.children.length>0){
            encodeLink(sb.children)
        }
    }
}
/**
 * 将sidebar的所有链接加入到matchPage(所有的link页面应用相同的sidebar)
 * @param {} sidebar 
 * @param {*} list 
 */
function getAllLink(sidebar,list){
    for(let sb of sidebar){
        if(sb.link && list.indexOf(sb.link)<0){
            list.push(sb.link)
        }
        if(sb.children && sb.children.length>0){
            getAllLink(sb.children,list)
        }
    }
}
/**
 * 获取某个link对应的某个属性,主要是为了获取某个页面的sidebarDepth
 * @param {*} sidebar 
 * @param {*} link 
 * @param {*} proName 
 * @returns 
 */
function getLinkPro(sidebar,link,proName){
    for(let sb of sidebar){
        if(link==sb.link && sb[proName]!=null){
            return sb[proName]
        }else if(sb.children && sb.children.length>0){
            let s=getLinkPro(sb.children,link,proName)
            if(s!=null){
                return s;
            }
        }
    }
    return null;
}
export default  {
    name: 'vuepress-plugin-usermenu',
    onInitialized(app) {
      for(let menu of sidebar.sidebarList){
            encodeLink(menu.sidebar)
      }
      app.pages.forEach((item) => {
        let itemPath=item.path
        for(let menu of sidebar.sidebarList){
            //将所有sidebar下的link的sidebar都设置为相同。
            if(menu.matchAllSideBarLink){
                menu.matchPage=menu.matchPage||[]
                getAllLink(menu.sidebar,menu.matchPage)
            }
            if(menu.matchPage.indexOf(itemPath)>=0){
                //查询是否有sibar对应link的sidebarDepth,存在应用,不存在获取全局
                let sidebarDepth=getLinkPro(menu.sidebar,itemPath,"sidebarDepth")
                if(sidebarDepth!=null){
                    item.frontmatter.sidebarDepth=sidebarDepth;
                }else if(menu.sidebarDepth!=null){
                    item.frontmatter.sidebarDepth=menu.sidebarDepth;
                }
                item.frontmatter.sidebar=menu.sidebar;
            }
        }
      });
    }
  }

.vuepress/sidebar.json内容:

{
    "sidebarList":[{
        "matchPage":["/zh-cn/t.html"],
        "matchAllSideBarLink":true,
        "sidebarDepth":1,
        "sidebar":[
            {
                "text": "Group 1",
                "collapsible": true,
                "children": [
                {
                    "text": "Active on /foo/",
                    "link": "/zh-cn/sidebar/tt.html"
                },
                {
                    "text": "Always active",
                    "link": "/zh-cn/sidebar/tt1.html"
                }
              ]
            },
            {
                "text": "Group 2",
                "collapsible": true,
                "children": [
                {
                    "text": "Active on /foo/",
                    "link": "/zh-cn/sidebar/tt2.html",
                    "sidebarDepth":1
                }
              ]
            },
            {
                "text": "Group 3",
                "collapsible": true,
                "link": "/zh-cn/sidebar/tt3.html",
                "sidebarDepth":2
            },
            {
                "text": "中文文件名",
                "collapsible": true,
                "link": "/zh-cn/sidebar/中文测试.html",
                "sidebarDepth":1
            }
          ]
    }]
}

config.js中引用插件

import sideBarPlugin  from './plugins/sidebar'
export default {
    base: '/jiedoc/',
    cmd: 'node ./node_modules/vuepress/bin/vuepress.js build',
    plugins:[
        sideBarPlugin
    ],

相关文章:

  • HDLBits: 在线学习 SystemVerilog(十一)-Problem 60-64
  • Linux环境基础开发工具使用
  • IDEA使用swing创建应用程序
  • 22.0、C语言数据结构——二叉排序树
  • GSW同态加密方案学习
  • Java -- 每日一问:Exception 和 Error 有什么区别?
  • 使用 Typora 画图
  • Gradle 入门说难也不难,说简单吧也不简单~
  • 层次聚类分析及代码实现
  • 学生选课系统 前后端分离 vue springboot
  • 网络安全比赛A模块任务书
  • IP报文在阿里云上的神奇之旅:同地域内云上通信
  • 基于网络安全的Docker逃逸
  • Android——一个简单的音乐APP(二)
  • 在Eclipse 中使用 Maven 创建雅加达 EE 应用程序
  • [deviceone开发]-do_Webview的基本示例
  • Angular 响应式表单 基础例子
  • HTML中设置input等文本框为不可操作
  • HTTP那些事
  • Javascripit类型转换比较那点事儿,双等号(==)
  • javascript 哈希表
  • JavaScript标准库系列——Math对象和Date对象(二)
  • leetcode讲解--894. All Possible Full Binary Trees
  • React Native移动开发实战-3-实现页面间的数据传递
  • Web设计流程优化:网页效果图设计新思路
  • 分布式任务队列Celery
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 人脸识别最新开发经验demo
  • 使用Swoole加速Laravel(正式环境中)
  • 一、python与pycharm的安装
  • #FPGA(基础知识)
  • #ubuntu# #git# repository git config --global --add safe.directory
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (三)模仿学习-Action数据的模仿
  • (三)终结任务
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)memcache、redis缓存
  • (转)母版页和相对路径
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转载)利用webkit抓取动态网页和链接
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • **CI中自动类加载的用法总结
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .net core控制台应用程序初识
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .net 简单实现MD5
  • .net 使用ajax控件后如何调用前端脚本
  • .NET下ASPX编程的几个小问题
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • @TableLogic注解说明,以及对增删改查的影响
  • [1159]adb判断手机屏幕状态并点亮屏幕