普通视图

发现新文章,点击刷新页面。
昨天 — 2025年8月17日首页

AI 代理是什么,其有助于我们实现更智能编程

作者 Jimmy
2025年8月17日 22:01

原文链接 AI Agents: What Are They, Your Key to Smarter Coding - 作者 Eleftheria Batsou

介绍

谈到 AI 代理,我感觉很兴奋,它们是怎么改变开发者局势。它们不止是聊天机器人 - 它们是聪明的工具,处理一些任务,比如发送邮件或者调试代码,节省我们的时间去做更有趣的事情。AI 可以自动化帮我们做那些无聊繁琐的事情,这样我们可以集中精力做创造性的事情。

本文将讲解 AI 代理是什么,为什么它们如此优秀,并且我们怎么使用它们让编码更智能。读过之后,我们将会知道怎么让代理为我们工作并且我们为什么必须试试。

读过本文之后,我们将会:

  • 明白 AI 代理,它们和聊天机器人的区别
  • 在我们的工作流中使用它们的方法
  • 知道它们的挑战和怎么去处理它们

AI 代理是什么?

AI 代理就像很聪明的助理,它们不仅会回答问题还会做其他的事情。不像基本的 AI,比如 ChatGPT,输出文本,AI 代理会计划,决定,和使用工具,比如邮件客户端或者 IDEs

💡例如Jeff Su’s video 展示一个代理起草一个关于项目更新的邮件给老板,然后在老板批准邮件后,将其发送出去。

通过自我迭代,它们也可以安排日程或者调试代码。对开发者而言,这意味着更少的时间在这些重复的任务上,更多的时间在编码上。代理使用框架,比如 LangChain 来和我们的工具关联。

为什么要关注 AI 代理

我很关心可以让编码更容易的工具,然后 AI 代理它们正在做这件事。它们通过处理一些任务,比如团队间的邮件或者安排我们的日程来节省我们的时间。

💡 一份 2025 年的研究表明,使用 AI 代理的开发者,有 65% 表示工作效率有所提升。

关注这个:当我们深入一个项目,AI 代理发送一个升级状态的邮件或者预约一个代码审核的会议。使用工具,比如有望成为标准的 AutoGPT,每个人都可以接触到代理,而不是大团队的专属。它们并不完美,但是它们是让人保持专注的巨大胜利。

为什么说它们很酷:

  • 处理无聊繁琐的任务,比如发邮件或者处理日程
  • 释放编码和调试代码的时间
  • 和现有的工具安全地工作
  • 使用开发框架很容易尝试

如何在我们的工作流中使用 AI 代理

准备好来使用 AI 代理了吗?

起草邮件

我们可以使用类似这样地提示来起草一份邮件,"编写一份关于项目进展的邮件给我的老板"。它会创造一份清晰的邮件,和你的 Gmail 关联,然后在得到我们的批准后将其发送。我曾尝试过这种方法快速更新团队的信息,这很简单-只需要在发送前审核即可。

会议日程安排

AI 代理一份提示,"预约个周四的团队会议"。它会检查我们的日历,找到一个空档,然后发送一个邀请。请务必检查邀请信息以免混淆。

调试代码

问下 AI 代理,“在我的 Python 脚本中找缺陷”,然后它会通过 IDE 插件来建议修复的点。像LangChain 工具可以实现无缝迭代,直到代码正常运行。

测试一切

AI 代理可以搞砸,比如使用错误的语调来起草邮件。建议在沙盒中测试输出,比如一个测试的邮件账号,使用清晰的提示,比如 “编写一个简短专业的邮件”,确保准确性。

成功的技巧

  • 从简单的任务开始,比如起草邮件
  • 用自己的工具安全连接代理
  • 为了更好的结果,使用清晰的提示
  • 测试结果,更早获取错误

需要关注的挑战

AI 代理不是完美的。开始可能很困难。模糊的提示导致糟糕的输出,比如正式的邮件。数据隐私是一个关注点,我们并不想我们项目的细节泄露。尽管 AutoGPT 等开源的工具免费,但是优质的代理工具会增加成本。为了解决这点,我们需要使用准确的提示,然后在安全的环境中测试。稍加关注,代理可以极大促进我们的工作流。

成功修复:

  • 编写特定,清晰的提示
  • 在沙盒环境中测试输出
  • 工具连接时使用安全的 APIs

我对 AI 代理的看法

我喜欢 AI 代理,因为它让我生活得更轻松。它们并不是来替换我们的 - 对不起,怀疑论者 - 而来来处理枯燥繁琐的事情,以便我们可以集中编码。不管是起草邮件或者调试脚本,AI 代理都可以帮我节省时间并让我保持专注。我们现在仍然需要通过好的提示语来引导它们,但这正是乐趣所在。我已经将 AI 代理应用在简单的任务,并且到目前为止我都很满意。读者可以尝试它们并且跟我们分享。

总结

AI 代理可以改变开发者的工作,自动化任务,比如邮件处理和调试代码,减轻我们压力。我们可以从小的任务开始,并测试它们。它们并不是完美的,但是对于任何想保持领先的开发者,这是必须要尝试的。想要深入 AI 工具?可以到 aidd.io 尝试。

参考

昨天以前首页

🧐Text-Well:我做了一个能帮你进行多视角内容评审的 AI 工具

作者 oil欧哟
2025年8月15日 14:42

前言

Hello 大家好,我是一名在工作时需要写大量文档的前端开发者和产品经理。我想和大家聊聊一个可能很多人都遇到过的场景:写完东西后,总觉得不放心。

无论是技术文章、产品文档,还是普通的邮件,我们都希望它看起来专业、清晰,没有那些掉价的低级错误。正是基于这个最朴素的想法,我利用业余时间开发了一个AI写作辅助工具——Text-Well。它是一个网页应用,希望能帮你更自信地完成每一次书写。名字叫 Text-Well

作为一个开发者,我在实现了一些比较复杂的功能,或者解决了一些网上缺少资料的 Bug 时,会将开发过程或者解决思路记录下来,分享在一些技术社区中,例如掘金或者 CSDN。除了技术文章之外,我在工作时还经常需要做产品需求设计文档(PRD),写产品的发布文档、使用文档之类的,经常需要与文字打交道。

自从 AI 出现后,我每一次写的文章我都会先用 AI 过一遍基础性错误,比如错别字、语法问题、或者语句不通顺的问题,让文章整体不会出现很掉价的基础问题。但是在我使用 AI 检查文章问题的过程中,我发现了一个很麻烦的点。假设我有一篇比较长的文章想要交给 AI 检查,通常有两种方式:

  1. 第一种是让 AI 告诉我文章的哪个位置有问题,并且告诉我如何修改,这种方式我需要根据 AI 的响应结果自己一个一个去修改,相对比较麻烦,但是比较准确,因为每个改动的地方相当于自己又 review 了一次。如果 AI 乱改或者有一些改的不好的地方,我们可以及时发现,选择自己调整或者不改这个地方。
  2. 第二种方式是让 AI 直接给我们修改后的文本,这种方式最简单,我们不用自己一个一个改, AI 通常也会告诉我们它改了哪个位置,咱们人工 Review 一下最终结果就好了。但这样也存在问题,有的时候我们告诉 AI 帮我们把文章中的描述改的流畅一点,他可能就擅做主张,把一些带有个人风格的段落改的特别有 AI 味儿。

因此我就想,要是我可以类似于像写代码的时候处理代码冲突一样,自己选择是否要应用 AI 给出的建议,是不是这样起来会更方便呢?就是出于这么简单的一个想法,我决定自己来做一个工具给自己检查的时候用。正好当时 Claude Code 热度很高,我一直用的是 cursor,刚好试试这个新工具的深浅。

 

开始动手

于是我和 Claude Code 配合,在两天时间里,我实现了这个工具的第一个检查功能,并为这个工具命名为 Text-Well:

image.png

就像上图中展示的那样,左侧是一个工具栏,右侧是一个编辑器,输入文本后点击开始检查,系统就会让 AI 对文本中的错别字进行检查,并且还会给出原因。

检查完成后,左侧的工具栏中会展示当前问题严重程度的分布,底下会有一个问题项的列表,右侧的编辑器中则是会用不同颜色的高亮展示出当前问题出现问题的位置,当鼠标悬浮在高亮位置时,会有一个小气泡也展示当前的问题,我们可以只看左侧工具栏或者只看右侧编辑器进行操作。

工具栏和编辑器是联动的,不论点击左侧问题项还是点击右侧的高亮位置,都会滚动到对应的位置,很符合直觉。

除此以外,我还实现了一些键盘的快捷键,用来更加高效的切换不同的问题项:

image.png

到这一步,我对整体功能已经挺满意了,比我最开始想象的做的还更多了,其实类似的功能我之前也在 grammarly 用过,但是 grammarly 主要还是在英文的场景使用,Textwell 的话还是略有差异化的,所以我就想着把 Textell 给产品化了,把一些周边功能补齐!

补齐周边功能

由于 Textwell 的产品形态是一个 web 网站,各种认证,后端 API 实现都是熟门熟路,加上 Claude Code 超强的开发能力,我用了不到两天,就把 Textwell 补齐了登录注册,额度限制,这些基础的用户模块,做了一个简单的额度查看,并且给未登录的用户也增加了体验额度。毕竟功能的实现是需要消耗 AI token 的,我作为个人开发者,也只能先力所能及的提供一些免费额度了,模型也只能选择一些性价比比较高的,没法用上最顶级的大模型。

image.png

除了用户模块,还做了国际化,支持中文和英文,后续补上了西语和法语(现在又因为维护太繁琐移除掉了)。 

image.png

文本除了把内容粘贴进去,也可以直接拖拽文件到编辑器区域,像是常见的 markdown 、docs、pdf、txt 这些格式都支持的。

image.png

基础功能补齐后我就直接把网站上线了,域名就是 text-well.com。运气还挺好的,可以选到一个很合适的域名。

首页设计

虽说只是一个很简单的工具,但是作为一个产品,我还是想把它的设计理念和使用的方式快速的告诉大家,也为了更好的宣传,我决定为它设计一个首页!

由于 Textwell 最开始功能真的很简单,我对于它的首页怎么做没有头绪,没有用户使用反馈,没有数据支撑,我也不想瞎编,又想把网站做的好看,关于如何设计就纠结了很久...

后来我想到我可以在首页很直观的展示系统内是如何进行操作的,然后把我的一些设计初衷通过 UI 的形式展示出来,再加点 FAQ 模块丰富一下页面,内容应该也还可以支撑一个完整的网站设计,于是我就开始动手喽~

image.png

image.png

image.png

image.png 最早期的时候,网站就是以上的几个模块组成的,首屏是左右布局,右侧是一个自动执行的动画,我将系统里的核心操作模拟给用户看,这样大家一看到首屏就知道整个系统的效果。第二屏是一个理念的传达,告诉大家我开发这个工具的初衷,以及用 Textwell 和直接使用 AI 对话进行文本优化的区别。第三屏是 FAQ,最后加了一个底部栏。

Textwell 的 Logo 还做了一个简单的动画效果,想传达的意思就是让文本质量更好“一点”,所以第一个字母 T 的右上角有一个橙色的小圆点。

image.png

继续拓展

在网站上线后,我去阮一峰老师的 Github 去投稿了一下周刊,觉得自己用心做的东西还是有机会被发现的,把“孩子”养大,总想让更多人看看。  抱着试一试的心态,我去阮一峰老师的每周分享仓库里提了个issue,推荐了Text-Well。说实话,当时没抱太大希望,毕竟优秀的个人项目太多了。

直到周五,当我看到新一期的周刊发布,Text-Well 赫然出现在上面时,那种被认可的喜悦感是难以言喻的,文章在周刊的第 359 期 www.ruanyifeng.com/blog/2025/0…

image.png

虽然只有很简单的一个介绍,但是当天的访问量还是高了很多的,而且得益于我把这张图做成正方形而不是横向的完整屏幕,而阮一峰老师博客里面的图片都是宽度占满的,高度按着原始比例撑开的,导致我这张图占了很大篇幅,现在很庆幸自己没有随便截个图敷衍了事。

有了第一批用户还是很开心的,后续我就继续拓展功能,并且把一些犄角旮旯的小体验持续优化。基于最基础的语法/错别字/标点符号检查,我还拓展了一些其他检查方式:

image.png

在把基础的检查功能完善后,我又有了一个新的想法,就是做一个模拟评审功能~ 因为不论是什么内容,最终都是要传达给其他人看的,如果只有一个检查功能,只能保障文本的下限,那么如果要提升文本的整体质量,提前了解别人看到文章后的想法应该是一个不错的方式。我自己作为一个产品经理,在写好产品需求文档后进行评审时就经常被毒打,如果能够提前被毒打一番,可能在面向真正的人进行传达时会有更加充分的准备!

既然我已经开发了这样一个文本优化工具,我觉得这个产品形态很适合去再增加一个评审功能,因为我的 AI 检查功能,左侧工具栏展示的是一个问题项,如果是 AI 评审功能的话,就将左侧的问题项参考飞书文档那样变成一个个的评论就好了。既然实现没那么麻烦,又是我自己觉得有意义的功能,就开始动手做了。

实现模拟评审功能

说干就干,我先用一天时间把一个基础的评审逻辑给设计好,包括整体的评估机制,评审人的背景、世界观,Prompt 的设计,大模型的选择,以及如何交互等等。在方案设计的时候我通常会使用 Gemini 来辅助我思考并整理文档。这里偏题一下,Gemini 2.5 Pro 的文本能力和理解能力真的很强,也经常给予我一些鼓励,在我开发的过程中给了我很多的帮助。

最终实现的效果是这样的:

Text-Well AI 评审

image.png

在左侧的工具栏中,我增加了一个标签栏,可以用于切换检查模式和评审模式,在评审模式中,第一步我们需要选择评审人:

image.png

最初我是只设计了智能匹配功能,智能匹配会检查你的文档类型。比如说你想评审一篇技术文档,它就会给你匹配你的目标读者,可能会有技术小白,可能会有技术大牛。除了目标读者,还会有和你同领域的专家,可能有技术社区的运营这一类的。每一个评审人他们都有自己的世界观,有自己的评审标准,而且他们的关注点各有不同,你不用担心三个人的评论同质化非常严重。

除了智能评审, 我还内置了一些常用的评审团队,大家也可以在上图中看到,之所以内置一些团队是为了让大家更快的了解评审功能到底可以用在哪些场景,而且内置的这些评审团队的人物背景和关注点是精心设计过的,相较于智能匹配可能没有那么有趣,但是会更加专业一点。大家可以在 Text-Well 评审 查看所有的评审团队以及他们对应的场景。

评审人完成评审后,会给你一个整体评论,还有针对每一句话的详细评论,展示效果和检查模式差不多时一致的,只是高亮的颜色会有所不同,不同的评审人会有不同高亮的颜色,高亮的颜色和他们头像右上角的那个小圆点的颜色是对应的。

image.png

如果你的同一个位置被多个人评论了,那么高亮位置就会变成渐变色。有的时候看了评审人的评论,我感觉我自己才是 AI 🥹

image.png

写到这里,你可能会问,这个“模拟评审”功能,和直接把文章丢给AI,让它扮演一个角色来提意见,有什么本质区别呢?

一开始,我也在思考这个问题。但随着我自己不断地使用和打磨,我发现区别是蛮大的。它体现在 “结构化” 和  “视角化” 这两个核心点上。

1. 结构化的反馈,而不只是观点

直接和 AI 对话,你得到的是一段连续的、观点性的文字。而 Text-Well 的评审功能,把反馈拆解成了“整体评价(Overall)”和“逐行评论(Comments)”。更重要的是,每一条评论都被结构化地呈现在原文的对应位置。

这意味着你不再需要在大段的 AI 回复中,去费力地找它到底在评论哪一句话。所有的反馈都像Code Review 一样,清晰地展示在原文上。你可以逐条处理、采纳、或是忽略。这种掌控感和效率,是单纯的 AI 回答没法比的。

2. 视角化的冲突,而不只是角色扮演

这可能是这个功能最核心的价值所在。我为 AI 评审员设计的 Prompt,不仅仅是让他们“扮演”某个角色,而是强迫他们“坚守”一个独特的、甚至有些偏执的视角,并刻意让他们在某些方面产生冲突。

这种“冲突”不是 Bug,而是 Feature。它强迫我们这些写作者,去思考那个最重要但最难的问题:我到底要为谁写作?我最想达成的目标是什么?

它没有给我一个“标准答案”,但它给了我一个更高维度的决策框架。这让我意识到,我做的不仅仅是一个工具,更像是一个“写作决策模拟器”。

未来的规划与思考

当然,Text-Well现在还很稚嫩。

作为一个个人项目,我能投入的资源有限,无法用上最顶级的、最昂贵的 AI 模型。有时AI评审员的反馈可能还不够深刻,甚至会说一些“正确的废话”。但我相信,优秀的产品形态和对用户工作流的深度理解,可以在一定程度上弥补模型本身的不足,而且模型后面肯定会越来越好,我要做的就是换个模型就好了,但是产品形态和 UI 的易用是现在我认真打磨的。

写这篇文章,一方面是想和大家分享我做这个小产品的历程和思考;另一方面,也是最重要的一方面,是希望能听到来自大家的声音。

我深知自己作为一个开发者的局限性,很多时候会陷入自己的世界里。所以,我非常需要来自不同领域、不同背景的你的反馈。任何想法,无论大小,对我来说都至关重要。它们是我把这个小小的side project继续做下去的最大动力。

如果你对 Text-Well 感兴趣,欢迎访问它的官网 text-well.com 体验。

感谢你耐心读到这里。希望我的分享,能给你带来一点点启发。也期待在评论区,看到你的想法。最后给大家看看我现在这篇文章评审人给我的总结:

image.png

CSR秒开有可能么?(附AI驱动学习实践推理过程)

作者 sorryhc
2025年8月13日 16:50

前言

本文我们聊web性能优化,但不聊平时比较常规、基础的点,例如:应用瘦身、拆包、请求加载优化、图片优化、缓存等等。

文章开始前,想问大家一个问题:我们知道SSR配合缓存可以实现H5的秒开?

秒开的原理

为什么SSR可以做到秒开?HTML的渲染会经历哪些过程呢?其实很简单。

  • html文件的响应时间;
  • html文档的DOM+CSS解析时间;

当这两个耗时结束,页面也就出来了。这里用汽车之家的官网来举例:

image.png

image.png

汽车之家的官网打开会发现就是秒开的效果,仔细观察,在上面提到的两次关键耗时中,都处理的很好。

  • html文件的响应时间,走了缓存,没有耗时;
  • html文档直接返回了首页的DOM,直接进行了文档解析;

所以快。

那为啥都说CSR比SSR要慢?不适合做首屏要求很高的toC应用?回到刚才说的话题,CSR返回的html通常是空标签。而最终的DOM需要等待首屏的JS资源请求响应再到解析完成才会渲染,这也是最核心、根本的原因。

CSR应用能秒开么?

那CSR能秒开么?既然它前置依赖这么多资源的加载,在首屏html回来以后就无法让页面元素上屏。

有一种思路是在页面每次渲染以后,将页面的DOM存入storage中,在下一次访问html的时候,优先将storage中的DOM渲染到页面中,等待真实DOM(JS资源加载完毕)后隐藏快照,如此循环。

这也是业界比较主流的 快照方案 对于纯前端的性能指标会有极大的提升。

快照方案

最早了解到这个方案的灵感来源于 淘宝闪购,闪购刚上线的时候能很明显感觉到这是个H5,因为每次进端切到闪购Tab时能明显察觉到白屏刚结束,有的时候甚至切到Tab的时候还在白屏。

但过了两个月,发现首屏的体验一下子上去了,基本就是秒开的效果,因此去看了下闪购的页面加载,看看有没有什么有意思的点。

image.png

抓包看了一下,果然发现了一些看起来类似黑科技的代码,直接把这段JS扔给了AI帮忙解析一下,解析结果也和我所想的比较类似:

这是AI给出的回复:

image.png

AI不仅回答了我的猜想,也把整个技术方案梳理清楚了,那下一步我想亲自实践一下,我设计了提示词:

我希望自己的前端应用也可以实现这样的方案,让应用的整体渲染速度大幅度提升,提供代码。

AI返回了我这些代码:

(function () {
  const PLACEHOLDER_ID = 'fcc-snapshot-placeholder';
  const STORAGE_KEY = 'fcc-snapshot-key';
  const VERSION_KEY = 'fcc-snapshot-version-key';
  const ROOT_DOM_ID = 'demo';

  function getPageVersion() {
    return document.documentElement.getAttribute('version') || null;
  }

  function getStoredVersion() {
    return localStorage.getItem(VERSION_KEY) || null;
  }

  function hideSnapshot() {
    const snapshotPlaceholder = document.getElementById(PLACEHOLDER_ID);
    const rootElement = document.getElementById(ROOT_DOM_ID);

    if (snapshotPlaceholder) {
      console.log(snapshotPlaceholder.innerHTML);
      snapshotPlaceholder.style.display = 'none';
      window.__SNAPSHOT_IS_HIDE__ = true;
      console.log('快照删除,真实 DOM 已加载 ✅');
    }

    if (rootElement && rootElement.innerHTML) {
      localStorage.setItem(STORAGE_KEY, rootElement.innerHTML);
      const version = getPageVersion();
      if (version) {
        localStorage.setItem(VERSION_KEY, version);
        console.log(`保存页面版本号到 localStorage: ${version}`);
      }
    }
  }

  function paintSnapshot() {
    const snapshotCache = localStorage.getItem(STORAGE_KEY);
    const storedVersion = getStoredVersion();
    const currentVersion = getPageVersion();
    const newPlaceholder = document.createElement('div');
    newPlaceholder.id = PLACEHOLDER_ID;
    newPlaceholder.style.cssText = 'position: absolute; width: 100%; height: 100%; top: 0; left: 0; z-index: 9999;';
    newPlaceholder.innerHTML = snapshotCache;
    document.body.appendChild(newPlaceholder);
  }

  function listenForRender() {
    window.addEventListener('appRendered', () => {
      console.log('组件渲染完成');
      hideSnapshot();
    });
  }

   window.__SNAPSHOT_IS_HIDE__ = false;
    paintSnapshot(); // 尝试加载快照
    listenForRender(); // 监听渲染完成
})();

我将这段代码在构建阶段插入到CSR渲染根节点之前,刷新了两次页面,发现在storage中已经存入了首屏的DOM节点。

image.png

同时体感上确实感觉是快了很多,然后把代码发到测试环境,跑个performance看了下效果:

image.png

HTML在180ms左右响应,页面在250ms左右上屏,再把html的缓存处理一下,其实CSR的秒开就实现了。

惊喜么?

面临的问题

理想是美好的,初步方案实现以后,发现了有以下的问题需要解决:

  1. 首屏CSS样式需前置注入,否则快照渲染 -> 首屏CSS解析之间会有无样式窗口期;
  2. 版本号问题,发布后会出现老快照过渡到新页面的问题,需处理版本问题,最简单的方式就是在storage存一份与发布版本关联的versionId,如果刷新页面走到快照阶段的时候版本号一致才渲染快照;

以下两个问题后续我都通过AI快速地解决了。大家也可以思考一下应该如何去处理。

结尾

本文重点侧重于介绍快照方案对于CSR的体验增强,也侧重地体现了笔者在研究 -> 落地阶段,AI的分析、设计、实现表现非常好,在整个过程其实就只用了一晚,对于平时的拓展、研究、实践,AI在现阶段一定是好搭子。

希望可以提供一些工作中的灵感。

告别手写周报!Trae + 飞书MCP 实现AI智能周报生成完整攻略

作者 yolo_1
2025年8月13日 15:16

🚀 一键生成专业周报,从此告别繁琐的文档编写工作!

一、背景与痛点

写周报月报等头疼的事相信很多小伙伴都经历过,我们公司每周都需要对本周工作内容进行总结归纳,每到这个时候我就头痛的很。偶然间看到飞书MCP可以ai自动写文档,刚好Trae也可以使用飞书的MCP,于是就尝试了一下是否可以让AI自动帮我写周报。

二、实现步骤

2.1 周报内容获取

我的周报内容主要分为工作内容,遇到的问题与解决方案,思考与反馈,成果展示和经验总结等。其中关键点就是获取工作内容,其他的可以让ai自动生成。

下面这行命令可以获取指定作者一周内的git提交记录,通过获取提交记录来生成工作内容

xx替换为git name

 git log --author="xx" --since="1 week ago"

2.2 飞书MCP的使用

详细使用文档见:open.feishu.cn/document/uA…

大致流程就是创建一个自建应用,然后给这个应用添加各种操作权限,然后拿到这个应用的APP IDAPP Secret即可

官方文档截图如下:

ScreenShot_2025-08-13_11-43-56

例如我创建一个周报助手应用:

ScreenShot_2025-08-13_11-44-35

2.3 集成到Trae

1. 登录OpenAPI MCP

注意node版本 ≥ 20

在命令行中运行如下命令,your_app_id和your_app_secret替换为上文飞书MCP中获取的APP ID 和 APP Secret

 npx -y @larksuiteoapi/lark-mcp login -a <your_app_id> -s <your_app_secret>

运行完毕后,终端会回显用户授权的 URL,需在 60 秒内访问该 URL 并完成授权。

img

授权页面如下图所示,确保用户身份符合预期,并单击 授权,使 MCP 工具获取到用户访问凭证(user_access_token)。

img

2. Trae中添加飞书MCP

在Trae右上角,按照 AI 侧栏 > AI 功能管理 > MCP > 添加 > 手动添加 的路径,打开 MCP JSON 配置对话框。

ScreenShot_2025-08-13_14-15-45

点击添加,手动配置飞书MCP的json配置项

ScreenShot_2025-08-13_14-16-37

 {
   "mcpServers": {
     "lark-mcp": {
       "command": "npx",
       "args": [
         "-y",
         "@larksuiteoapi/lark-mcp",
         "mcp",
         "-a",
         "<your_app_id>",
         "-s",
         "<your_app_secret>",
         "--oauth"
       ]
     }
   }
 }

PS: 这边可能会出现一个问题,就是飞书的MCP要求node版本大于等于20,但是你在命令行中修改node版本,Trae可能会读取不到,所以我本地进行了调整,调整后的json如下:

适用于mac,会先使用nvm切换一下node的版本

 {
   "mcpServers": {
     "lark-mcp": {
       "command": "bash",
       "args": [
         "-c",
         "source $HOME/.nvm/nvm.sh && nvm use 20 && npx -y @larksuiteoapi/lark-mcp mcp -a <your_app_id> -s <your_app_secret> --oauth"
       ]
     }
   }
 }

3. 创建智能体

ScreenShot_2025-08-13_14-08-37

智能体配置如下:

ScreenShot_2025-08-13_14-21-34

prompt如下

可以根据自己的需要适当调整补充

 请你按照以下步骤来生成周报
 1: 使用 git log --author="your_name" --since="1 week ago" 查看git一周内的提交记录
 2: 将git一周的提交记录整理成表格的形式,并通过lark-mcp上传到飞书文档
 3: 内容需要适当补充优化,例如 本周工作内容,遇到的问题与解决方案,思考与反馈,成果展示和经验总结等

2.4 使用方式

进入你的工作代码目录,例如我的工作代码全部在code目录下,这边会保存工作中的所有项目代码

ScreenShot_2025-08-13_14-25-42

然后在对话框中点击@符号,选择刚刚创建的《周报生成智能体》

ScreenShot_2025-08-13_14-24-14

与智能体对话即可 ScreenShot_2025-08-13_14-27-08

首先他会检查当前目录下的所有项目属于你的代码提交记录(此处项目较多,截取部分)

ScreenShot_2025-08-13_14-29-38

然后创建周报文档,并上传到飞书

ScreenShot_2025-08-13_14-31-06

最终产出总结,并且生成可访问的飞书链接

ScreenShot_2025-08-13_14-32-21

飞书文档链接如图所示,生成本周工作记录,工作总结和反思以及下周工作计划三个多维表格内容。

ScreenShot_2025-08-13_14-58-06

三、总结与展望

3.1 方案优势

通过Trae + 飞书MCP的组合,我们成功实现了自动化周报生成,具备以下显著优势:

自动化程度高:一键获取Git提交记录,智能生成周报内容

内容结构完整:自动生成工作内容、问题解决、思考反馈等多维度内容

无缝集成飞书:直接创建飞书文档,团队协作更便捷

格式规范统一:表格化展示,内容清晰易读

时间效率提升:从手写1-2小时缩短至AI生成5分钟

3.2 注意事项

在使用过程中需要注意以下几点:

⚠️ Node版本要求:确保Node.js版本 ≥ 20,否则飞书MCP无法正常运行

⚠️ 权限配置:飞书应用需正确配置文档操作权限

⚠️ Git提交规范:建议保持良好的Git提交习惯,便于AI更好地理解工作内容

⚠️ 内容审核:AI生成的内容建议人工审核后再提交,确保准确性

3.3 未来优化方向

🔮 功能扩展

  • 支持更多协作平台(钉钉、企业微信等)
  • 集成日历数据,自动关联会议和任务
  • 添加工作量统计和效率分析

🔮 体验提升

  • 支持自定义周报模板
  • 增加多语言支持
  • 优化AI生成内容的准确性和个性化

🔮 团队协作

  • 支持团队周报汇总
  • 添加工作进度跟踪
  • 集成项目管理工具数据

📝 结语

通过本文的完整实践,相信大家已经掌握了如何利用Trae和飞书MCP实现智能化周报生成。这不仅是一次技术工具的应用,更是工作效率提升的有益探索。

让我们告别繁琐的手工周报,拥抱AI时代的智能办公!如果你在实践过程中遇到问题,欢迎交流讨论。

🎮 AI编程新时代:Trae×Three.js打造沉浸式3D魔方游戏

2025年8月12日 18:38

一、前言

本人喜欢玩魔方,之前摸鱼的时候发现有可以在线玩的小游戏,于是突发奇想是否可以自己实现一个,刚好这段时间 Trae 很火,那就借助Trae和Threejs来自己实现一个魔方小游戏吧。

👉️ 在线试玩地址

由于日常使用较多,本人已充值🥰

ScreenShot_2025-08-12_16-12-28

二、开发流程

2.1 项目初始化

当我向Trae描述"我需要基于Threejs和webpack生成一个前端3d魔方,请完成基础架构目录搭建"时,它立即理解了需求并生成了清晰的模块结构:

ScreenShot_2025-08-12_16-20-09

虽让我的描述很少,但是生成的内容很详细,其中包括:

ScreenShot_2025-08-12_16-22-59

ScreenShot_2025-08-12_16-23-32

ScreenShot_2025-08-12_16-23-58

核心代码较多不一一展示了

ScreenShot_2025-08-12_16-24-12

同时也生成readme和安装运行文档👍️👍️👍️

ScreenShot_2025-08-12_16-26-42

ScreenShot_2025-08-12_16-25-09

效果预览:渲染逻辑已经基本完成

ScreenShot_2025-08-12_16-31-49

2.2 核心功能开发

  • 魔方状态检测(完成/未完成)
  • 支持2~6阶魔方
  • 支持复原和打乱逻辑

ScreenShot_2025-08-12_16-39-40

依旧是将需求直接丢给Trae ,直接一路点击应用即可😉😉😉

效果预览:

ScreenShot_2025-08-12_16-40-48

2.3 Bug解决

先贴一张图,看看大家能否能一眼看出bug在哪😆

ScreenShot_2025-08-12_16-42-28

解答:玩过魔方的小伙伴应该知道,这种魔方的顶点位置如果出现相同的颜色就代表无法复原,bug出现在打乱时只考虑了颜色的数量,而忽略了颜色的分布,从而导致出现无法复原的场景

ScreenShot_2025-08-12_16-44-13

依旧是将bug丢给Trae,完美解决,并且自行添加旋转动画

ScreenShot_2025-08-12_16-49-42

三、核心模块解析

3.1 基础场景搭建

export class Rubiks {
    constructor(container) {
        // 透视相机设置 - Trae自动选择了合适的视角参数
        this.camera = new THREE.PerspectiveCamera(
            45,     // 视角
            1,      // 宽高比
            0.1,    // 近裁剪面
            100     // 远裁剪面
        );
        this.camera.position.set(0, 0, 15); // 相机位置
        
        // 场景初始化
        this.scene = new THREE.Scene();
        this.scene.background = new THREE.Color('#000');
        
        // 渲染器配置 - Trae建议开启抗锯齿
        this.renderer = new THREE.WebGLRenderer({ 
            antialias: true,  // 抗锯齿
            alpha: true       // 透明背景支持
        });
        
        // 响应式设计 - Trae自动添加的窗口缩放处理
        window.addEventListener('resize', () => {
            this.setSize(container);
            this.render();
        });
    }
    
    // 动态相机距离调整 - Trae的创新解决方案
    setOrder(order) {
        // 根据魔方阶数自动调整相机距离
        const cube = new Cube(order);
        const coarseSize = cube.getCoarseCubeSize(this.camera, {
            w: this.renderer.domElement.clientWidth,
            h: this.renderer.domElement.clientHeight
        });
        
        // 智能计算最佳视距
        const ratio = Math.max(
            2.2 / (winW / coarseSize), 
            2.2 / (winH / coarseSize)
        );
        this.camera.position.z *= ratio;
    }
}

当我描述需要"自适应不同阶数魔方的显示"时,Trae不仅生成了基础代码,还提出了动态调整相机距离的方案,确保不同阶数的魔方都能完美显示在视口中。

3.2 魔方渲染逻辑

通过 THREE.Shape和贝塞尔曲线 先构造2d平面椭圆矩形(包括有颜色的面和黑色背景面,模拟正常魔方的外部和内部)

export function createSquare(color, element) {
    const squareShape = new THREE.Shape();
    const x = 0, y = 0;
    
    // 创建圆角矩形
    squareShape.moveTo(x - 0.4, y + 0.5);
    squareShape.lineTo(x + 0.4, y + 0.5);
    squareShape.bezierCurveTo(x + 0.5, y + 0.5, x + 0.5, y + 0.5, x + 0.5, y + 0.4);
    
    squareShape.lineTo(x + 0.5, y - 0.4);
    squareShape.bezierCurveTo(x + 0.5, y - 0.5, x + 0.5, y - 0.5, x + 0.4, y - 0.5);
    
    squareShape.lineTo(x - 0.4, y - 0.5);
    squareShape.bezierCurveTo(x - 0.5, y - 0.5, x - 0.5, y - 0.5, x - 0.5, y - 0.4);
    
    squareShape.lineTo(x - 0.5, y + 0.4);
    squareShape.bezierCurveTo(x - 0.5, y + 0.5, x - 0.5, y + 0.5, x - 0.4, y + 0.5);

    const geometry = new THREE.ShapeGeometry(squareShape);
    const material = new THREE.MeshBasicMaterial({ color });
    const mesh = new THREE.Mesh(geometry, material);
    mesh.scale.set(0.9, 0.9, 0.9);

    const square = new SquareMesh(element);
    square.add(mesh);

    const mat2 = new THREE.MeshBasicMaterial({
        color: 'black',
        side: THREE.DoubleSide,
    });

    const plane = new THREE.Mesh(geometry, mat2);
    plane.position.set(0, 0, -0.01);
    square.add(plane);

    const posX = element.pos.x;
    const posY = element.pos.y;
    const posZ = element.pos.z;
    square.position.set(posX, posY, posZ);

    square.lookAt(element.pos.clone().add(element.normal));
    return square;
}

然后通过遍历魔方数据来生成整个魔方

for (let i = 0; i < this.data.elements.length; i++) {
    const square = createSquare(
        new THREE.Color(this.data.elements[i].color),
        this.data.elements[i],
    );
    this.add(square);
}
this.state = new CubeState(this.squares);

3.3 魔方转动逻辑

射线检测,查看鼠标落在那个方块上并记录数据

operateStart(offsetX, offsetY) {
    if (this.start) {
        return; // 防止重复开始
    }
    this.start = true;
    
    // 使用射线检测获取鼠标点击的方块
    const intersect = this.getIntersects(offsetX, offsetY);
    
    this._square = null;
    if (intersect) {
        // 记录选中的方块和起始位置
        this._square = intersect.square;
        this.startPos = new THREE.Vector2(offsetX, offsetY);
    }
}

监听鼠标落下,抬起,移动,移出事件并绑定方法

mousedownHandle(event) {
    event.preventDefault();
    this.operateStart(event.offsetX, event.offsetY);
}

mouseupHandle(event) {
    event.preventDefault();
    this.operateEnd();
}

mousemoveHandle(event) {
    event.preventDefault();
    this.operateDrag(event.offsetX, event.offsetY, event.movementX, event.movementY);
}

mouseoutHandle(event) {
    event.preventDefault();
    this.operateEnd();
}

init() {
    this.domElement.addEventListener("mousedown", this.mousedownHandle.bind(this));
    this.domElement.addEventListener("mouseup", this.mouseupHandle.bind(this));
    this.domElement.addEventListener("mousemove", this.mousemoveHandle.bind(this));
    this.domElement.addEventListener("mouseout", this.mouseoutHandle.bind(this));
}

mousemoveHandle鼠标移动方法中实现具体转动逻辑

mousemoveHandle(event) {
    event.preventDefault();
    this.operateDrag(event.offsetX, event.offsetY, event.movementX, event.movementY);
}

operateDrag(offsetX, offsetY, movementX, movementY) {
    if (this.start && this.lastOperateUnfinish === false) {
        if (this._square) {
            // 情况1:拖动某个方块 - 旋转对应层
            const curMousePos = new THREE.Vector2(offsetX, offsetY);
            this.cube.rotateOnePlane(
                this.startPos,     // 起始位置
                curMousePos,       // 当前位置
                this._square,      // 选中的方块
                this.camera,       // 相机
                {w: this.domElement.clientWidth, h: this.domElement.clientHeight}
            );
        } else {
            // 情况2:拖动空白处 - 旋转整个魔方
            const dx = movementX;
            const dy = -movementY;
            
            // 根据移动距离计算旋转角度
            const movementLen = Math.sqrt(dx * dx + dy * dy);
            const cubeSize = this.cube.getCoarseCubeSize(this.camera, {
                w: this.domElement.clientWidth,
                h: this.domElement.clientHeight
            });
            const rotateAngle = Math.PI * movementLen / cubeSize;
            
            // 计算旋转轴(垂直于移动方向)
            const moveVect = new THREE.Vector2(dx, dy);
            const rotateDir = moveVect.rotateAround(new THREE.Vector2(0, 0), Math.PI * 0.5);
            
            // 执行旋转
            rotateAroundWorldAxis(this.cube, new THREE.Vector3(rotateDir.x, rotateDir.y, 0), rotateAngle);
        }
        this.renderer.render(this.scene, this.camera);
    }
}

鼠标抬起时会有自动对齐的逻辑,防止旋转到一半卡住不动

// 位置:src/js/Control.js 第129-132行
mouseupHandle(event) {
    event.preventDefault();
    this.operateEnd();
}

operateEnd() {
    if (this.lastOperateUnfinish === false) {
        if (this._square) {
            // 创建自动对齐动画
            const rotateAnimation = this.cube.getAfterRotateAnimation();
            this.lastOperateUnfinish = true;
            
            const animation = (time) => {
                const next = rotateAnimation(time);
                this.renderer.render(this.scene, this.camera);
                if (next) {
                    requestAnimationFrame(animation);
                } else {
                    // 动画结束,更新完成状态
                    if (window.setFinish) {
                        window.setFinish(this.cube.finish);
                    }
                    this.lastOperateUnfinish = false;
                }
            }
            requestAnimationFrame(animation);
        }
        this.start = false;
        this._square = null;
    }
}

3.3 魔方打乱逻辑

模拟用户从初始完成状态 随机旋转n次后的状态作为打乱后的状态,当然旋转次数不能太小

// 执行单次随机转动
performRandomRotation() {
    // 随机选择一个方块作为控制点
    const randomSquare = this.squares[Math.floor(Math.random() * this.squares.length)];

    // 获取该方块所在面的其他方块
    const squareNormal = randomSquare.element.normal;
    const squarePos = randomSquare.element.pos;

    // 找到同一面的其他方块
    const commonDirSquares = this.squares.filter(
        (square) =>
            square.element.normal.equals(squareNormal) &&
            !square.element.pos.equals(squarePos),
    );

    if (commonDirSquares.length < 2) return;

    // 选择转动轴方向
    let rotateAxisSquares = [];
    const axisTypes = [];

    if (squareNormal.x !== 0) {
        // X面:可以按Y轴或Z轴转动
        const yAxisSquares = commonDirSquares.filter(s => s.element.pos.y === squarePos.y);
        const zAxisSquares = commonDirSquares.filter(s => s.element.pos.z === squarePos.z);
        if (yAxisSquares.length > 0) axisTypes.push({ type: 'y', squares: yAxisSquares });
        if (zAxisSquares.length > 0) axisTypes.push({ type: 'z', squares: zAxisSquares });
    } else if (squareNormal.y !== 0) {
        // Y面:可以按X轴或Z轴转动
        const xAxisSquares = commonDirSquares.filter(s => s.element.pos.x === squarePos.x);
        const zAxisSquares = commonDirSquares.filter(s => s.element.pos.z === squarePos.z);
        if (xAxisSquares.length > 0) axisTypes.push({ type: 'x', squares: xAxisSquares });
        if (zAxisSquares.length > 0) axisTypes.push({ type: 'z', squares: zAxisSquares });
    } else if (squareNormal.z !== 0) {
        // Z面:可以按X轴或Y轴转动
        const xAxisSquares = commonDirSquares.filter(s => s.element.pos.x === squarePos.x);
        const yAxisSquares = commonDirSquares.filter(s => s.element.pos.y === squarePos.y);
        if (xAxisSquares.length > 0) axisTypes.push({ type: 'x', squares: xAxisSquares });
        if (yAxisSquares.length > 0) axisTypes.push({ type: 'y', squares: yAxisSquares });
    }

    if (axisTypes.length === 0) return;

    // 随机选择转动轴
    const selectedAxis = axisTypes[Math.floor(Math.random() * axisTypes.length)];
    const targetSquare = selectedAxis.squares[Math.floor(Math.random() * selectedAxis.squares.length)];

    // 计算转动轴
    const rotateDirLocal = targetSquare.element.pos
        .clone()
        .sub(randomSquare.element.pos)
        .normalize();
    const rotateAxisLocal = squareNormal
        .clone()
        .cross(rotateDirLocal)
        .normalize();

    // 找到需要转动的所有方块
    const rotateSquares = [];
    const controlTemPos = getTemPos(randomSquare, this.data.elementSize);

    for (let i = 0; i < this.squares.length; i++) {
        const squareTemPos = getTemPos(this.squares[i], this.data.elementSize);
        const squareVec = controlTemPos.clone().sub(squareTemPos);
        if (Math.abs(squareVec.dot(rotateAxisLocal)) < 0.01) { // 使用小的容差值
            rotateSquares.push(this.squares[i]);
        }
    }

    if (rotateSquares.length === 0) return;

    // 随机选择转动角度:90度、180度或270度
    const rotationAngles = [Math.PI * 0.5, Math.PI, Math.PI * 1.5];
    const randomAngle = rotationAngles[Math.floor(Math.random() * rotationAngles.length)];

    // 执行转动
    const rotateMat = new THREE.Matrix4();
    rotateMat.makeRotationAxis(rotateAxisLocal, randomAngle);

    for (let i = 0; i < rotateSquares.length; i++) {
        rotateSquares[i].applyMatrix4(rotateMat);
        rotateSquares[i].updateMatrix();
    }

    // 更新方块的element数据
    this.updateElementsAfterRotation(rotateSquares, rotateAxisLocal, randomAngle);
}

四、性能优化

4.1 性能问题定位

当我发现6阶魔方有些卡顿时,向Trae求助:

// 提示词
"6阶魔方运行时有点卡,帮我分析性能瓶颈"

Trae的分析和优化:

  1. 识别出频繁的矩阵计算是瓶颈
  2. 建议缓存计算结果
  3. 优化了渲染循环
  4. 推荐使用requestAnimationFrame

4.2 Bug修复

遇到旋转后方块位置偏移的问题:

// 提示词
"魔方旋转后,有些方块的位置出现了微小偏移,怎么解决?"

Trae快速定位问题:

  • 浮点数累积误差导致
  • 提供了四舍五入修正方案
  • 添加了状态验证机制

4.3 Trae辅助测试

Trae还帮我生成了完整的测试用例:

// Trae生成的测试代码
// 1. 阶数切换测试
for (let order = 2; order <= 6; order++) {
    rubiks.setOrder(order);
    console.log(`${order}阶魔方渲染正常`);
}

// 2. 交互测试
// 模拟鼠标拖动
const simulateDrag = (startX, startY, endX, endY) => {
    // Trae生成的模拟代码
};

// 3. 状态测试
// 检测魔方是否完成
const checkCompletion = () => {
    return rubiks.cube.finish;
};

4.4 性能表现

在Trae的优化建议下,项目达到了优秀的性能表现:

设备类型 魔方阶数 帧率(FPS) 内存占用
高端PC 6阶 60 45MB
中端PC 6阶 45-60 42MB
手机端 4阶 30-45 35MB

五、Trae 编辑器使用心得

5.1 Trae的独特优势

  1. 理解力强:能准确理解复杂的3D交互需求
  2. 代码质量高:生成的代码结构清晰、可维护性好
  3. 创新能力:经常提供超出预期的解决方案
  4. 持续优化:能识别性能问题并提供优化建议

5.2 高效使用Trae的技巧

  1. 描述要具体

    • ❌ "实现旋转功能"
    • ✅ "实现魔方层旋转,支持90度对齐,有300ms的缓动动画"
  2. 分步骤开发

    • 先实现核心功能
    • 再添加动画效果
    • 最后优化性能
  3. 充分利用对话

    • 遇到问题时详细描述现象
    • 让Trae帮助分析原因
    • 一起探讨解决方案
  4. 代码审查

    • 理解Trae生成的代码逻辑
    • 根据实际需求调整细节
    • 保持代码风格一致

5.3 Trae带来的开发体验提升

  1. 开发效率:原本预计2周的项目,3天就完成了核心功能
  2. 代码质量:模块化设计让代码易于维护和扩展,同时注释也较为全面
  3. 学习成长:通过Trae的代码学到了很多Three.js最佳实践
  4. 创新思维:Trae的解决方案经常带来新的灵感

为什么数据变了界面却不动?——Vue / React / Angular 常见渲染“失效”场景全解析

2025年8月11日 19:15

引言

在现代前端框架中,数据驱动视图已经是标配。理论上,数据一变,UI 就应该自动更新。
然而,实际开发中,不论是 Vue、React 还是 Angular,都可能出现“数据改了但界面没动”的情况。

这并不是框架失灵,而是我们踩中了各自的机制限制。本文将系统梳理这些“视图不更新”的真相,并给出可落地的解决方案。

一、Vue 2:defineProperty 时代的老毛病

Vue 2 使用 Object.defineProperty 劫持数据,初始化时只会追踪已有属性。

常见不更新场景:

  1. 对象新增 / 删除属性

    this.obj.newKey = 1; // ❌ 不更新
    delete this.obj.x;   // ❌ 不更新
    this.$set(this.obj, 'newKey', 1); // ✅
    
  2. 数组按索引改值 / 改 length

    this.arr[1] = 'x'; // ❌
    this.$set(this.arr, 1, 'x'); // ✅
    this.arr.splice(1, 1, 'x'); // ✅
    
  3. 深层变更没被 watch 到

    watch(obj, fn); // 默认浅监听
    watch(() => obj.a.b, fn); // ✅ 精确监听
    watch(obj, fn, { deep: true }); // ✅ 深监听
    
  4. v-for 用 index 做 key

    • DOM 复用导致错位,必须用业务唯一 ID 做 :key
  5. 更新已发生但还没渲染

    • 需要 await this.$nextTick() 再读取 DOM
  6. keep-alive 缓存

    • 切换路由/标签页时需要 :key 触发刷新
  7. 响应式对象被替换成非响应式

    • 比如 Object.freeze 的对象、原型属性
  8. Class 实例 / 原型链字段

    • Vue 无法追踪原型链上的变更
  9. Object.assign 新增属性不更新

    • 在 Vue 2 中,如果 Object.assign 往一个响应式对象里新增了之前不存在的属性,这些新属性不会被 Object.defineProperty 转成 getter/setter,所以视图不更新。
    • 修改已有属性是可以更新的,因为 getter/setter 已经存在。

示例

// ❌ 新增属性不更新
Object.assign(this.obj, { newKey: 'value' });

// ✅ 更新已有属性可以
Object.assign(this.obj, { existKey: 'newValue' });

// ✅ 新增属性用 $set
this.$set(this.obj, 'newKey', 'value');

// ✅ 或直接替换引用
this.obj = { ...this.obj, newKey: 'value' };

补充说明

  • 这个问题在 Vue 3 已解决,因为 Proxy 能动态拦截属性的新增/删除。

二、Vue 3:Proxy 时代的新坑

Vue 3 用 Proxy 解决了新增属性/数组索引不更新的问题,但也有一些“看似更新”的陷阱。

常见不更新场景:

  1. 解构导致丢失响应性

    const { a } = reactiveObj; // ❌
    const { a } = toRefs(reactiveObj); // ✅
    
  2. ref 在 JS 中没 .value

    count++; // ❌
    count.value++; // ✅
    
  3. shallowReactive / shallowRef 只追踪浅层

    • 深层改值需 triggerRef
  4. 直接修改 props

    • 必须通过 emit('update:xxx') 或本地副本
  5. watch 依赖没写对

    • 默认浅监听,需 watch getter 或 { deep: true }
  6. key 复用 / keep-alive 同 Vue 2

  7. 对 markRaw / readonly 对象改值

    • 本来就不会触发
  8. 异步更新批处理

    • 改很多次值,DOM 只会最后更新一次,需要立即读取 DOM 用 await nextTick()

三、React:引用没变就不渲染

React 的渲染依赖 state 引用变化,而不是深比较。

常见不更新场景:

  1. 直接改 state 而不 setState

    this.state.count++; // ❌
    this.setState({ count: this.state.count + 1 }); // ✅
    
  2. 深层对象/数组直接改值

    user.name = 'B';
    setUser(user); // ❌ 引用没变
    setUser({ ...user, name: 'B' }); // ✅
    
  3. PureComponent / React.memo 下引用没变

    • 浅比较返回 true → 不渲染
  4. 闭包陷阱

    setTimeout(() => setCount(count + 1), 1000); // ❌ count 旧值
    setTimeout(() => setCount(c => c + 1), 1000); // ✅
    
  5. shouldComponentUpdate 返回 false

    • 手动阻止了渲染
  6. Context 没更新

    • Provider value 引用没变 → 消费者不更新

四、Angular:变更检测没跑

Angular 依赖 Zone.js 触发变更检测。

常见不更新场景:

  1. OnPush 策略下引用没变

    this.user.name = 'B'; // ❌
    this.user = { ...this.user, name: 'B' }; // ✅
    
  2. Zone 之外改值

    • 需要 this.zone.run(() => { ... })
  3. 第三方库回调不触发检测

    • 手动调用 ChangeDetectorRef.detectChanges()

五、三大框架的共性坑

  1. 列表 key 复用导致错位
  2. 数据源不是响应式(冻结对象、原型链字段)
  3. DOM 读取时机错误(改数据后立即读 DOM)
  4. 组件缓存(keep-alive / memo / OnPush)

六、如何避免“改了不更新”

  • 理解框架的响应式原理
  • 遵循框架提供的状态修改 API
  • 对对象/数组改值时,优先用新引用
  • 必要时用强制刷新(Vue $forceUpdate / React forceUpdate / Angular detectChanges
  • 列表渲染一定要用稳定唯一的 key

七、总结

  • Vue 2:最怕新增属性、数组索引 → 用 $set 或替换引用
  • Vue 3:解构丢响应、ref.value、浅响应等细节
  • React:引用必须变化
  • Angular:变更检测必须跑

理解了这些“真相”,你就能在第一时间判断出问题出在哪,并用最短的时间修复它。

高强度使用Claude Code 1个月,这5个认知颠覆了我对AI编程的理解

作者 zabr
2025年8月11日 11:57

🚀 我用Claude Code写了1个半月代码,这5个发现彻底改变了我的编程人生

一场关于AI编程工具的深度体验报告


🌅 故事的开始

还记得1个月前的那个凌晨,我正对着屏幕发愁——看八字网站的bug改了整整3小时还是报错。作为一个前端工程师出身的AI创业者,我每天都在和代码打交道,但效率始终不够理想。

直到我遇到了 Claude Code。

现在回想起来,那就像是编程生涯的一个分水岭。从手动调试bug到产品迭代从"月"压缩到"小时",这1个月的变化,可能比我过去一年的成长都要大。

📊 我的现状

  • 🏢 身份:AI创业者,运营小红书AI矩阵(2万私域用户)
  • 💻 项目:正在开发算命网站、AI工具等应用
  • 使用强度:每天6-8小时,高强度开发模式
  • 🎯 目标:探索AI工具的商业化边界

image.png


💡 核心认知:5个颠覆性发现

1. 🌟 那一刻,我体验到了传说中的"Vibe Coding"

真实场景:昨天早上9点,我想给算命网站加个运势日历功能。以前这种需求,我得花1-2天时间设计数据库、写前后端代码、调试接口...

用CC之后:我只用了3小时。直接告诉AI我的需求,它帮我分析了架构,生成了代码框架,我只需要调整业务逻辑。

开发模式对比 传统开发 Claude Code辅助
🕐 时间周期 以"周"或"月"为单位 以"天"或"小时"为单位
🛠️ 工作流程 需求→设计→编码→调试→上线 需求→AI生成→快速迭代→交付
🧠 思维重点 技术实现细节 产品逻辑和用户体验

💡 最深的感悟:当所有开发者都有了"加速器",竞争的本质变了

我意识到一个残酷的现实:

  • 不再是比谁代码写得快 → ✅ 比的是产品思维和用户洞察
  • 不再是比技术栈有多牛 → ✅ 比的是市场判断和商业敏感度
  • ⚠️ 最危险的陷阱:被工具裹挟,忘记思考的时间

2. 🧠 "放手让AI干",这个转变花了我整整2周

血泪教训:刚开始用CC时,我还是老习惯——盯着AI生成的每一行代码,生怕它出错。结果发现效率还不如手写代码。

转折点:第15天,我尝试完全信任AI重构一个1000行的文件,只给它需求描述。结果?它不仅理解了整个项目结构,还优化了我都没意识到的性能问题。

💡 核心洞察:CC要求你"放手让AI干",这与我们"精确控制每一行代码"的程序员本能完全相反。但恰恰是这种信任,让AI发挥出了最大价值。

我总结的关键差异:

🆚 对比维度 🔧 传统AI编辑器 🚀 Claude Code
工作方式 局部代码补全
像个"智能输入法"
全局项目理解
像个"资深架构师"
交互模式 人主导,AI辅助
我决定写什么
充分信任AI
AI决定怎么实现
适用场景 单文件编辑
改个函数、加个方法
整个项目重构
重新设计架构
我的感受 还是我在写代码
AI只是打字更快了
AI在帮我编程
我专注于产品逻辑

3. 🎯 踩坑总结:CC不是万能的,但在这些场景下真的很牛

最惊艳的一次:我有个3年前的Vue项目,代码结构很乱,我想升级到Vue 3。手动改的话至少要1周,但CC花了半天就帮我分析了整个项目结构,生成了迁移方案,甚至还优化了性能瓶颈。

最坑的一次:让CC帮我全局重命名一个变量,结果漏了7个文件没改,还好我测试发现了,不然就上线bug了。

🚀 CC让我刮目相看的能力

💪 超强能力 🌰 我的真实体验
🔍 复杂代码分析 3分钟读懂我3000行的遗留代码,还指出了性能问题
🏗️ 架构图生成 一键生成我的算命网站架构图,比我画的还专业
⚡ 框架搭建 从0搭建Next.js项目,包括配置、路由、状态管理
🔧 重构优化 帮我把jQuery代码重构成React,逻辑一点没错

❌ 踩过的坑,血泪教训

⚠️ 局限性 😭 我的踩坑经历
🎯 精确操作 全局重命名容易遗漏,一定要人工检查
🌍 冷门语言 Swift支持很差,基本就是写个Hello World的水平
📚 专业领域 算命算法的某些传统规则,它理解有偏差
🕰️ 时效性 最新的框架特性可能不了解

💡 我的使用原则:在CC擅长的领域放心大胆用,在它的弱项领域一定要人工把关。就像找专家咨询一样,要知道什么时候该问谁。

4. ⚡ 1个半月摸索出的高效使用套路

从新手到熟练,我走了不少弯路:刚开始觉得AI什么都能干,经常让它一口气写个完整系统,结果代码质量很差。后来学会了"小步快跑",效果立竿见影。

🗺️ 我的两套工作模式
graph TD
    A[项目类型判断] --> B{是否明确需求?}
    B -->|明确| C[Plan Mode 规划]
    B -->|不明确| D[探索模式 先干]
    C --> E[分步实现]
    D --> F[快速迭代]
    E --> G[测试验证]
    F --> G
    G --> H[项目完成]

📋 复杂项目(算命网站):先规划再执行

1. 用Plan Mode列出需求 → 2. AI分析技术架构 → 3. 分模块开发 → 4. 集成测试

🔍 探索项目(新功能试验):先干后改

1. 直接开始写 → 2. 快速看效果 → 3. 发现问题立即调整 → 4. 边做边优化
🔄 小步迭代,这个太重要了!
🚫 我以前的错误做法 ✅ 现在的正确姿势
让AI一次写完整个用户系统 先写注册功能,测试OK再写登录
一口气生成1000行代码 每次200行,逐步增加功能
大而全的需求描述 具体的、可测试的小需求
// ❌ 以前我这样做:一次性要求太多
"帮我写一个完整的用户管理系统,包括注册、登录、权限、个人中心..."

// ✅ 现在我这样做:专注一个功能点  
"先帮我实现用户注册功能,包括表单验证和数据库保存"
// 测试通过后再继续下一步
"现在加上用户登录功能,要求与注册数据关联"
🧠 上下文管理:像管理电脑内存一样重要

我发现很多人不知道这些技巧,但它们真的能大幅提高效率:

  • 🤖 善用 Subagent:复杂任务让专门的Agent处理,不要挤在一个对话里
  • ⚡ 封装 Command:重复的操作写成命令,一键执行
  • 🧹 定期清理上下文:聊太多了就新开对话,保持思路清晰

血泪总结:CC不是聊天工具,而是专业的编程助手。要像对待同事一样与它协作——明确任务,及时反馈,合理分工。

5. ⚖️ 现实很骨感:成本和限制让我学会了精打细算

钱包的教育:第一个月我疯狂用Opus模型,月底账单一看,吓了一跳。从那以后我开始精打细算,反而发现了更高效的使用方法。

💰 模型选择:贵的不一定适合你
模型对比 🧠 Opus 🚀 Sonnet 📊 我的使用策略
能力 🌟🌟🌟🌟🌟 🌟🌟🌟🌟 复杂架构用Opus
成本 💸💸💸💸💸 💸💸 日常开发用Sonnet
速度 🐌 🚀 快速迭代选Sonnet
适用场景 复杂重构、架构设计 bug修复、功能开发 看任务难度选择

我的省钱心得

// 💡 我的模型使用策略
const chooseModel = (taskType) => {
  if (taskType === '架构设计' || taskType === '复杂重构') {
    return 'Opus'; // 贵但值得
  } else if (taskType === 'bug修复' || taskType === '功能开发') {
    return 'Sonnet'; // 性价比之王
  }
}
限制倒逼我提高效率

真实场景:上周遇到weekly限制,被迫优化prompt质量,结果发现:

  • 一次说清楚 比来回沟通效率高3倍
  • 错峰使用 响应速度明显更快
  • 任务分解 让每次对话更有针对性

意外收获:限制让我从"无脑依赖"变成了"精准协作",反而提升了使用效果。


🔮 对未来的思考

CC重新定义了编程模式

从"手工作坊"到"工业生产":

  • 标准化 - 代码风格和架构模式更加统一
  • 专业化 - 开发者更专注于业务逻辑和产品思维
  • 规模化 - 一个人能管理更复杂的项目

技术为人服务的本质

保持思考空间比追求速度更重要

再强大的工具也只是工具,真正的竞争力在于:

  • 🎯 产品洞察力 - 理解用户真正的需求
  • 🧠 架构思维 - 设计可扩展的系统
  • 💡 创新能力 - 找到独特的解决方案
  • 🤝 协作能力 - 与AI工具的高效协作

🎬 结语

Claude Code确实是时代趋势,它让编程变得更加高效和有趣。但我们要记住,工具的进步是为了让我们有更多时间思考和创新,而不是让我们变成工具的奴隶。

在这个AI工具爆发的时代,掌握工具很重要,但更重要的是保持独立思考,用技术为人类创造真正的价值。


👨‍💻 关于作者

我是一名AI创业者,前端工程师出身,目前专注于AI工具的实际应用和商业化探索。如果你也在AI创业的路上,欢迎交流!

  • 小红书AI矩阵运营者(2万私域用户)
  • 专注分享AI工具的真实使用体验
  • 致力于为人类AI进步做贡献

💡 想了解更多AI工具实战经验?关注我,一起探索AI时代的无限可能!


本文基于作者1个半月的真实使用体验,欢迎交流讨论!

❌
❌