npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

create-vitepress-demo

v0.1.23

Published

搭建一个带有专业 demo 演示能力的 vitepress 项目

Downloads

16

Readme

create-vitepress-demo

搭建一个带有专业 demo 演示能力的 vitepress 项目,查看相关介绍示例站点

npm init vitepress-demo
# or
yarn create vitepress-demo

背景

vitepress 凭借着 vite 的秒级启动速度、markdown-it 的强大扩展能力、天然支持 vue3 在文档圈迅速流行开来,使用 vitepress 做 vue3 组件库文档也已经非常流行。笔者也有幸实践过一次,在这里记录一下。

首先 vitepress 的 markdown 扩展能力 无疑是极香的,我觉得及其舒适的有以下几点:

笔者使用 vitepress 搭建业务组件库的文档,依赖 element-plus,根据 vitepress 文档,写了一个简单的 DemoContainer 组件用于包裹 Demo。

vitepress 缺点

随着时间的推移和组件数量的累积,现有的开发方式逐渐暴露出来一些问题:

  1. 无法演示全屏组件(height:100vh)
  2. 无法演示路由组件(耦合 vue-router,如 menu-item)
  3. vitepress 有一些全局样式挺烦的,经常干扰到 Demo,比如:
table {
  display: block;
  border-collapse: collapse;
  margin: 1rem 0;
  overflow-x: auto;
}
  1. 引用 demo 太繁琐,而且易出错(引用一个 Demo 要 15 行代码)
<script setup>
  // 这个 demo1 重复了多次,复制修改的时候容易漏掉
  import Demo1 from './demo/demo1.tsx'
</script>

<DemoContainer title="基本使用">
<ClientOnly>
<Demo1 />
</ClientOnly>
  <details>
    <summary>查看代码</summary>

<!-- 这个源码引用方式是 vitepress 提供的 -->

<<< packages/query-table/demo/demo1.tsx

  </details>
</DemoContainer>

前两点很容易想到用 iframe 是完美的解决方案,而且还能顺手解决第三点。

总结一下缺点有两个:

  1. Demo 引用繁琐
  2. 缺少 iframe 模式

前置介绍

涉及到的框架之间的关系

vitepress 本质上是一个 vite 插件,使用它开发的文档网站效果相当于 vue3 + vite 的 ssr 项目,它在内部帮你把所有逻辑都封装好了,你只需要写 markdown 就行。

对 markdown 的扩展能力是基于 markdown-it 写了很多 markdown-it 插件。源码里所写的 markdown 文档最终都会转成 vue 组件,原理如下:

vitepress 运行 vue 组件原理

把 markdown 编译成 html 字符串,把 html 字符串拼凑成一个 vue 字符串,交给 vue-loader,处理成一个 vue 组件挂载到页面上。

调研

  • dumi 效果完美,可以说是标杆了。但是不支持 vue
  • storybook 并不是想要的 iframe 模式,也不行。
  • vitepress-for-component 是 fork 了 vitepress(因为 vitepress 目前未支持插件),提供了 demo 演示能力,但是没有 iframe 模式。
  • element-plus 也是用 vitepress , 但是也没有 iframe 模式。而且它的引用方式不清晰、不灵活。
  • 自研,舍不得 vitepress 的 markdown 扩展能力。不到走投无路不要自研。

最终决定尝试通过修改配置和自定义插件解决。

研发

demo 引入简化

参考了 element-plusvitepress-for-component ,定制一个 markdown-it 插件修改 html 编译结果。

引入方式设计

element-plus 的引入方式不够清晰,也不够灵活。采用相对路径更清晰更灵活:

<demo src="./demo-example.vue" title="Demo演示" desc="这是一段描述" />

当然 container 的方式也顺便兼容下,里面的内容可以写写 markdown:

::: demo src="./demo-example.vue" title="Demo 演示"

这是一段描述,可以用 `Markdown` 来写

:::

插件思路

遇到特定标记(如:<demo src=xxx ... />),根据标记拼接字符串,将来会被插入到 vue template 里相应位置,通常情况下拼接 <DemoContainer ... ><Demo/></DemoContainer>,如果标记了以 iframe 模式运行 demo,则拼接一个<iframe src=xxx ... />

此过程还会包括如下步骤,感兴趣可以看源码

  • 插入 import statement 语句
  • 记录 demoId 和入口的对应关系

这一步把引入 Demo 的过程从原来的 15 行代码之间简化到 1 行。

iframe 模式

运行时动态创建 iframe

试过在 DemoContainer 里 document.createElement('iframe'),但是没有成功:

  • 获取到 slot 内容的时候,组件代码已经运行了,此时放入沙箱已经晚了。
  • 获取到 demo 源代码交给 vue-compiler 编译这个编译工作运行时做不现实。

微前端

这明显更复杂了,而且和动态 iframe 具有相同的问题。

在 vite 配置里直接加入口

第一个念头是 vite.config 里添加入口,因为 vite 就是支持多个 html 的。

实际操作之后发现根本行不通:vitepress 接管路由了,访问任何路径都会经过 vitepress router 处理。即使设置 base,也会收到 vitepress 的提醒。

image

查看源码得知,devServer 拦截了所有 html 请求,根据请求路径动态生成 html:

const vitePressPlugin: Plugin = {
  name: "vitepress",
  // ...
  configureServer(server) {
    if (configPath) {
      server.watcher.add(configPath);
    }

    // serve our index.html after vite history fallback
    return () => {
      server.middlewares.use((req, res, next) => {
        if (req.url!.endsWith(".html")) {
          res.statusCode = 200;
          res.end(`
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="description" content="">
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/@fs/${APP_PATH}/index.js"></script>
  </body>
</html>`);
          return;
        }
        next();
      });
    };
  },
};

定制 devServer 中间件

上面 vitepress 的操作给了我灵感,我也写个 devServer 中间件,根据请求路径动态生成 html。试了一下,还真成功了,流程如下:

  1. 上面提到定制的 markdown-it 修改 html 输出为 <iframe src=xxx />
  2. 浏览器会向 devServer 请求 iframe 地址
  3. devServer 中间件拿到这个请求,如果命中约定格式,比如 /^\/~demos\/(?<demoId>\w+)\.html$,则拼接一个可以运行此 demo 的 html 字符串给浏览器。
    1. 找到 demoId 对应的入口文件 demoEntry
    2. 写一些运行时 script 作为入口
      1. 通过入口地址从 vite 请求编译结果:const module = await import('@fs/${demoEntry}')
      2. 约定 module.default 导出自动挂载的组件。否则视为 demoEntry 自行挂载

流程图

构建模式

由于构建模式没有 devServer,所以上述 devServer 也不会生效。

vitepress 和处理请求一样一刀切,没有留余地,无法通过 vite 添加入口。

所以只能在 vitepress build 之后再跑一遍 vite build -c=xxx

流程图

总结

由于这套刚刚出炉,所以有很多的优化点,发出来权当抛砖引玉了。

因为 vitepress 暂时没有插件机制,所以这套方案也没什么抽象的点子,暂时作为一个样板仓库。

如果你有好的点子或者优化的地方,请联系我。