现在 Vite 的生态逐渐完善,今天给大家介绍一款 React 的组件/应用文档编写神器:vite-plugin-react-pages
目前主流搭建文档站点的方式:
- Gatsby - 以 GraphQL 为核心,功能相当完善,插件生态丰富。但学习曲线高(React)
- Docusaurus - Meta 公司出品。功能强大,与 Gatsby 相似(React)
- dumi - 一款 UmiJS 生态中的组件开发文档工具(React)
- Nextra - 一个基于 Next.js 的静态站点生成器。(React)
- VuePress - 包含由 Vue 驱动的主题系统和插件 API,另一个部分是为书写技术文档而优化的默认主题(Vue)
- VitePress - 对 VuePress 进行了不少的改进。VitePress 旨在降低当前 VuePress 的复杂性,并从其极简主义的根源重新开始。(Vue)
除了 VitePress 之外,其他都是用 Webpack 作为开发服务器。一个只有几页的简单文档站点启动开发服务器所花费的时间变得难以忍受。即使是 HMR 更新也可能需要几秒钟才能反映在浏览器中。Vite 的出现很好地解决了这些问题:近乎即时的服务器启动、仅编译所服务页面的按需编译以及闪电般快速的 HMR。
作为 Vite 的基础上的文档方案, VitePress 只支持 Vue。
翻阅 Vite 的官方库列表,偶然发现了一款 star 数量仅 100 多的文档解决方案 vite-plugin-react-pages。开始用试试水的心态去去体验一把,结果发现相当好用。就是知道的人太少了。现在,我们从使用者的角度去解析它好用的功能。
特性
vite-plugin-react-pages(vite-pages) 是一个由 vite 提供支持的 React 应用程序框架。它非常适合:
- 博客网站
- 组件库或产品的文档站点
- React 组件的 Demo 演示
它提供了许多帮助开发人员快速构建 React App 的功能:
- 很棒的开发体验。即使有很多页面,也可以极速启动本地开发服务器。HMR 适用于 React 和 MDX,因此可以在编辑代码时获得即时的更新反馈。
- 基于文件系统的路由。通过遵循简单的文件系统路由约定,可以轻松创建、定位和开发页面。也可以配置 vite-pages 如何查找页面文件,以便支持任何文件结构的项目。
- 支持 MDX。可以使用 “普通 React” 或 MDX 编写内容。“普通 React” 与 Markdown 灵活组合,更具可读性和易于编辑。
- 强大的主题定制。vite-pages 本身不渲染任何具体的 DOM 节点。使用默认的官方主题库,也可以很容易自己创建一个主题,可以自定义页面上的任何内容。
- 基于页面的自动代码拆分。只根据访问需要加载当前的页面数据。
- 支持组件与代码预览。在看到组件功能之后,可以通过代码查看组件使用方式。
- 支持 typescript 类型提取。将 typescript 的类型定义和注释生成为组件 API 文档表格。
- 支持 SSR 开箱即用。通过在构建时将应用预渲染为 HTML,用户可以在加载任何 JS 之前看到内容。
使用
vite-pages 默认提供了三种模板,可以选择初始化app(应用)、lib(组件库)、lib-monorepo
可以通过命令创建一个自己的初始仓库
- npm init vite-pages [仓库名] --template [模板名]
我们执行 npm init vite-pages library-demo --template lib 生成了一个典型的 Vite 结构的项目,有熟悉的 vite.config.ts、pages 文件夹等
该插件所有明确的依赖包作用:
- @mdx-js/mdx MDX的实现
- @mdx-js/react 作为 MDX 的 React 实现。
- vite-plugin-mdx Vite 支持 MDX 的插件
- vite-plugin-react-pages 文档插件核心实现
- vite-pages-theme-doc 官方的文档主题。依赖 react-router-dom ^5.0.0 版本
pages 目录为文档入口。文件式路由约定用 $ 符号的文件名结尾来识别为一个文档页面。
.ts|.tsx|.js|.jsx|.md|.mdx 只要 $ 是扩展名前的最后一个字符,所有文件扩展名都有效。
pages 目录中还存在一个特殊的入口 _theme.tsx 用来配置当前文档的主题,vite-pages 默认使用了 vite-pages-theme-doc 官方主题。如果自定义需求不大,可以通过配置官方主题的参数来实现常规的功能。比如配置 logo、顶部链接、左侧菜单等。
我们来看看首页文档代码
- ---
- title: 首页
- ---
-
- import README from '../README.md'
-
引入了 README.md 文档作为首页内容,在 MDX 里,文档和组件契合得相当完美。
接下来新建一个文档,粘贴一篇之前写过的主题
菜单名称会优先使用 md 文件中的一级,否则使用文件名。也可以在 md 文件的首行加入以下代码设置:
- ---
- title: mac-scrollbar
- group: components
- subGroup: general
- ---
group 一级分组,会显示在头部列表区域;subGroup 二级分组,会将左侧菜单分组显示。
可以通过 _theme.tsx 更改分组名和分组顺序
- sideNavs: (ctx) => {
- return defaultSideNavs(ctx, {
- groupConfig: {
- components: {
- general: {
- label: '通用',
- order: 1,
- },
- dataRecord: {
- label: '数据录入',
- order: 2,
- },
- },
- },
- });
- };
用它写文档体验相当不错,markdown 主题默认自带 github 样式,排版清晰。热更新做得非常棒,更改后即时就能看到效果。
接下来我们用它来做一个组件看看效果如何,做一个最基础的按钮组件
首先定义组件类型:
- interface ButtonProps extends React.HTMLAttributes
{ -
- type?: 'primary' | 'default' | 'text';
-
- size?: 'large' | 'middle' | 'small';
-
- loading?: boolean;
- }
简单实现:
- import React from'react';
- import { ButtonProps } from'./types';
- import styles from'./style.module.css';
-
- const Button = ({ className, type, ...props }: ButtonProps) => {
- return (
-
- {...props}
- className={[styles.button, type === 'primary' && styles.primary, className].filter(Boolean).join(' ')}
- />
- );
- };
-
- exportdefault Button;
接着写一些 Demo
-
-
- import React from'react';
- import { Button } from'my-lib';
-
- const Demo1 = () => {
- return;
- };
-
- exportdefault Demo1;
最后,在文档中引入这些 Demo
- ---
- title: Button
- subGroup: general
- ---
-
- # Button
-
- 一个简单的按钮组件
-
"./demos/demo1.tsx" /> -
"./demos/demo2.tsx" />
看看效果:
用它来调试组件相当省心,调试完成等于文档也写好了,还有自动提取的代码演示,做到了开箱即用。
组件很重要的一部分是 API 文档,要是能自动从 typescript 注释中提取就好了。没错,vite-pages 支持了这项功能。
只需要在 MDX 中引入 TsInfo 组件,并设置类型地址和名称:
"./types.ts" name="ButtonProps" />
将 ts 类型提取出一个表格,能识别必填/描述/默认值等。有了这个功能就不用担心文档和代码割裂的问题了
目前这个插件官方主题自带趋向于组件文档模式,基本功能齐全,没有多余花哨的功能。如果想实现组件在线编辑预览的话,可以 fork vite-pages-theme-doc 这个库,然后可以改为自己想要的样式,添加 react-live 支持在线实时编辑预览。
官方默认没有提供博客主题。自己实现不难,因为 vite-plugin-react-pages 不渲染任何 DOM 节点,一切渲染相关都可以从 vite-pages-theme-doc 这个库中修改。
小提示
总结当前使用情况来看,使用了该插件会使 Vite 自动依赖预构建失效。你需要手动将 node_modules 中的包添加到 optimizeDeps.include 中。比如 vite-pages-theme-doc 、lodash 等,否则会出现打开页面的时候重复刷新的情况。