00. 速览
大家好,我是大家的 林语冰
。
不久前尤大给 VS Code 点赞 + 转发,因为 AI 插件使用了 Vue 作为示例。
说来忏愧,白嫖了 Vue 这么久,但本人从未贡献过源码,因为我对 TS 的"类型体操"感到头大。
看到尤大的朋友圈之后我悟了,主流的 IDE 或插件都支持 AI,不如联手 AI 试试 ------ 我来写 JS,类型体操外包给 AI。
反正 Vue 源码有类型检查和单元测试兜底,提交还有真人审核,应该不会因为我的尝试让 Vue 项目意外中毒。
不幸的是,试试就逝世,虽然最终代码没有 bug,但我掉进到了过早优化的陷阱。
幸运的是,借助 VS Code 的 MarsCode AI 插件,我摸索到了参与开源的新路子:
- AI 规避"代码屎山" - 以 Vue 源码
isObject()
为例 - AI 赋能类型体操 - 以 Vue 源码
isPromise()
为例 - AI 赋能性能优化 - 以 Vite 源码
isPrimitive()
为例
01. AI 解释源码
根据 ES 语言规范,JS 有且仅有一种 Object
类型,用于表示非原始值。
但实际开发中,"对象"这个范畴的判定方案各有利弊:
obj?.constructor === Object
- 包含Object
的实例,但不包含数组和函数等派生对象,也不包含无原型对象(即原型为null
)({}).toString.call(obj)
- 包含Object
的实例,但不包含数组和函数等派生对象typeof obj
- 包含数组等派生对象,但不包含函数(小心null
的历史包袱)obj instanceof Object
- 包含数组和函数等派生对象,但不包含无原型对象
这些"测不准"的方案都无法精准表示 非原始值。
对此,Vue 源码的思路是采用 typeof
封装 isObject() || isFunction()
,再让两者完美搭档。
js
// 重构前的"代码屎山"
const notPrimitive = (v) =>
v !== null && (typeof v === 'object' || typeof v === 'function')
const isPrimitive = (v) =>
v == null ||
typeof v === 'boolean' ||
typeof v === 'number' ||
typeof v === 'string' ||
typeof v === 'symbol' ||
typeof v === 'bigint'
但这种写法稍显冗长,有没有更棒的方案呢?其实还有基于 Object()
的奇技淫巧:
js
// 重构后两行代码搞定:
const notPrimitive = (v) => Object(v) === v
const isPrimitive = (v) => Object(v) !== v
这个写法并非本人凭空发明,而是借助 AI 解释 Vitest 源码时发现的。起初我也看得一脸懵逼,但在 VS Code 中调试源码,可以让 MarsCode AI 插件解释源码:
AI 已经解释得很详细了,我还翻阅了 ES 语言规范,简而言之,规范中 Object()
的核心算法简化如下:
- 如果
Object(v)
的参数是非原始值,则返回参数本身; - 如果
Object(v)
的参数是原始值,则返回参数的包装对象; - 非原始值的引用地址相等,而原始值的包装对象和原始值不等。
理解 Vitest 源码的这种奇技淫巧之后,我决定用它来重构 Vue 源码。
02. AI + 类型体操
首先我需要在 GitHub 上 fork(复刻)了 Vue 源码库,再克隆到 VS Code。
但我只是贡献源码,对 Vue 过去十年的版本历史不感兴趣,所以我其实只需要克隆最近最新的源码即可。
遇事不决问 AI,AI 告诉我使用下列命令即可:
bash
git clone --depth 1 <repository-url>
浅拷贝源码库之后,我新建了分支,开始用上文的方案来重构,其中一个需要重构的方法是 isPromise()
,源码的 JS 版本大致如下:
ts
// 重构前:
const isPromise = (v) =>
(isObject(v) || isFunction(v)) && isFunction(v.then) && isFunction(v.catch)
// 重构后:
const isPromise = (v) => notPrimitive(v) && isFunction(v.then) && isFunction(v.catch)
此处只是一个示例,我还复用 notPrimitive()
方法重构了其他地方,并作为不同的 git commit
提交。
给 Vue 贡献源码的另一个需求是,使用 TS 确保类型安全。同样,遇事不决问 AI,然后再自己动手修改。
我在 VS Code 里使用了 MarsCode 插件的 apply 功能,可以直接将 AI 的参考答案 apply(应用)到 Vue 源码中,类似 git diff
的效果:
如图,红色部分是 Vue 源码,绿色部分是我联手 AI 的重构,如果觉得代码没问题,就点击采纳即可。
在提交或推送代码之前,我们还需要跑一下类型检查和单元测试。如果是新功能或修复 bug,有时还要按需编写单元测试。如果你尚未接触 TDD(测试驱动开发),也可以让 MarsCode 给你生成单元测试参考。
顺利通过脚本测试之后,我就提交了源码,等待 Vue 团队审核。
03. AI + 性能优化
很可惜,本次贡献没有通过 Vue 团队的审核,因为我忽略了另一个问题:性能。我们虽然通过了功能测试,但我们并没有测试性能。
Vue 团队告诉我,经过 JS 性能的基准测试,这次重构并没有提升性能,这时我才意识到除了考虑代码风格,还需要考虑性能。
事实上,我还提交给 Vite 源码,同样惨遭拒绝,Vite 团队解释除了速度性能外,还可能涉及包装对象的垃圾回收。这又是我的另一个知识盲区。
但试错也不是毫无收获,别忘了我们的灵感是从 Vitest 源码中发现的,所以理论上可以反向把 Vue 高性能的源码移植到 Vitest,但我并没有继续尝试。
因为 Vite 团队成员向我解释了"过早优化乃万恶之源",在稳定的开源项目中,迷你重构属于 JS 微观性能,不同于算法级别或特定功能的优化,这种优化的性能影响可以忽略不计。
想象一下,如果是虚拟 DOM 等需要多次运行的算法,那么优化肯定是有意义的。但如果只是微观优化,性能差异敏感,且接受我的源码还会复杂化版本历史,所以一般稳定的开源项目不会纠结此类问题。
高潮总结
在本次联手 AI 参与开源的冒险中,AI 可以解释源码,还在类型参考和生成测试等方面初露锋芒,降低了开源贡献的心智负担。
此外,提交之前除了功能测试,最好利用 Vitest 源码依赖的 tinybench 或在线工具测试性能,这样开源团队才能理解你的贡献意图。
开源经验方面,如果是新项目,JS 微观性能重构是可接受的,我发现 Vue / Vite 源码早期的提交中就合并了这种优化,甚至现在的 Vitest 源码就采用了两种不同方案封装 isPrimitive()
。
但如果你想给稳定的开源项目贡献源码,不建议纠结 JS 的微观性能。
粉丝福利
除了尤大的分享和本人的开源经历,Vue 学院官方博客还发表了一篇《在 Vue 编程工作流中解锁 AI 能力》的博客,推荐阅读。
本文正在参加豆包MarsCode上新Apply体验活动,附带 @豆包MarsCode官方 的 VS Code AI 插件的新年福利:
- 新用户体验送手机支架,打字体验 AI 代码补全,一天一次,三次即可(5000 人,先到先得),积分换手机支架活动链接:www.marscode.cn/events/s/if...
- 完成任务一就是老用户,老用户不想要手机支架,还可以邀请更多新人(完成任务一双方各得 10 分),继续积分换其他奖品,积分奖品池如下。
我是大家的 林语冰
,欢迎持续 关注,随时了解海内外前端开发的最新情报。
谢谢大家的激情点赞和友情转发,我们下期再见~👍