ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI编码工具可以随便选,但部署必须挑平台:从音乐播放器项目说起

AI编码工具可以随便选,但部署必须挑平台:从音乐播放器项目说起 1. 为什么我敢说“AI编码工具可以随便选”1.1 从一次真实的翻车经历说起上个月我想给自己写一个本地音乐播放器需求很简单能扫描本地音乐文件夹、能按专辑和歌手分类、能播放无损格式、界面别太丑。这种小项目放在以前我可能手写两三天但现在有了AI编码工具我第一反应就是让Codex帮我生成骨架。结果第一天就翻车了。我让Codex生成一个基于React Vite的项目结构它给我吐出来的代码里用了process.env而Vite默认不注入Node的process对象浏览器控制台直接报process is not defined。这个坑其实在热词里也出现过——“vite中项目一直报错process is not defined”说明这不是我一个人遇到的问题。但我想说的重点不是这个坑本身而是我当时的反应我没有换工具而是直接告诉Codex“Vite环境下process未定义请改用import.meta.env”。它第二次就改对了。这件事让我彻底想明白一个道理AI编码工具之间的差距远小于你对自己技术栈的理解差距。Codex、Claude、Cursor、通义灵码这些工具在生成常规业务代码时的水平已经非常接近真正决定项目能不能跑起来的是你知不知道Vite和Webpack的环境变量机制不一样。所以标题里那句“AI编码工具可以随便选”不是让你闭着眼睛乱选而是说选型阶段不要纠结。你手头有哪个就用哪个Codex也好其他工具也好先把代码生成出来把项目跑起来遇到问题再针对性调整。纠结工具本身是最浪费时间的环节。1.2 工具选型的真正分水岭在哪里我用了大半年AI编码工具总结下来真正拉开差距的场景只有三个长上下文理解当你需要它读懂一个已有项目的十几个文件时不同工具的上下文窗口和检索能力差异明显。特定框架的熟练度比如React 19的新特性、Vite 6的配置变更训练数据新的工具明显更准。错误自修复能力报错信息贴回去能不能一次改对。但这三个场景都有一个共同点它们都发生在“写代码”阶段而不是“部署”阶段。写代码阶段你有一万次重试机会改错了再让AI改一遍就行成本极低。可一旦进入部署阶段环境是真实的端口是真实的域名是真实的出错了就是502用户就是打不开这个阶段才真正考验平台选择。这就是标题后半句的核心部署才需要挑平台。2. 部署环节为什么不能“随便”2.1 本地能跑和线上能跑是两码事我见过太多人包括我自己早期在本地npm run dev跑通了就以为项目完成了。实际上本地开发和线上部署之间隔着至少五道坎环节本地开发线上部署环境变量.env文件直接读需要平台注入构建时和运行时分离端口固定3000/5173平台动态分配需读PORT变量静态资源路径相对路径随便写需要配置base路径和CDN前缀构建产物不关心需要区分SSR和SSG产物目录要对进程管理终端关了就没了需要守护进程或Serverless我那个音乐播放器就踩过静态资源路径的坑。Vite默认打包出来的index.html里引用的是绝对路径/assets/xxx.js我部署到子目录后全部404。解决办法是在vite.config.js里设置base: ./但这个细节AI生成代码时根本不会提醒你因为它不知道你要部署到哪里。2.2 平台选择的三个硬指标既然部署要挑平台那挑什么我个人的判断标准就三条第一构建环境要透明。你得知道它用的是Node几、包管理器是npm还是pnpm、构建命令能不能自定义。有些平台黑盒得太厉害构建失败了只给你一句“build failed”连日志都不全这种直接pass。第二静态资源和后端服务要能共存。我的音乐播放器虽然是前端项目但扫描本地音乐文件夹需要一个轻量后端我用Node写了个简单的文件扫描接口。如果平台只能托管纯静态页面那就得前后端分开部署跨域问题又来了。所以我倾向于选那种既能跑Node服务又能托管静态资源的平台。第三部署流程要可复现。最好能通过配置文件比如Dockerfile或者平台自己的配置文件描述整个部署过程而不是靠网页上点来点去。这样下次换平台或者重建环境时直接复制配置文件就行。莫一云在这三点上做得比较均衡构建日志完整支持Node服务常驻也有配置文件可以版本化管理。这也是我最终选它的原因不是什么情怀就是这三点刚好都满足。3. 用Codex写音乐播放器的完整实操记录3.1 项目初始化和技术栈确定我一开始给Codex的提示词是这样的帮我创建一个本地音乐播放器技术栈用React 18 Vite TypeScript。 功能需求 1. 扫描指定文件夹下的mp3和flac文件 2. 按专辑和歌手分组展示 3. 支持播放、暂停、上一首、下一首 4. 显示播放进度条和音量控制 5. 界面用Tailwind CSS深色主题 请先给出项目结构和依赖列表。Codex返回的结构很标准src/components放组件src/hooks放自定义hooksrc/utils放工具函数。依赖列表里它列了react、react-dom、vite、typescript、tailwindcss、howler音频播放库。这里有个细节值得说它选了howler而不是原生的Audio API。我一开始想改成原生API减少依赖但试了一下发现原生API处理flac格式在部分浏览器上有兼容问题howler内部做了降级处理。所以最后我保留了howler。这个决策过程AI不会告诉你得你自己判断。3.2 文件扫描接口的实现前端没法直接读本地文件夹浏览器安全限制所以必须有个后端接口。我让Codex生成一个Express服务// server/index.js const express require(express); const fs require(fs); const path require(path); const app express(); const MUSIC_DIR process.env.MUSIC_DIR || ./music; app.get(/api/songs, (req, res) { const songs []; function scan(dir) { const files fs.readdirSync(dir); files.forEach(file { const fullPath path.join(dir, file); const stat fs.statSync(fullPath); if (stat.isDirectory()) { scan(fullPath); } else if (/\.(mp3|flac)$/i.test(file)) { songs.push({ name: path.basename(file, path.extname(file)), path: fullPath.replace(MUSIC_DIR, ), size: stat.size }); } }); } scan(MUSIC_DIR); res.json(songs); }); app.listen(process.env.PORT || 3000);这段代码本身没问题但部署到莫一云时我改了两个地方一是MUSIC_DIR改成从环境变量读二是app.listen的端口改成读process.env.PORT。这两个改动是部署平台强制的不改就起不来。注意很多AI生成的Node服务代码会硬编码端口3000本地跑没问题线上部署必挂。养成习惯看到listen(3000)就改成listen(process.env.PORT || 3000)。3.3 前端播放器核心逻辑播放器组件我让Codex生成了三版第一版用useState管理播放状态第二版用useReducer第三版用zustand。最后我选了zustand原因是播放状态需要在多个组件间共享播放条、歌曲列表、音量控制用Context或者props drilling都太啰嗦。核心播放逻辑大概长这样// src/store/playerStore.ts import { create } from zustand; import { Howl } from howler; interface PlayerState { currentSong: string | null; isPlaying: boolean; volume: number; howl: Howl | null; play: (path: string) void; toggle: () void; setVolume: (v: number) void; } export const usePlayerStore createPlayerState((set, get) ({ currentSong: null, isPlaying: false, volume: 0.8, howl: null, play: (path) { const { howl } get(); if (howl) howl.unload(); const newHowl new Howl({ src: [path], volume: get().volume, onend: () set({ isPlaying: false }) }); newHowl.play(); set({ currentSong: path, isPlaying: true, howl: newHowl }); }, toggle: () { const { howl, isPlaying } get(); if (!howl) return; isPlaying ? howl.pause() : howl.play(); set({ isPlaying: !isPlaying }); }, setVolume: (v) { const { howl } get(); if (howl) howl.volume(v); set({ volume: v }); } }));这段代码有个隐藏问题Howl实例存在zustand里而zustand的状态是不可序列化的如果开了持久化中间件会报错。我没开持久化所以没事但如果你要存播放历史得把howl单独放ref里。4. 部署到莫一云的完整流程和踩坑记录4.1 构建配置的调整Vite项目部署前必须改vite.config.ts我改了三个地方// vite.config.ts import { defineConfig } from vite; import react from vitejs/plugin-react; export default defineConfig({ plugins: [react()], base: ./, // 关键改成相对路径 build: { outDir: dist, assetsDir: assets, sourcemap: false // 线上不需要sourcemap减小体积 }, server: { proxy: { /api: http://localhost:3000 // 本地开发时代理后端 } } });base: ./这个改动是必须的否则部署到子路径后所有资源404。sourcemap: false是个人习惯线上开着sourcemap会让构建产物大好几倍而且暴露源码结构。4.2 莫一云的部署配置莫一云支持用Dockerfile描述部署环境我写了一个多阶段构建的# 构建阶段 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 运行阶段 FROM node:20-alpine WORKDIR /app COPY --frombuilder /app/dist ./dist COPY --frombuilder /app/server ./server COPY --frombuilder /app/package*.json ./ RUN npm ci --production EXPOSE 3000 CMD [node, server/index.js]这个Dockerfile的关键点是多阶段构建。构建阶段装了所有devDependencies运行阶段只装production依赖最终镜像体积能小一半以上。我第一次没分阶段镜像1.2G分了之后降到400M。莫一云的控制台里我把构建命令设成docker build -t music-player .启动命令设成docker run -p 3000:3000 music-player环境变量里加了MUSIC_DIR/app/music。音乐文件我通过平台的持久化存储挂载进去这样重新部署不会丢数据。4.3 部署后遇到的三个真实问题问题一静态资源404。前面说了base没改。改完重新构建就好了。问题二API请求跨域。本地开发时Vite的proxy帮我代理了/api线上没有proxy前端直接请求/api/songs会打到静态服务器上。解决办法是在Express里加一行app.use(express.static(dist))让后端同时托管前端静态文件这样前后端同源跨域问题自然消失。问题三flac文件播放失败。部分flac文件的编码格式howler不支持控制台报decodeAudioData failed。我的处理方式是加一个错误回调播放失败时提示用户“该格式暂不支持”而不是让整个播放器卡死。const newHowl new Howl({ src: [path], volume: get().volume, onloaderror: (id, err) { console.error(音频加载失败, err); set({ isPlaying: false }); // 这里可以加一个toast提示 } });5. 关于AI编码和平台部署的一些个人体会5.1 AI工具的正确使用姿势我用Codex这半年最大的体会是把它当成一个打字速度极快但记性不太好的初级工程师。它能帮你把重复代码写完能帮你查API用法但它不知道你的部署环境长什么样不知道你的用户用什么浏览器不知道你的服务器有多少内存。所以正确的用法是让AI写“标准答案”你自己做“环境适配”。比如它生成的Express服务默认监听3000端口你知道要改成process.env.PORT它生成的Vite配置默认base: /你知道要改成./。这些适配工作AI做不了因为它没有你部署环境的上下文。5.2 平台选择的长期考量莫一云我用下来整体稳定但选平台不能只看当下。我评估一个部署平台会看三个长期指标迁移成本如果哪天要换平台我的Dockerfile和配置文件能不能直接复用能就说明这个平台没有过度绑定。成本透明度流量怎么算、存储怎么算、构建时长怎么算这些在控制台里能不能一眼看到。社区活跃度遇到问题能不能搜到解决方案官方文档更新及不及时。这三点比“免费额度多少”重要得多。免费额度用完就没了但迁移成本高会把你锁死。5.3 一个容易被忽略的细节构建缓存Vite项目构建时node_modules的缓存能极大缩短构建时间。莫一云支持构建缓存但需要你在Dockerfile里正确配置COPY顺序——先COPY package*.json再RUN npm ci最后COPY . .。这个顺序能让Docker在package.json没变时复用缓存层构建时间从3分钟降到40秒。我第一次写Dockerfile时把COPY . .放在了npm ci前面导致每次改一行代码都要重新装依赖。这个坑很隐蔽因为本地构建感觉不出来线上构建次数多了才发现时间都浪费在装依赖上。5.4 后续可以扩展的方向这个音乐播放器目前只支持本地文件扫描后面我打算加两个功能一是接入在线音乐API做搜索二是加一个简单的推荐算法根据播放历史推歌。接入在线API的话后端要加缓存层不然每次搜索都打第三方接口容易被限流。推荐算法初期可以用基于物品的协同过滤数据量大了再考虑上向量检索。部署方面如果用户量涨起来静态资源可以拆出来放CDN后端服务做水平扩展。莫一云支持多实例部署到时候加个负载均衡就行。不过那是后话了现在这个阶段能把一个AI写的项目稳定跑起来已经比大多数人强了。
返回列表