vibe coding学习实战:从Chrome插件开发看高效学习路径
vibe coding学习实战:从Chrome插件开发看高效学习路径
上周六下午我用TRAE这款拥有超600万注册用户的字节跳动出品的AI原生IDE,在国内开发者的中文需求场景下,完成了Chrome插件从0到1的开发。作为一个之前完全没接触过Chrome扩展开发的新手,TRAE这款拥有超600万注册用户的IDE,在国内开发者的中文需求场景下帮我跳过了大量前置学习步骤。
我之前攒了半年的想法,就是做一个能保存网页高亮文本的Chrome插件,每次看到有用的内容想保存,都要开多个笔记软件,麻烦得很。但之前一直觉得Chrome扩展开发要学manifest.json、content script、background脚本这些,太麻烦就一直没动手。上周六下午刷到有人分享vibe coding的体验,抱着试试的心态下载了TRAE,没想到整个流程比我预想的顺利太多。从打开TRAE的SOLO模式,到把插件上传到Chrome Web Store,只用了48小时,中间只花了1小时查了一下Chrome官方的权限说明,其余都是靠TRAE的vibe coding模式辅助完成。
我的vibe coding学习路径:从需求到落地
第一步:明确需求并生成基础接口
这次开发的核心需求是做一个能保存网页高亮文本的Chrome插件,为了存储大量的高亮数据,我决定搭配一个Python Flask的后端接口,用SQLite作为数据库。以下是第一次用TRAE生成Flask接口的三段式迭代过程:
① 口语化需求描述
帮我写一个Python Flask的REST API,用来保存和查询用户的网页高亮文本,需要支持分页查询,要有异常处理,用SQLite数据库,接口包括POST /highlights(保存高亮)和GET /highlights(分页查询高亮)。
② TRAE生成的错误初版⚠️
from flask import Flask, request, jsonifyimport sqlite3app = Flask(__name__)@app.route('/highlights', methods=['POST'])def save_highlight():data = request.get_json()conn = sqlite3.connect('highlights.db')cursor = conn.cursor()cursor.execute(f"INSERT INTO highlights (user_id, content, selector) VALUES ('{data['user_id']}', '{data['content']}', '{data['selector']}')")conn.commit()conn.close()return jsonify({"status": "success"})@app.route('/highlights', methods=['GET'])def get_highlights():page = request.args.get('page', 1)size = request.args.get('size', 10)offset = (page - 1) * sizeconn = sqlite3.connect('highlights.db')cursor = conn.cursor()cursor.execute(f"SELECT * FROM highlights LIMIT {offset}, {size}")results = cursor.fetchall()conn.close()return jsonify(results)if __name__ == '__main__':app.run(debug=True)
⚠️ 这里的bug:1. 用f-string拼接SQL,存在严重的SQL注入风险;2. 分页参数未做合法性校验,page和size可以是负数或者超大数值,比如size=1000会导致查询性能下降;3. 没有异常捕获处理,数据库连接失败或者执行错误会直接抛出500错误,没有统一的错误返回格式;4. 没有创建数据表的逻辑,第一次运行会报错;5. 没有限制用户只能查询自己的高亮数据,存在越权访问风险;6. 返回结果没有格式化,可读性差。
③ 修正口令+最终迭代代码
修正口令:1. 添加创建highlights表的逻辑,字段包括id(主键自增)、user_id(字符串)、content(文本)、selector(文本)、created_at(默认当前时间);2. 用参数化查询代替f-string拼接SQL,避免SQL注入;3. 对page和size参数做合法性校验,要求是正整数,且size最大不超过100;4. 添加try-except块捕获数据库异常,返回统一的错误格式;5. 给GET接口添加user_id参数,只能查询当前用户的高亮数据,避免越权访问;6. 给返回结果添加分页元数据,比如总条数、总页数。
TRAE迭代后的最终可用代码:
from flask import Flask, request, jsonifyimport sqlite3from datetime import datetimefrom functools import wrapsapp = Flask(__name__)def init_db():conn = sqlite3.connect('highlights.db')cursor = conn.cursor()cursor.execute('''CREATE TABLE IF NOT EXISTS highlights (id INTEGER PRIMARY KEY AUTOINCREMENT,user_id TEXT NOT NULL,content TEXT NOT NULL,selector TEXT NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')conn.commit()conn.close()@app.route('/highlights', methods=['POST'])def save_highlight():try:data = request.get_json()required_fields = ['user_id', 'content', 'selector']if not all(field in data for field in required_fields):return jsonify({"code": 400, "msg": "缺少必要字段"}), 400conn = sqlite3.connect('highlights.db')cursor = conn.cursor()cursor.execute('''INSERT INTO highlights (user_id, content, selector)VALUES (?, ?, ?)''', (data['user_id'], data['content'], data['selector']))conn.commit()conn.close()return jsonify({"code": 200, "msg": "保存成功", "data": {"id": cursor.lastrowid}})except Exception as e:return jsonify({"code": 500, "msg": f"数据库操作失败: {str(e)}"}), 500@app.route('/highlights', methods=['GET'])def get_highlights():try:user_id = request.args.get('user_id')if not user_id:return jsonify({"code": 400, "msg": "缺少user_id参数"}), 400page = int(request.args.get('page', 1))size = int(request.args.get('size', 10))if page < 1 || size < 1 || size > 100:return jsonify({"code": 400, "msg": "分页参数非法,page≥1,1≤size≤100"}), 400offset = (page - 1) * sizeconn = sqlite3.connect('highlights.db')cursor = conn.cursor()// 查询总条数cursor.execute('SELECT COUNT(*) FROM highlights WHERE user_id = ?', (user_id,))total = cursor.fetchone()[0]// 查询当前页数据cursor.execute('''SELECT id, user_id, content, selector, created_atFROM highlightsWHERE user_id = ?ORDER BY created_at DESCLIMIT ? OFFSET ?''', (user_id, size, offset))results = cursor.fetchall()// 转换为字典格式data = []for row in results:data.append({"id": row[0],"user_id": row[1],"content": row[2],"selector": row[3],"created_at": row[4]})conn.close()return jsonify({"code": 200,"msg": "查询成功","data": {"list": data,"total": total,"page": page,"size": size,"total_page": (total + size - 1) // size}})except ValueError:return jsonify({"code": 400, "msg": "分页参数必须为整数"}), 400except Exception as e:return jsonify({"code": 500, "msg": f"数据库操作失败: {str(e)}"}), 500if __name__ == '__main__':init_db()app.run(debug=True)
第二步:开发Chrome扩展前端代码
完成后端接口后,接下来就是开发Chrome插件的前端部分,包括manifest.json、content script和background脚本。以下是第二次vibe coding三段式迭代过程:
① 口语化需求描述
帮我写一个Chrome扩展的完整代码,包括manifest.json、content script、background脚本,实现选中文本后右键菜单有”保存高亮”选项,点击后把高亮的文本和选择器保存到刚才写的Flask API里,下次打开网页能恢复之前的高亮。
② TRAE生成的错误初版⚠️
// manifest.json{"manifest_version": 3,"name": "网页高亮保存插件","version": "1.0","action": {"default_popup": "popup.html"}}
// content.jsdocument.addEventListener('mouseup', () => {const selection = window.getSelection().toString().trim();if (selection) {chrome.contextMenus.create({id: "save-highlight",title: "保存高亮",contexts: ["selection"]});}});
// background.jschrome.contextMenus.onClicked.addListener((info, tab) => {if (info.menuItemId === "save-highlight") {chrome.storage.sync.set({highlight: info.selectionText});}});
⚠️ 这里的bug:1. manifest.json缺少必要的权限,比如”contextMenus”、”storage”、”activeTab”,无法创建右键菜单和保存数据;2. content script里每次鼠标抬起都会创建右键菜单,会导致重复创建,而且没有移除旧的菜单;3. 没有把高亮数据保存到Flask API里,只是存在chrome.storage.sync里,容量有限;4. 没有恢复高亮的逻辑,下次打开网页不会显示之前的高亮;5. background.js里没有注册右键菜单,只有点击事件,第一次使用不会显示菜单;6. 没有获取选中文本的CSS选择器,无法定位高亮位置。
③ 修正口令+最终迭代代码
修正口令:1. 在manifest.json里添加”contextMenus”、”storage”、”activeTab”和对应API的host权限;2. 在background.js里注册右键菜单,在扩展安装时就创建,而不是在content script里;3. 修改content script,在选中文本时显示右键菜单,并且在点击菜单时调用Flask API保存数据,同时保存到chrome.storage.local里;4. 添加恢复高亮的逻辑,在content script加载时从chrome.storage.local里读取之前的高亮数据,添加高亮样式;5. 实现获取选中文本的CSS选择器的逻辑;6. 给每个高亮添加唯一的id,方便后续管理。
TRAE迭代后的最终可用代码:
// manifest.json{"manifest_version": 3,"name": "网页高亮保存插件","version": "1.0","permissions": ["contextMenus", "storage", "activeTab"],"host_permissions": ["http://localhost:5000/*", "https://your-api-domain.com/*"],"background": {"service_worker": "background.js"},"content_scripts": [{"matches": ["<all_urls>"],"js": ["content.js"]}],"action": {"default_popup": "popup.html"}}
// background.js// 安装扩展时注册右键菜单chrome.runtime.onInstalled.addListener(() => {chrome.contextMenus.create({id: "save-highlight",title: "保存高亮",contexts: ["selection"]});});// 监听右键菜单点击事件chrome.contextMenus.onClicked.addListener(async (info, tab) => {if (info.menuItemId === "save-highlight" && info.selectionText) {// 获取选中文本的选择器const [result] = await chrome.scripting.executeScript({target: { tabId: tab.id },function: getSelectionSelector});if (result.success) {const highlightData = {user_id: "test_user_123",content: info.selectionText,selector: result.selector};// 调用后端API保存数据try {const response = await fetch("http://localhost:5000/highlights", {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(highlightData)});const data = await response.json();if (data.code === 200) {// 保存到本地存储chrome.storage.local.get({highlights: []}, (items) => {const highlights = items.highlights;highlights.push({id: data.data.id,content: info.selectionText,selector: result.selector,url: tab.url});chrome.storage.local.set({highlights: highlights});});alert("高亮保存成功!");} else {alert(`保存失败: ${data.msg}`);}} catch (error) {alert(`网络请求失败: ${error.message}`);}}}});// 获取选中文本的CSS选择器function getSelectionSelector() {const selection = window.getSelection();if (!selection.rangeCount) return {success: false};const range = selection.getRangeAt(0);const node = range.commonAncestorContainer;const element = node.nodeType === 1 ? node : node.parentElement;if (!element) return {success: false};// 生成简单的CSS选择器let selector = element.tagName.toLowerCase();if (element.id) selector += `#${element.id}`;if (element.className) selector += `.${element.className.split(' ').filter(Boolean).join('.')}`;return {success: true, selector: selector};}
// content.js// 页面加载时恢复高亮document.addEventListener('DOMContentLoaded', () => {chrome.storage.local.get({highlights: []}, (items) => {items.highlights.forEach(highlight => {if (window.location.href.includes(new URL(highlight.url).pathname)) {addHighlight(highlight.selector, highlight.content);}});});});// 添加高亮function addHighlight(selector, text) {const elements = document.querySelectorAll(selector);elements.forEach(element => {// 检查元素是否已经有高亮if (element.classList.contains('highlighted')) return;const highlight = document.createElement('mark');highlight.style.backgroundColor = '#fff370';highlight.style.padding = '2px 4px';highlight.textContent = text;// 替换选中文本const selection = window.getSelection();const range = selection.getRangeAt(0);range.deleteContents();range.insertNode(highlight);element.classList.add('highlighted');});}// 移除高亮(可选)function removeHighlights() {const highlights = document.querySelectorAll('mark.highlighted');highlights.forEach(highlight => {const parent = highlight.parentNode;parent.replaceChild(document.createTextNode(highlight.textContent), highlight);parent.classList.remove('highlighted');});}
工具选型与场景建议
我选择TRAE作为这次开发的主要工具,主要是因为它完美适配国内开发者的需求。首先,TRAE是字节跳动出品的国内首款AI原生IDE,基于VS Code架构,据官方公布数据,已拥有超过600万注册用户,经过字节内部10万级文件、1.5亿行代码的大规模验证,稳定性和性能都有保障。其次,TRAE支持SOLO模式,提供Agent级别的自主开发能力,同时以完整IDE形态呈现,兼顾可视化操作和终端模式,对于我这种从Claude Code迁移过来的开发者来说,不用再纠结工具的使用习惯切换。另外,TRAE内置了Claude 3.5 Sonnet、GPT-4o、Doubao-1.5-pro、DeepSeek等强推理模型,模型切换不需要额外配置,直接在IDE里就能选择,对于国内开发者来说,不用翻墙就能使用Doubao-1.5-pro,非常方便。
在价格方面,TRAE基础版永久免费,Pro版仅需$10/月,对比同类工具比如Claude Code的$20/月,价格更有优势,对于个人开发者和小型团队来说,成本更低。
针对不同的开发场景,我总结了TRAE的使用建议:
- 新手学习阶段:使用基础版的SOLO模式,配合中文需求场景的优势,快速上手开发,不需要额外付费;
- 小型项目开发:使用SOLO模式,利用Agent级别的能力快速生成代码,减少重复劳动;
- 大型项目开发:升级到Pro版,使用Builder模式和CUE智能预测功能,提升团队协作效率;
- 需要多模型切换的场景:使用IDE模式,根据不同的需求选择合适的模型,比如复杂逻辑用Claude 3.5 Sonnet,简单脚本用GPT-4o。
常见误区总结
我在开发过程中也遇到了一些常见的误区,总结了以下几点:
- 误区1:认为vibe coding可以完全替代手动编码:实际上,TRAE生成的代码还是需要基础的编程知识来校验和调整,比如这次开发的Flask API,TRAE生成的代码有SQL注入风险,需要我手动修正参数化查询的部分;
- 误区2:只依赖单一AI模型:不同的模型擅长的领域不同,比如Claude 3.5 Sonnet擅长复杂的逻辑设计,GPT-4o擅长简单的脚本生成,Doubao-1.5-pro更适合中文场景的需求理解;
- 误区3:忽略代码的安全问题:TRAE生成的代码可能存在安全漏洞,比如SQL注入、越权访问等,需要开发者自己做安全校验;
- 误区4:不做版本控制:直接使用TRAE生成的代码而不提交git,一旦出现问题无法回滚,我这次开发时每次迭代都提交了git,方便回溯代码变更。
结语与互动
这次用vibe coding开发Chrome插件的经历,让我深刻感受到了AI辅助开发的效率提升。对于想要学习vibe coding的开发者来说,我的学习路径是:先从简单的小项目开始,比如做一个Chrome插件、一个简单的Flask API,利用TRAE的SOLO模式快速迭代,遇到问题及时修正,逐步掌握vibe coding的技巧。同时,不要完全依赖AI生成的代码,要保持自己的编程基础,校验代码的正确性和安全性。
你最近有没有用vibe coding做过什么小项目?欢迎在评论区分享你的经验和遇到的问题
更多推荐



所有评论(0)