文档库建设踩坑实记录

937 阅读4分钟

vitepress

vitepress 官网 VitePress | Vite & Vue Powered Static Site Generator 什么是 VitePress? | VitePress 上手简单易用。

用到了 vitepress-demo-editor 插件 github.com/liyao1520/v…,可以将源码 lib 文件夹放入到项目中方便直接调试。

vitepress-demo-editor

vitepress-demo-editor 插件里面用到了 Demo 组件和自定义的 Compiler 类,本质是使用的 vue/compiler-sfc 库里面的 parse 以及compileTemplate\compileScript\compileStyle 方法。vue/compiler-sfc 为用户提供了将 vue 文件编译为 js 文件的能力。

compilerSFC 中,

  • compiler.parse() 函数,将 vue 文件分为3部分,template 块、script 块和 scriptup 块、多个 style 块。这一块做的是解析,并没有对代码进行编译。

  • compilerTemplate() 对 template 进行编译,得到 render 函数

  • compilerScript() 对 script 进行编译

  • compiler.compileStyle() 对 style 进行编译

得到 vue 代码片段编译后的 js 代码片段后,在页面中动态生成 script 标签,通过 Vue 提供的 h 方法和 render 方法,将代码渲染到页面中。一个简化的代码页面如下:

<div id="app"></div>
<script type="importmap">
  {
    "imports": {
      "vue": "https://unpkg.com/vue@3.2.33/dist/vue.esm-browser.js",
      "@vue/compiler-sfc": "https://unpkg.com/@vue/compiler-sfc@3.2.47/dist/compiler-sfc.esm-browser.js"
    }
  }
</script>
<script type="module">
    import { parse, compileTemplate, compileScript, rewriteDefault } from '@vue/compiler-sfc'
    // 引入 vue 字符串
    import { code } from './vue.js'
    const parsedResult = parse(code)
    const scopeId = 'testId'
    const templateRes = compileTemplate({id: scopeId, source: parsedResult.descriptor.template.content})
    const scriptRes = compileScript(parsedResult.descriptor, {id: scopeId})
    // 转为 __sfc_main__ 对象
    const defaultRes = rewriteDefault(scriptRes.content, "__sfc_main__")
    // script 的内容
    const scriptContent = `import { render as _render, h } from 'vue';
    ${templateRes.code}
    ${defaultRes}
    __sfc_main__.render = render;
    _render(h(__sfc_main__), document.querySelector('#app'))`
    // 创建 script 标签动态插入 HTML 中
    const scriptEL = document.createElement('script')
    scriptEL.type = "module"
    scriptEL.innerHTML = scriptContent
    document.body.appendChild(scriptEL)
</script>

其中,使用 vue 的两个函数:

  • h(component) 函数,将一个组件生成 VNode

  • render(vnode, container) 函数,将 vnode 渲染到容器 container 中

这里要注意一下 vue 的版本问题,使用最新版 3.2.47 渲染有问题,会报 warn:

使用 3.2.33 没有该问题。

支持导入

如果想要在 vitepress-demo-editor 示例源码中使用其他库,例如使用 axios,需要使用 addImportMap 方法引入:

// .vitepress/theme/index.js
import { vuePlugin, addImportMap } from "vitepress-demo-editor";
import axios from "axios";

export default {
  // ...otherConfig
  enhanceApp({ app }) {
    app.use(vuePlugin);
    addImportMap("axios", axios);
  },
};

在 markdown 中使用

:::demo
```vue
<template>...</template>
<script setup>
// 使用
import axios from "axios";
</script>
```
:::

ant design vue

首先一点是 vitepress 本身是支持 vue 代码编写的。在 vitepress 中,每个 markdown 文件会被编译成 HTML 文件,然后作为 Vue Single-File Component 进行处理,这意味着我们在 markdown 文件中可以加入 script 标签,写 vue 组件。如下

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
## Markdown Content
The count is: {{ count }}
<button :class="$style.button" @click="count++">Increment</button>
<style module>
.button {
  color: red;
  font-weight: bold;
}
</style>

同样,ant design vue 组件在 vitepress 中通过自定义 theme 也是可以在 markdown 文件中直接使用的。

// theme/index.js
import AntD from 'ant-design-vue'
import "ant-design-vue/dist/antd.css";
import DefaultTheme from 'vitepress/theme'

export default {
  ...DefaultTheme,
  enhanceApp({ app }) {
    app.use(AntD)
  }
}

可以在 markdown 中使用 AntD 组件:

<script>
import { Button } from 'ant-design-vue'
</script>
## Markdown Content
<Button type="primary">primary</Button>

如果想要在可以交互使用 AntD 组件,即页面的代码框中修改 AntD 组件,渲染页面实时修改的效果,vitepress-demo-editor 插件中,也可直接使用,需要注意的是,要使用 AntD 组件的 name 值,如下:

业务组件

在 vitepress + vitepress-demo-editor 中怎么引入自己的业务组件呢?

我们知道 app.use(AntD) 方法会执行 AntD 的 install 方法,install 方法里,会将 AntD 组件一个个注册到 vue 的组件(以 Button 为例)中:

app.component(_button.default.name, _button.default);

所以针对业务组件也可以将其注册到 vue 组件中,在 theme 中实现

// theme/index.js
import DefaultTheme from 'vitepress/theme'
import { RoleSelect, } from '@xhs/people-ui'

export default {
  ...DefaultTheme,
  enhanceApp({ app }) {
    app.component("RoleSelect", RoleSelect);
  }  
}

一些问题

  • 本地联调时业务组件中的异步数据请求统一拦截需要额外拦截

  • vitepress 打包时,会出现 global 等变量没有的情况,依赖的包无法正确编译,修改成本较高

Vue 文件是如何被转换并渲染到页面的?_vue文件是怎么转换的_蚂蚁二娘的博客-CSDN博客

vue3 -- @vue/compiler-sfc 单文件转换工具 -- 学习笔记 - 掘金

vue.js - 「将 Vue SFC 编译为 ESM 」探索之路 - 小蘿蔔丁 - SegmentFault 思否

VuePress

vuepress 官网 Home | VuePress 找到符合需求的插件是 vuepress-plugin-md-enhance,支持交互式 vue 代码演示,看起来似乎很完美,然而在支持 ant design vue UI 组件库的时候问题多多。

vuepress-plugin-md-enhance 因为只支持外部 Import Map 设置,

首先得找到 ant design vue 组件的 ES Module 形式的 cdn 形式,主要途径有两种:

主要的问题是,通过 cdn 引入组件,组件体量太大,同时受到网络限制,网速慢,体验差。还有就是 cdn 引入的组件不稳定,难以获得底层依赖模块的 bug 快速修复支持。

另外 vitepress 存在的业务组件本地联调拦截的问题,vuepress 也存在。

自己实现

利用 @vue/repl 自己实现, @vue/repl 可以实现 vue 代码交互式演示功能。方便代码修改。