跳到主要内容

cursor agent 使用体验

前言

最近公司在考虑重构整个开发者后台,这是公司非常重要的后台项目,有着非常多的功能模块,能够控制不同游戏在不同包,不同渠道,不同大区,不同版本,不同平台中的各项配置,能最大程度的控制和管理发行的游戏包。

同时还对游戏账号玩家实名防沉迷问卷调查短信迁移错误日志等等各种涉及到公司业务以及第三方服务的模块都能够在后台上操作以及管理,总之,功能十分强大。

之前这个后台使用了公司的技术总监在 react 的基础上二次封装的 mo-react,本意是希望服务端人员高效快速开发,但是随着公司发展壮大以及 AI 的普及,mo-react 反而变得不是非常好用,因此决定抛弃 mo-react 直接使用 react 。拥抱开源的技术栈来更好的使用 AI 提升开发效率。

评估重构这个后台的工时和任务就交给了我。也就是这个契机下,我决定写一个页面来更好的展示评估结果。

评估难度我决定主要按照代码行数和模块文件数量来判断。个人认为是一个比较粗略的评估方式,仅供参考。

获取模块数据

基本方向

我希望有个 node.js 脚本,用来把 /pages 目录下的所有模块文件夹中的文件都遍历出来,然后获取其中文件数量,每个文件行数,以及是否 import 了 mo-react 这个模块。生成一个 json 数据,然后我再来对这里面的数据做可视化页面。

此JSON数据是我模拟的,并非真实数据。

代码实现和生成数据

const fs = require("fs");
const path = require("path");

function isFunctionComponent(content) {
// 判断是否引入了 FC
if (
/import\s+\{\s*FC\s*\}\s+from\s+['"]react['"]/.test(content) ||
/import\s+type\s+\{\s*FC\s*\}\s+from\s+['"]react['"]/.test(content) ||
/React\.FC/.test(content)
) {
return true;
}
// 其他常见函数式组件写法,包括带类型注解的 FC 写法
const functionComponentPatterns = [
/export\s+default\s+function\s+[A-Z][A-Za-z0-9_]*\s*\(/,
/function\s+[A-Z][A-Za-z0-9_]*\s*\(/,
/const\s+[A-Z][A-Za-z0-9_]*\s*=\s*\(?.*\)?\s*=>/,
/export\s+const\s+[A-Z][A-Za-z0-9_]*\s*=\s*\(?.*\)?\s*=>/,
// 新增:带类型注解的 FC 写法
/const\s+[A-Z][A-Za-z0-9_]*\s*:\s*FC(<.*?>)?\s*=\s*\(?.*\)?\s*=>/,
/export\s+const\s+[A-Z][A-Za-z0-9_]*\s*:\s*FC(<.*?>)?\s*=\s*\(?.*\)?\s*=>/,
];
return functionComponentPatterns.some((pattern) => pattern.test(content));
}

function listAllFilesWithLineCountAndMoReactAndFun(dir, prefix = "") {
const result = [];
const items = fs.readdirSync(dir, { withFileTypes: true });
items.forEach((item) => {
const relPath = prefix ? `${prefix}/${item.name}` : item.name;
const fullPath = path.join(dir, item.name);
if (item.isDirectory()) {
result.push(
...listAllFilesWithLineCountAndMoReactAndFun(fullPath, relPath)
);
} else if (item.isFile()) {
const content = fs.readFileSync(fullPath, "utf-8");
const lineCount = content.split("\n").length;
const isMoReact =
/from\s+['"]mo-react['"]|require\(['"]mo-react['"]\)/.test(content);
const isFun = isFunctionComponent(content);
result.push({ file: relPath, line: lineCount, isMoReact, isFun });
}
});
return result;
}

const baseDir = path.join(__dirname, "client/src/node_modules/g-view/pages");
const folders = fs
.readdirSync(baseDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);

let allfiles = {};

folders.forEach((folder) => {
const folderPath = path.join(baseDir, folder);
const files = listAllFilesWithLineCountAndMoReactAndFun(folderPath);

allfiles[folder] = files;
});
fs.writeFileSync(
path.join(__dirname, "allfiles.json"),
JSON.stringify(allfiles, null, 2),
"utf-8"
);
console.log("已生成 allfiles.json 文件");

展示模块文件信息

基本方向

我给 json 数据做了三种数据分析

  • 按照文件数量,分为: 小于3个文件,大于3个小于5个,5个以上
  • 按照模块总代码长度行数分(去除 less 文件) 分为:简单模块(≤500代码行),中等模块(501~1200代码行), 复杂模块(1201~2000代码行), 超复杂模块(>2000代码行)
  • 最后是查看其中 mo-react 的占比 ,分为:mo-react组件函数式组件

然后了一些简单的交互,分类展示以及侧边的评估栏。最后页面如下。

页面展示和代码

总结

之前我使用 cursor 本质上和使用 vscode + copilot 没什么差别。感觉只是升级版的 copilot。

cursor 对于各个文件之间的引用以及读取能力是远超 copilot 的,在开发中,如果 A 文件和 B 文件有关联并且我修改了 A 代码会导致 B 代码的修改,那么我可以无缝的从 A 文件一路按 Tab 到 B 文件,使用起来比 copilot 舒服非常多。

之所以一直使用 Tab 而非 Agent 是因为现在公司的代码逻辑非常多,害怕在某些时候 AI 不小心改动到了某些地方而我没注意到最后导致线上出 bug。而当完全起一个新的项目,就不怎么担心了

我个人认为:

优秀的 AI 使用者一定有着过硬的专业能力

AI 确实可以完全按照你的需求生成代码,但是你要对这里面涉及到的知识有足够的认知,知道这个东西的概念是什么,称呼是什么,原理是什么,才能更好的使用 AI,好的程序员才会是好的 AI 使用者,这是我始终坚信的。

AI 有着非常强的抹平信息差的能力

如果对于概念不清楚,你也可以在和 AI 沟通的过程中,逐渐明白这个东西的概念。并且能够很快的了解这个内容,变学习边工作的感觉我觉得非常好,AI 能够抹平非常多的信息差。

在 AI 的辅助下,一个困难的东西能够轻松的实现,对于我的正反馈也是非常大的,就像一个非常谦逊且优秀的老师一样,你只要不停的问他,他就会孜孜不倦的给你反馈,教导你,提升你。毫无疑问当下,最好的学习方式就是和 AI 沟通。

ZoomIn 和 ZoomOut

在 ai 编程的时候,确实是仿佛雇佣了一个顶级程序员,很多时候,你只要考虑大方向上的逻辑,剩下的小细节 ai 能够自动补充,有错误再纠错就好了。我能够更好的专注在想要实现什么样的功能上面,这是非常好的感觉。

这次这个重构评估我花了 5 个多小时,因为我并不知道根据什么去评估,需要什么图表,需要哪些数据,所以一边思考,一边给 Ai 提示沟通,我大部分时间都在思考这个页面的需求以及展示效果,而不是考虑怎么遍历过滤排序去生成数据如何写逻辑去实现交互,能够更好的专注在需求功能以及页面内容而非技术实现这种感觉确实是非常的产品经理。

我认为,在 ai 时代,综合能力将会是更大的考验,整体应用大方向上交互以及需求的把控,以及小细节上技术的实现,这种抓大放小,既能有全局视野也有针对细节的能力才是更重要的。