踩坑分享:Vite Plus 最佳实践
- rattail 提供面向 Vite+ 的开箱即用工程化预设,统一 lint、fmt 等配置
- 内置 140+ 类型安全且可 Tree-shake 的工具函数及渐进式请求库
- 通过 Agent Skills 增强 AI 编程助手对项目上下文的理解,减少代码幻觉
写在前面
掘金的同学们大家好呀,作者是 [Varlet UI](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Fvarlet "https://github.com/varletjs/varlet") 的作者。掘金文章已经一年没更新了,去年跳槽到了一家创业公司负责前端架构工作,写文章这件事就一直搁置了。最近稍微缓过来了一点点(其实还是压力很大...),但不妨碍今天来给大家分享一下我们最新的开源项目 [rattail](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail "https://github.com/varletjs/rattail")。
先聊聊 Vite Plus
上个月 `VoidZero` 正式以 `MIT` 协议开源了 [Vite+](https://link.juejin.cn/?target=https%3A%2F%2Fviteplus.dev "https://viteplus.dev"),它把 `Vite`、`Vitest`、`Oxlint`、`Oxfmt`、`Rolldown`、`tsdown` 统一收拢到了一个 `vp` 命令下面,一套工具链覆盖 `dev`、`build`、`test`、`lint`、`fmt`、`pack` 等所有工程化环节。作者第一时间就把 `varlet` 周边的项目迁移到了 `Vite+` 上面试试水,迁移下来发现效果特别好。以前那些散落在各处的 `eslint` 配置、`prettier` 配置、`lint-staged` 配置、`commitlint` 配置可以统一收拢到一个 `vite.config.ts` 里面,项目根目录一下子干净了不少(以前打开根目录看到十几个 `.xxxrc` 文件的日子终于结束了)。而且因为工具链统一了,AI Agent 在理解项目配置的时候幻觉也少了很多。
公司项目迁移
既然体验这么好,作者就决定把公司内部的前端项目也都迁移到 `Vite+` 上。迁移的过程中也让作者重新审视了一下 `rattail`,我们在 `varlet` 生态里积累了大量的工具函数、请求库、校验规则工厂、CLI 工具链,之前一直是分散在各个包里的,正好借这次机会做一次大整合,于是就有了 `rattail 2.0`——一个面向 `Vite+`、对 `AI Agent` 非常友好的前端工具链。140+ 工具函数、渐进式请求库、链式校验规则工厂、CLI 工具链、类型安全枚举,`pnpm add rattail` 一条命令全部拉齐。
目前作者也在公司项目中全面使用了 `Vite+` + `rattail` 这套技术栈,体验下来非常舒服。另外值得一提的是,这次 `rattail 2.0` 的迁移和开发过程中,作者大量使用了 `AI` 辅助编程,包括工具函数的编写、单元测试的补全、文档的生成等等,效率提升非常明显。配合 `rattail` 提供的 `Agent Skills`,AI Agent 能够很好的理解项目上下文并正确使用 `rattail` 的 API,整个工作流跑下来还是相当丝滑的。后续在业务开发中也明显感觉到,因为 `rattail` 把工具函数、请求库、校验规则这些东西 all in one 了,AI 在生成代码的时候幻觉变得特别少,而且很会按照规范做事。
相关链接
- 官方文档: [rattail.varletjs.org](https://link.juejin.cn/?target=https%3A%2F%2Frattail.varletjs.org "https://rattail.varletjs.org")
- 仓库地址: [github.com/varletjs/ra…](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail "https://github.com/varletjs/rattail")
特性一览
- ⚙️ 面向 [Vite+](https://link.juejin.cn/?target=https%3A%2F%2Fviteplus.dev "https://viteplus.dev") 的开箱即用配置预设
- 🔧 CLI 工具链,支持发布、日志、Git Hooks、Commit Lint、API 生成
- 🧰 140+ 工具函数,覆盖通用、字符串、数字、数组、对象、数学等场景
- 🚀 基于 axios 的渐进式请求工具,支持 Vue 组合式 API
- 📏 链式校验规则工厂,适配任意 UI 框架
- 🏷️ 类型安全的枚举工具
- 🤖 提供 Agent Skills,帮助 AI 编程助手理解和使用 Rattail
- 🌲 可 Tree-shake,轻量,TypeScript 完整类型支持
- 💪 90%+ 单元测试覆盖率
下面作者挑几个有意思的能力展开聊聊。
Vite+ 配置预设
做过前端工程化的同学应该都有体会,每次新项目光配 `eslint` 和 `prettier` 这些东西就够喝一壶的了,配好了还得处理各种冲突。`rattail` 内置了面向 `Vite+` 的开箱即用预设,一个 `vite.config.ts` 搞定 `lint`、`format`、`staged`、`git hooks` 等所有工程化配置。
import { lint, fmt, staged, clean, hook, defineConfig } from 'rattail/vite-plus'
export default defineConfig({
lint: lint(),
fmt: fmt(),
staged: staged(),
rattail: {
clean: clean(),
hook: hook(),
api: {},
release: {},
changelog: {}
},
})之前作者为了配这些东西写了好几个配置文件,现在一个文件就够了(少写代码是第一生产力)。
CLI 工具链
安装 `rattail` 后会注册一个 `rt` 命令,覆盖了作者日常开发中最常用的几个场景。
# 清理产物
rt clean
# 安装 git hooks
rt hook
# 发布
rt release
# 生成 changelog
rt changelog
# 从 OpenAPI 生成 API 模块
rt api这些命令都支持通过 `vite.config.ts` 中的 `rattail` 字段进行配置,也就是说项目根目录不需要再多出一堆 `.xxxrc` 文件了。这一点作者是比较在意的,毕竟谁也不想打开项目根目录看到十几个配置文件吧(有些项目根目录比 `node_modules` 还热闹)。
140+ 工具函数
`lodash` 大家都耳熟能详了,`rattail` 里的工具函数覆盖的场景和 `lodash` 类似,包括`类型判断`、`数组`、`对象`、`字符串`、`数学`、`函数`、`集合`、`文件`等分类,用法就不逐个列举了。和 `lodash` 不同的是,这些函数从第一天就是用 `TypeScript` 写的,类型推导是第一优先级,全部可 `Tree-shake`。除了 `lodash` 风格的工具函数以外,`rattail` 还内置了一些前端项目中常用的实用工具,比如 `sumHash` 计算哈希、`uuid` 生成唯一 ID、`mitt` 事件总线、`duration` 时间格式化、`storage` / `cookieStorage` 存储封装、`copyText` 复制文本、`download` 文件下载等等,省得同学们每次都要单独装一堆小包。更多的可以去[文档](https://link.juejin.cn/?target=https%3A%2F%2Frattail.varletjs.org "https://rattail.varletjs.org")里查看完整的 API 列表。
类型安全的枚举工具
这个是作者个人比较喜欢的一个工具。前端项目里到处都是枚举值,比如订单状态、用户角色之类的。一般我们用 `enum` 或者常量对象来管理它们,但是 `label`、`description` 这些配套信息就只能另外维护了。`enumOf` 把值和它的元信息放在一起管理,并且类型推导是完备的。
import { enumOf } from 'rattail'
const Status = enumOf({
Pending: { value: 0, label: '待处理' },
Active: { value: 1, label: '进行中' },
Done: { value: 2, label: '已完成' },
})
Status.Pending // 0
Status.Active // 1
Status.values() // [0, 1, 2]
Status.labels() // ['待处理', '进行中', '已完成']
Status.label(Status.Pending) // '待处理'
Status.options() // [{ value: 0, label: '待处理' }, ...]
// 直接丢给 select 组件的 options,再也不用手动维护了前端项目里到处都需要枚举值和它对应的文案,以前每次都要写个 `map` 或者 `switch`,现在一个 `enumOf` 就够了。另外 `enumOf` 的 `label` 和 `description` 支持传入一个 getter 函数,配合 `vue-i18n` 之类的国际化方案可以很方便的实现多语言:
const Status = enumOf({
Pending: { value: 0, label: () => t('status.pending') },
Active: { value: 1, label: () => t('status.active') },
Done: { value: 2, label: () => t('status.done') },
})基于 axios 的渐进式请求工具
这个能力来自于作者之前开源的 `@varlet/axle`,现在通过 `rattail/axle` 直接引入。熟悉作者的同学可能看过之前介绍 `axle` 的文章,它在兼容 `axios` 的同时,天然支持 `Vue3 Composition API`。
import { createAxle } from 'rattail/axle'
import { createUseAxle } from 'rattail/axle/use'
const axle = createAxle({ baseURL: '/api' })
const useAxle = createUseAxle({ axle })
const [users, getUsers, { loading, error }] = useAxle({
method: 'get',
url: '/user',
params: { current: 1, pageSize: 10 },
})作者一直觉得前端请求库和 `Vue` 的响应式系统应该有更好的结合方式,`axle` 就是在这个方向上的一个尝试。如果你不喜欢 `axle` 也完全没问题,`rattail` 的其他能力和请求库是解耦的,换成你喜欢的方案就好。
OpenAPI 生成 API 模块
`rt api` 可以直接解析后端提供的 `OpenAPI` / `Swagger` schema 文件,自动生成类型安全的 API 调用代码,这个在实际项目里把工作流做通之后体验可太好了。
在 `vite.config.ts` 里配置好 schema 路径和输出目录:
import { defineConfig } from 'rattail/vite-plus'
export default defineConfig({
rattail: {
api: {
input: './openapi.json'
},
},
})执行 `rt api` 后会自动生成这样的代码:
import { api } from '@/request'
import { type paths } from './_types'
export type ApiGetUsers = paths['/users']['get']
export type ApiCreateUser = paths['/users']['post']
export type ApiGetUser = paths['/users/{uuid}']['get']
export type ApiUpdateUser = paths['/users/{uuid}']['put']
export type ApiDeleteUser = paths['/users/{uuid}']['delete']
export type ApiGetUsersQuery = ApiGetUsers['parameters']['query']
export type ApiGetUsersRequestBody = undefined
export type ApiGetUsersResponseBody = ApiGetUsers['responses']['200']['content']['application/json']
// ... 其他类型同理
export const apiGetUsers = api<
ApiGetUsersResponseBody, ApiGetUsersQuery, ApiGetUsersRequestBody>('/users', 'get')
export const apiCreateUser = api<
ApiCreateUserResponseBody, ApiCreateUserQuery, ApiCreateUserRequestBody>('/users', 'post')
export const apiGetUser = api<
ApiGetUserResponseBody, ApiGetUserQuery, ApiGetUserRequestBody>('/users/:uuid', 'get')
export const apiUpdateUser = api<
ApiUpdateUserResponseBody, ApiUpdateUserQuery, ApiUpdateUserRequestBody>('/users/:uuid', 'put')
export const apiDeleteUser = api<
ApiDeleteUserResponseBody, ApiDeleteUserQuery, ApiDeleteUserRequestBody>('/users/:uuid', 'delete')请求类型、响应类型全部从 `schema` 里提取,不需要手写。后端接口变了,重新跑一遍 `rt api` 就行,前后端的类型始终保持同步。这个工作流对 AI Agent 也特别友好,AI 可以直接基于生成的类型去写业务代码,不会出现参数类型对不上的问题。甚至 AI Agent 可以通过 api 定义的变化,推测出你接下来要写什么业务。默认使用 `axle`,也支持 `axios` 的预设,同时支持 `自定义输出`。
链式校验规则工厂
做表单的同学应该都写过类似 `required`、`min`、`max` 这些校验规则。不同的 UI 框架校验规则的格式还不一样,每个项目都要适配一遍。`rattail` 提供了一个链式校验规则工厂,写起来很流畅,并且可以适配任意 UI 框架。这种内联的声明式写法和 `TailwindCSS` 的思路类似,可读性和可迁移性都非常好,对 AI 也特别友好,AI 可以直接从模板里读懂校验意图,生成和修改规则的准确度很高。
以 `Naive UI` 和 `Element Plus` 为例:
<!-- Naive UI -->
<script setup lang="ts">
import type { FormItemRule } from 'naive-ui'
import { rulerFactory } from 'rattail/ruler'
const r = rulerFactory<FormItemRule>((validator, params = {}) => ({
trigger: ['blur', 'change', 'input'],
validator: (_, value) => validator(value),
...params,
}))
</script>
<template>
<n-form :model>
<n-form-item
path="name"
label="姓名"
:rule="r().required('必填').min(2, '长度不正确').done()"
>
<n-input v-model:value="model.name" />
</n-form-item>
</n-form>
</template><!-- Element Plus -->
<script setup lang="ts">
import type { FormItemRule } from 'element-plus'
import { rulerFactory } from 'rattail/ruler'
const r = rulerFactory<FormItemRule>((validator, params) => ({
validator(_, value, callback) {
const e = validator(value)
e ? callback(e) : callback()
},
trigger: ['blur', 'change', 'input'],
...params,
}))
</script>
<template>
<el-form :model>
<el-form-item
prop="email"
label="邮箱"
:rules="r().email('必须是邮箱格式').done()"
>
<el-input v-model="model.email" />
</el-form-item>
</el-form>
</template>AI Agent Skills
`rattail` 提供了一套 [Agent Skills](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail%2Ftree%2Fmain%2Fskills "https://github.com/varletjs/rattail/tree/main/skills"),说白了就是给 AI 写了一份"说明书",让 AI Agent 知道 `rattail` 有哪些能力、怎么用,不用你每次都手动告诉 AI。作者觉得未来的开源库都应该考虑对 AI Agent 的友好度。
写在最后
`rattail` 的工具函数和能力大多来自前端社区的通用实践。感谢同学们能看到这里,但是希望 `rattail` 能够帮助到大家。项目基于 `MIT` 协议。如果在使用的过程中遇到任何问题,欢迎在 [issue](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail%2Fissues "https://github.com/varletjs/rattail/issues") 里反馈给我们,同时也十分欢迎对项目有兴趣的同学给我们发 [pull request](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail%2Fpulls "https://github.com/varletjs/rattail/pulls")。
支持我们的话留下一个 [star](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail%2Fstargazers "https://github.com/varletjs/rattail/stargazers") 就好~
- 官方文档: [rattail.varletjs.org](https://link.juejin.cn/?target=https%3A%2F%2Frattail.varletjs.org "https://rattail.varletjs.org")
- 仓库地址: [github.com/varletjs/ra…](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvarletjs%2Frattail "https://github.com/varletjs/rattail")