引言
早在大學的時候,我就期望做一款屬于自己的 APP,可惜那時不懂技術。現在有了技術,但卻沒有時間。好在 AI 的快速發展終于讓我完成了這個愿望。于是,我用半天的時間,用 AI 生成了一個純前端的 H5 程序:隨心記。
在線體驗地址:https://aicoding.juejin.cn/aicoding/work/7519454663779811366
什么是隨心記
隨心記是一個由 AI 生成的網頁備忘錄,它支持語音錄入(可下載)、圖文視頻記錄。最重要的是,它支持離線使用,所有數據都儲存在瀏覽器中,不依賴后端,刷新頁面數據也不會丟失!
此外,它還做了語音兼容處理,支持在 iOS 和安卓設備中通過 WebView 嵌套使用。
功能介紹
- 歡迎頁
- 信息錄入頁
支持圖文
支持語音錄入、播放、下載
數據離線緩存,刷新頁面不丟失
僅點擊退出會清空數據
技術棧
本項目通過Trae+Claude-3.7生成,純科技,不夾雜人工代碼,項目最終通過掘金MCP一鍵部署發布。
作品發布&體驗地址:https://aicoding.juejin.cn/aicoding/work/7519454663779811366
AI的代碼技術棧如下:
- 星空背景:Cnavas實現
- 錄音功能:純原生實現,后為了兼容改用record-core
- 圖片/視頻功能:Vant-UI
- 數據離線儲存:采用IndexDB
AI實踐過程
開發工具準備
要使用AI實現這樣一個小程序,首先要選取合適的AI編程工具。比如字節的Trae或國外的Cursor都行,由于要使用掘金的MCP一鍵部署,還是建議使用Trae。
不同的AI模型可能影響代碼生成效果,因此,建議使用Claude,編碼能力更強。
產品及prompt設計
要使用 AI 生成你想要的項目,首先一定要設計好你的產品,比如它包含哪些模塊,有哪些功能,交互邏輯如何等等。因為這些內容,最終都將作為 prompt(AI模型提示詞) 投喂給 AI,而 prompt 的詳細程度,往往直接決定了你生成的產品質量。
當然,如果你實在不知道怎么設計產品,也可以請教 ChatGPT,比如這樣問:
我想通過AI編譯器生成一個記事本APP,幫我設計一個詳細的prompt
設置自定義智能體
為了讓AI開發代碼更準確,符合我們的開發、編碼習慣,我們可以創建一個智能體,讓AI通過我們的預設信息來進行代碼生成。
通過AI提問開發項目
一切都準備好后,我們就可以使用AI生成項目了。首先,我們需要讓AI幫我們搭建項目框架。
我要創建一個名為“隨心記”的APP,使用Vue3+typeScript+less幫我搭建項目框架,不要寫入任何內容,等待我的指示
搭建項目的過程中,我們只需要按照提示,運行相應的命令即可:
項目搭建好,我們就可以按照模塊,讓AI幫我們一步步完善代碼。我們以生成星空背景+手機框架為例。
promot:幫我生成星空背景,樣式要炫酷,屏幕中間顯示一個Ihone的手機框架。
我們可以用過上下文協議,選中某個文件,將代碼生成在指定位置:
代碼生成后,我們先預覽效果,如果效果不達標,我們可以通過不斷提問去增加細節,如給背景的星星增加閃爍效果,給背景增加流星之類的。通過不斷優化promot,效果會越來越接近我們想要的效果。
給出我通過AI生成的星空背景代碼:
<template><div class="media-wrap"><canvas class="star-canvas"></canvas></div></template><script setup>import { onMounted } from 'vue'
onMounted(() => {const canvas = document.querySelector('.star-canvas')const ctx = canvas.getContext('2d')function resize() {canvas.width = canvas.offsetWidthcanvas.height = canvas.offsetHeight}resize()window.addEventListener('resize', resize)// ? 星星類(星星變大)class Star {constructor() {this.reset()}reset() {this.x = Math.random() * canvas.widththis.y = Math.random() * canvas.heightthis.radius = Math.random() * 2.5 + 0.5 // ? 更大:0.5 ~ 3pxthis.alpha = Math.random()this.fade = Math.random() * 0.02this.speed = 0.1 + Math.random() * 0.3}update() {this.y += this.speedthis.alpha += this.fadeif (this.alpha <= 0 || this.alpha >= 1) this.fade = -this.fadeif (this.y > canvas.height) this.reset()}draw() {ctx.beginPath()ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2)ctx.fillStyle = `rgba(255,255,255,${this.alpha})`ctx.fill()}}// ?? 流星類(左下 → 右上)class Meteor {constructor() {this.reset()}reset() {this.x = Math.random() * canvas.width * 0.5 // 左側this.y = canvas.height - Math.random() * canvas.height * 0.5 // 下方this.len = Math.random() * 80 + 100this.speed = Math.random() * 4 + 6this.angle = Math.PI / 4 // ↗ 從左下到右上this.alpha = 1}update() {this.x += this.speed * Math.cos(this.angle)this.y -= this.speed * Math.sin(this.angle)this.alpha -= 0.01if (this.alpha <= 0) this.reset()}draw() {const x = this.xconst y = this.yconst len = this.lenctx.save()ctx.beginPath()ctx.moveTo(x, y)ctx.lineTo(x + len * Math.cos(this.angle),y - len * Math.sin(this.angle))ctx.strokeStyle = `rgba(255,255,255,${this.alpha})`ctx.lineWidth = 2ctx.shadowColor = '#fff'ctx.shadowBlur = 10ctx.stroke()ctx.restore()}}const stars = Array.from({ length: 200 }, () => new Star())const meteors = Array.from({ length: 3 }, () => new Meteor())function animate() {ctx.clearRect(0, 0, canvas.width, canvas.height)stars.forEach(star => {star.update()star.draw()})meteors.forEach(meteor => {meteor.update()meteor.draw()})requestAnimationFrame(animate)}animate()
})
</script><style lang="less" scoped>
.media-wrap {width: 100%;height: 100vh;overflow: hidden;background: linear-gradient(to bottom, #1b1e3f, #2c3e50, #3b2c59); // 深藍+紫色// ? 動態漸變星云背景background: linear-gradient(-45deg, #1b1e3f, #2c3e50, #3b2c59, #1b1e3f);background-size: 400% 400%;animation: galaxyMove 20s ease infinite;.star-canvas {position: absolute;top: 0;left: 0;width: 100%;height: 100%;z-index: 0;}.iphone {width: 380px;height: 85%;border-radius: 30px;position: relative;box-shadow: 0 0 0 10px black;margin: 40px auto;z-index: 1;.screen {width: 100%;height: 100%;border-radius: 30px;margin-top: 5%;overflow: hidden;}.notch {position: absolute;top: 0;left: 50%;transform: translateX(-50%);width: 130px;height: 30px;background: #000;border-bottom-left-radius: 20px;border-bottom-right-radius: 20px;z-index: 2000;}.side-button {position: absolute;width: 4px;background: #666;border-radius: 2px;&.power {height: 40px;right: -6px;top: 120px;}&.volume-up {height: 30px;left: -6px;top: 100px;}&.volume-down {height: 30px;left: -6px;top: 140px;}}}
}.title {position: relative;z-index: 2;text-align: left;margin-top: 20px;font-weight: 600;letter-spacing: 1px;text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);.gradient-text {font-size: 28px;background: linear-gradient(45deg, #f9d423, #ff4e50, #7b4397, #00c6ff);background-size: 300% 300%;-webkit-background-clip: text;-webkit-text-fill-color: transparent;animation: gradient-shift 8s ease infinite, pulse 2s infinite alternate;}
}.info {color: #fff;position: fixed;bottom: 10px;right: 10px;cursor: pointer;font-size: 16px;
}@keyframes galaxyMove {0% {background-position: 0% 50%;}50% {background-position: 100% 50%;}100% {background-position: 0% 50%;}
}@keyframes gradient-shift {0% {background-position: 0% 50%;}50% {background-position: 100% 50%;}100% {background-position: 0% 50%;}
}@keyframes pulse {0% {transform: scale(1);}100% {transform: scale(1.05);}
}
</style>
在我用AI 生成的這個項目中,最復雜的部分莫過于錄音功能了,通過大約半小時的反復提問與細節優化,終于通過代碼實現了語音錄入、自定義語音樣式、語音播放的功能。
AI的核心實現原理是通過原生錄音事件完成的
// 創建音頻上下文
const AudioContext = window.AudioContext || (window as any).webkitAudioContext
audioContext = new AudioContext()// 創建分析器
analyser = audioContext.createAnalyser()
analyser.fftSize = 256 // 必須是2的冪次方
const bufferLength = analyser.frequencyBinCount // 通常是fftSize的一半
dataArray = new Uint8Array(bufferLength)// ...// 請求錄音權限
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {resolve(stream)}).catch((err) => {console.error('獲取錄音權限失敗:', err)Toast('獲取錄音權限失敗,請檢查權限設置')reject(err)})// 創建MediaRecorder實例
recorder = new MediaRecorder(stream as MediaStream)
但經過實際測試,在IOS的webview中,這種方案兼容性不好,在低版本的IOS中,錄音播放異常,這與原生錄音方法生成的錄音格式有關。
于是,通過搜索,最終使用了record-core實現了錄音功能,并讓AI幫我重構了代碼。
對于數據的離線儲存,通過提問AI后,選用了indexDB的儲存方案。但在實測中,播放語音時會報錯:
于是,通過提問AI繼續修復與優化。
在經過不斷地測試與AI修復后,功能終于是沒有任何問題了。在整體項目生成后,就是利用AI提問不斷完善一些小功能
用AI生成項目的過程需要一步步的提問,讓AI優化細節,這個過程其實是比較漫長的,但對于不懂技術或者不想自己寫代碼的同學而言,這都不是什么問題。
項目開發完畢,就可以部署在線上體驗了。
項目部署
要發布線上,目前最方便的方式就是在Trae中配置MCP,一鍵部署。
部署前,我們需要先配置掘金的MCP,在 Trae 或 IDE 的 MCP 配置中,添加以下 JSON 配置:
{"mcpServers": {"juejin-deploy-mcp": {"command": "npx","args": ["--registry=https://registry.npmjs.org","-y","@juejin-team/mcp-server@latest"],"env": {"JUEJIN_TOKEN": ""}}}
}
注意,配置項中的JUEJIN_TOKEN
需要填寫你自己的掘金Token,獲取地址如下:
https://www.zhihu.com/
點擊添加后,你就可以在你的內置智能體中看到到
現在,你只需要切換到這個智能體,給出對應的部署提示即可。
部署成功后,會在對話框看到部署鏈接,點擊后就可以看到效果。
總結
通過本文可以看出,借助 AI 從零開發一個完整的程序已不再是夢想,這讓越來越多的人有機會成為創作者。而要用好 AI 開發產品,提示詞(prompt)的質量至關重要。令人驚喜的是,優秀的提示詞本身也可以由 AI 生成。用 AI 生成 prompt,再用這個 prompt 指導 AI 開發產品,這種“AI 調用 AI”的方式,既魔幻又充滿趣味。