前言
隨著生成式 AI 技術的快速發展,Google AI Studio 已成為開發者快速原型製作和測試 AI 應用的重要平台。然而,當你完成了令人興奮的 AI 專案後,如何將其部署到生產環境,讓更多使用者能夠實際體驗,便成為下一個重要課題。
Cloudflare Pages 作為現代化的靜態網站託管平台,提供了全球 CDN、自動 HTTPS、無限頻寬等優勢,並且與 GitHub 等版本控制系統深度整合,成為部署前端應用的理想選擇。本文將帶你一步步完成從 Google AI Studio 到 Cloudflare Pages 的完整部署流程。
為什麼選擇 Cloudflare Pages?
在眾多託管平台中,Cloudflare Pages 具有以下顯著優勢:
效能與速度
Cloudflare 擁有遍布全球超過 275 個城市的資料中心網路,能夠確保你的 AI 應用在世界各地都能快速載入。當使用者訪問你的網站時,內容會從最近的邊緣節點提供,大幅降低延遲時間。
安全性保障
平台內建 DDoS 防護、自動 SSL/TLS 憑證,以及 Web Application Firewall 功能,讓你無需額外配置即可享有企業級安全防護。這對於處理 AI 應用中的敏感資料特別重要。
開發者友善
與 GitHub、GitLab 的深度整合,支援自動化部署流程。每次推送程式碼到倉庫,Cloudflare Pages 都會自動建置並部署新版本,大幅提升開發效率。
成本優勢
Cloudflare Pages 提供慷慨的免費方案,包括無限頻寬、每月 500 次建置、以及協作功能。對於個人開發者和中小型專案來說,免費方案已經足夠使用。
前置準備工作
在開始部署之前,你需要準備以下工具和帳號。
必要帳號註冊
你需要擁有以下帳號:
- Google 帳號:用於訪問 Google AI Studio 並獲取 API 金鑰
- GitHub 帳號:用於託管專案原始碼並與 Cloudflare Pages 整合
- Cloudflare 帳號:用於部署和管理你的網站
開發環境設置
確保你的本地開發環境已安裝:
- Node.js 18.0 或更高版本
- npm 或 yarn 套件管理工具
- Git 版本控制系統
- 程式碼編輯器(推薦使用 VS Code)
Google AI Studio API 金鑰取得
登入 Google AI Studio 後,前往 API Keys 頁面,建立一個新的 API 金鑰。請妥善保管這個金鑰,因為它將用於你的應用程式與 Google AI 服務的通訊。建議為不同的專案建立不同的 API 金鑰,以便於管理和追蹤使用情況。
專案架構規劃
在開始編寫程式碼之前,了解整體架構是非常重要的。
前端與後端分離
現代 AI 應用通常採用前後端分離的架構。前端負責使用者介面和體驗,部署在 Cloudflare Pages 上。後端 API 呼叫則通過 Cloudflare Workers 或直接從瀏覽器端發送到 Google AI Studio API。
安全性考量
絕對不要將 API 金鑰直接寫在前端程式碼中。這會讓任何查看網頁原始碼的人都能取得你的金鑰。正確的做法是使用環境變數,或者透過 Cloudflare Workers 作為中介層來處理 API 請求。
選擇合適的前端框架
根據專案需求,你可以選擇不同的前端框架。React、Vue.js、Next.js、Nuxt.js 都是優秀的選擇,Cloudflare Pages 對這些框架都有良好的支援。本教學將以 React 為例進行說明,但概念可以輕鬆應用到其他框架。
建立 React 專案
讓我們從建立一個新的 React 專案開始。
使用 Vite 初始化專案
在終端機中執行以下指令,使用 Vite 建立一個新的 React 專案:
npm create vite@latest my-ai-app -- --template react
cd my-ai-app
npm install
Vite 相比於傳統的 Create React App,具有更快的開發伺服器啟動速度和熱模組替換效能,非常適合現代前端開發。
安裝必要套件
你需要安裝一些額外的套件來處理 API 請求和狀態管理:
npm install axios
npm install @google/generative-ai
axios 用於處理 HTTP 請求,而 @google/generative-ai 是 Google 官方提供的 JavaScript SDK,讓你更方便地與 AI Studio API 互動。
專案結構組織
建議採用以下目錄結構來組織你的專案:
my-ai-app/
├── src/
│ ├── components/ # React 元件
│ ├── services/ # API 服務層
│ ├── utils/ # 工具函數
│ ├── hooks/ # 自定義 React Hooks
│ ├── config/ # 配置檔案
│ ├── App.jsx
│ └── main.jsx
├── public/ # 靜態資源
├── .env.example # 環境變數範例
└── package.json
這種結構清晰分離了不同類型的程式碼,便於維護和擴展。
整合 Google AI Studio API
現在讓我們開始整合 Google AI Studio 的功能。
建立 API 服務層
在 src/services 目錄下建立 aiService.js 檔案:
import { GoogleGenerativeAI } from '@google/generative-ai';
const genAI = new GoogleGenerativeAI(import.meta.env.VITE_GOOGLE_AI_API_KEY);
export const generateContent = async (prompt) => {
try {
const model = genAI.getGenerativeModel({ model: 'gemini-pro' });
const result = await model.generateContent(prompt);
const response = await result.response;
return response.text();
} catch (error) {
console.error('AI 生成內容時發生錯誤:', error);
throw error;
}
};
export const generateContentStream = async (prompt, onChunk) => {
try {
const model = genAI.getGenerativeModel({ model: 'gemini-pro' });
const result = await model.generateContentStream(prompt);
for await (const chunk of result.stream) {
const chunkText = chunk.text();
onChunk(chunkText);
}
} catch (error) {
console.error('串流生成時發生錯誤:', error);
throw error;
}
};
這個服務層封裝了與 Google AI Studio API 的互動邏輯,提供了一般生成和串流生成兩種方式。
建立自定義 Hook
在 src/hooks 目錄下建立 useAI.js:
import { useState } from 'react';
import { generateContent, generateContentStream } from '../services/aiService';
export const useAI = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [response, setResponse] = useState('');
const generate = async (prompt) => {
setLoading(true);
setError(null);
setResponse('');
try {
const result = await generateContent(prompt);
setResponse(result);
return result;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
};
const generateStream = async (prompt) => {
setLoading(true);
setError(null);
setResponse('');
try {
await generateContentStream(prompt, (chunk) => {
setResponse(prev => prev + chunk);
});
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
};
return { generate, generateStream, loading, error, response };
};
這個 Hook 封裝了狀態管理邏輯,讓你在元件中可以輕鬆使用 AI 功能。
建立使用者介面元件
在 src/components 目錄下建立 AIChat.jsx:
import { useState } from 'react';
import { useAI } from '../hooks/useAI';
export const AIChat = () => {
const [input, setInput] = useState('');
const { generate, loading, error, response } = useAI();
const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim()) return;
await generate(input);
};
return (
<div className="ai-chat-container">
<form onSubmit={handleSubmit}>
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="輸入你的問題..."
disabled={loading}
/>
<button type="submit" disabled={loading}>
{loading ? '生成中...' : '送出'}
</button>
</form>
{error && <div className="error">{error}</div>}
{response && (
<div className="response">
<h3>AI 回應:</h3>
<p>{response}</p>
</div>
)}
</div>
);
};
這個元件提供了基本的聊天介面,使用者可以輸入問題並獲得 AI 回應。
環境變數配置
正確管理環境變數對於應用程式的安全性至關重要。
本地開發環境設定
在專案根目錄建立 .env 檔案:
VITE_GOOGLE_AI_API_KEY=你的API金鑰
同時建立 .env.example 檔案作為範本:
VITE_GOOGLE_AI_API_KEY=your_api_key_here
確保在 .gitignore 中已經包含 .env,避免將敏感資訊推送到 Git 倉庫。
Vite 環境變數使用規則
在 Vite 中,只有以 VITE_ 前綴開頭的環境變數才會被暴露給客戶端程式碼。這是一個重要的安全機制,防止伺服器端的敏感資訊意外洩露。
環境變數驗證
建立 src/config/env.js 來驗證必要的環境變數:
export const validateEnv = () => {
const requiredEnvVars = ['VITE_GOOGLE_AI_API_KEY'];
const missing = requiredEnvVars.filter(
varName => !import.meta.env[varName]
);
if (missing.length > 0) {
throw new Error(
`缺少必要的環境變數: ${missing.join(', ')}`
);
}
};
在應用程式啟動時呼叫這個函數,確保所有必要的配置都已就緒。
使用 Cloudflare Workers 保護 API 金鑰
對於生產環境,直接在前端使用 API 金鑰並不安全。最佳實踐是使用 Cloudflare Workers 作為代理層。
建立 Worker 專案
在專案根目錄建立 workers 資料夾,並初始化 Worker:
mkdir workers
cd workers
npm create cloudflare@latest ai-proxy
選擇 “Hello World” Worker 範本。
撰寫 Worker 程式碼
編輯 workers/ai-proxy/src/index.js:
export default {
async fetch(request, env) {
// 處理 CORS
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
});
}
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
try {
const { prompt } = await request.json();
// 呼叫 Google AI API
const response = await fetch(
`https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${env.GOOGLE_AI_API_KEY}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
}),
}
);
const data = await response.json();
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
} catch (error) {
return new Response(
JSON.stringify({ error: error.message }),
{
status: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
}
);
}
},
};
這個 Worker 接收前端的請求,使用儲存在 Worker 環境變數中的 API 金鑰呼叫 Google AI API,然後將結果回傳給前端。
部署 Worker
在 workers/ai-proxy 目錄下執行:
npx wrangler deploy
部署完成後,你會獲得一個 Worker URL,例如 https://ai-proxy.your-subdomain.workers.dev。
更新前端程式碼
修改 src/services/aiService.js,改為呼叫 Worker:
const WORKER_URL = import.meta.env.VITE_WORKER_URL;
export const generateContent = async (prompt) => {
try {
const response = await fetch(WORKER_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
});
const data = await response.json();
return data.candidates[0].content.parts[0].text;
} catch (error) {
console.error('AI 生成內容時發生錯誤:', error);
throw error;
}
};
現在 API 金鑰完全隱藏在 Worker 中,前端無法接觸到敏感資訊。
準備 Git 倉庫
版本控制是現代軟體開發的基石,也是 Cloudflare Pages 自動部署的基礎。
初始化 Git 倉庫
在專案根目錄執行:
git init
git add .
git commit -m "Initial commit: React app with Google AI Studio integration"
檢查 .gitignore
確保你的 .gitignore 檔案包含以下內容:
node_modules/
dist/
.env
.env.local
.DS_Store
*.log
這能防止不必要的檔案和敏感資訊被推送到倉庫。
建立 GitHub 倉庫
登入 GitHub,建立一個新的倉庫。可以選擇公開或私有,根據你的專案需求決定。建立後,按照 GitHub 提供的指示將本地倉庫連接到遠端倉庫:
git remote add origin https://github.com/你的使用者名稱/my-ai-app.git
git branch -M main
git push -u origin main
分支策略
對於持續部署的專案,建議採用以下分支策略:
main分支:生產環境,每次推送都會自動部署develop分支:開發環境,用於整合功能feature/*分支:功能開發分支
Cloudflare Pages 可以為不同分支建立不同的預覽環境,讓你在合併到主分支前就能測試變更。
連接 Cloudflare Pages
現在讓我們將專案部署到 Cloudflare Pages。
登入 Cloudflare Dashboard
前往 Cloudflare Dashboard,在左側選單中找到 “Pages” 選項。
建立新專案
點擊 “Create a project” 按鈕,選擇 “Connect to Git”。Cloudflare 會要求你授權訪問 GitHub 帳號。完成授權後,你會看到所有可用的倉庫列表。
選擇倉庫
找到並選擇剛才建立的 my-ai-app 倉庫。
設定建置配置
在配置頁面中,填寫以下資訊:
- Project name:為你的專案命名,這將成為預設域名的一部分
- Production branch:選擇
main - Framework preset:選擇 “Vite”
- Build command:
npm run build - Build output directory:
dist
Cloudflare Pages 會自動偵測你的框架並預填這些資訊,但仍建議檢查一遍確保正確。
配置環境變數
在 “Environment variables” 區塊,新增必要的環境變數。如果你使用 Worker 代理,需要新增:
VITE_WORKER_URL=https://ai-proxy.your-subdomain.workers.dev
如果直接在前端使用 API(不推薦用於生產環境),則新增:
VITE_GOOGLE_AI_API_KEY=你的API金鑰
注意 Cloudflare Pages 的環境變數分為 Production 和 Preview 兩種環境,你可以為不同環境設定不同的值。
開始部署
點擊 “Save and Deploy” 按鈕。Cloudflare Pages 會開始建置和部署你的專案。整個過程通常需要 2-5 分鐘,你可以在部署日誌中查看詳細進度。
自動化部署流程
一旦完成初始設定,後續的部署就會完全自動化。
Git 工作流程
當你對專案進行變更並推送到 GitHub 時:
git add .
git commit -m "Add new feature"
git push origin main
Cloudflare Pages 會自動偵測到新的推送,並觸發建置和部署流程。你會在 Cloudflare Dashboard 中看到新的部署記錄。
預覽部署
當你推送到非生產分支(例如 develop)時,Cloudflare Pages 會建立一個預覽部署。每個預覽部署都有獨特的 URL,你可以在正式上線前測試變更。這對於團隊協作特別有用。
回滾機制
如果新版本出現問題,你可以在 Cloudflare Dashboard 中輕鬆回滾到先前的部署版本。所有歷史部署都會被保留,讓你隨時可以恢復到任何一個穩定版本。
自訂域名設定
使用自訂域名可以讓你的應用程式看起來更專業。
新增自訂域名
在 Cloudflare Pages 專案設定中,找到 “Custom domains” 區塊,點擊 “Set up a custom domain”。
DNS 配置
如果你的域名已經在 Cloudflare 管理,系統會自動新增必要的 DNS 記錄。如果域名在其他服務商,你需要手動新增 CNAME 記錄:
Type: CNAME
Name: www (或你想要的子域名)
Value: your-project.pages.dev
SSL/TLS 憑證
Cloudflare 會自動為你的自訂域名提供免費的 SSL/TLS 憑證。通常在新增域名後的 24 小時內,憑證就會生效。
域名驗證
某些情況下,你可能需要透過 TXT 記錄驗證域名所有權。按照 Cloudflare 提供的指示完成驗證即可。
效能優化技巧
部署完成後,我們還可以進行一些優化來提升使用者體驗。
程式碼分割
利用 React 的 lazy loading 功能分割程式碼:
import { lazy, Suspense } from 'react';
const AIChat = lazy(() => import('./components/AIChat'));
function App() {
return (
<Suspense fallback={<div>載入中...</div>}>
<AIChat />
</Suspense>
);
}
這能減少初始載入的檔案大小,加快首次渲染速度。
圖片優化
使用現代圖片格式如 WebP,並實施延遲載入:
<img
src="image.webp"
alt="描述"
loading="lazy"
/>
快取策略
在 public 目錄建立 _headers 檔案,設定快取策略:
/assets/*
Cache-Control: public, max-age=31536000, immutable
/*.js
Cache-Control: public, max-age=31536000, immutable
/*.css
Cache-Control: public, max-age=31536000, immutable
這能讓瀏覽器長時間快取靜態資源,減少重複載入。
壓縮資源
確保 Vite 的生產建置啟用了壓縮。在 vite.config.js 中:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
},
},
},
});
這會移除 console.log 並壓縮程式碼,進一步減小檔案大小。
監控與除錯
了解應用程式的執行狀況對於維護至關重要。
Cloudflare Analytics
Cloudflare Pages 內建了分析功能,提供訪問統計、效能指標等資訊。你可以在 Dashboard 的 “Analytics” 標籤中查看詳細數據。
錯誤追蹤
整合 Sentry 或類似的錯誤追蹤服務:
npm install @sentry/react
在 src/main.jsx 中初始化:
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
});
這能幫助你快速發現和診斷生產環境中的問題。
效能監控
使用 Web Vitals 追蹤關鍵效能指標:
npm install web-vitals
在應用程式中記錄指標:
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
console.log(metric);
// 發送到你的分析服務
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
日誌管理
建立一個集中式的日誌服務:
// src/utils/logger.js
export const logger = {
info: (message, data) => {
console.log(`[INFO] ${message}`, data);
// 發送到日誌服務
},
error: (message, error) => {
console.error(`[ERROR] ${message}`, error);
// 發送到錯誤追蹤服務
},
warn: (message, data) => {
console.warn(`[WARN] ${message}`, data);
},
};
在整個應用程式中使用這個 logger,而不是直接使用 console。
安全性最佳實踐
保護你的應用程式和使用者資料是首要任務。
API 金鑰保護
永遠不要在前端程式碼中硬編碼 API 金鑰。使用環境變數和 Worker 代理層是必須的。定期輪換 API 金鑰,並為不同環境使用不同的金鑰。
內容安全政策(CSP)
在 public/_headers 中新增 CSP 標頭:
/*
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://your-worker.workers.dev
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
這能防止 XSS 攻擊和其他常見的網頁安全威脅。
輸入驗證
永遠驗證和清理使用者輸入:
const sanitizeInput = (input) => {
// 移除潛在危險的字元
return input
.replace(/[<>]/g, '')
.trim()
.slice(0, 1000); // 限制長度
};
const handleSubmit = async (e) => {
e.preventDefault();
const sanitized = sanitizeInput(input);
await generate(sanitized);
};
速率限制
在 Worker 中實施速率限制,防止濫用:
// 在 Worker 中
const rateLimiter = {
requests: new Map(),
check: (ip) => {
const now = Date.now();
const userRequests = rateLimiter.requests.get(ip) || [];
// 保留最近一分鐘的請求
const recentRequests = userRequests.filter(
time => now - time < 60000
);
if (recentRequests.length >= 10) {
return false; // 超過限制
}
recentRequests.push(now);
rateLimiter.requests.set(ip, recentRequests);
return true;
}
};
進階功能實作
當基礎部署完成後,你可以進一步擴展應用程式的功能,提供更豐富的使用者體驗。
實作串流回應
串流回應能讓使用者即時看到 AI 生成的內容,而不需要等待完整回應:
// src/services/aiService.js
export const generateContentStream = async (prompt, onChunk) => {
const response = await fetch(WORKER_URL + '/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
onChunk(chunk);
}
};
更新 Worker 以支援串流:
// workers/ai-proxy/src/index.js
async function handleStreamRequest(request, env) {
const { prompt } = await request.json();
const stream = new TransformStream();
const writer = stream.writable.getWriter();
// 非同步處理 AI 請求
(async () => {
try {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1/models/gemini-pro:streamGenerateContent?key=${env.GOOGLE_AI_API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
}),
}
);
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
await writer.write(value);
}
} finally {
await writer.close();
}
})();
return new Response(stream.readable, {
headers: {
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
},
});
}
對話歷史記錄
實作對話記錄功能,讓 AI 能理解上下文:
// src/hooks/useConversation.js
import { useState } from 'react';
export const useConversation = () => {
const [messages, setMessages] = useState([]);
const addMessage = (role, content) => {
setMessages(prev => [...prev, { role, content, timestamp: Date.now() }]);
};
const clearHistory = () => {
setMessages([]);
};
const getContext = (maxMessages = 10) => {
return messages.slice(-maxMessages).map(msg => ({
role: msg.role,
parts: [{ text: msg.content }]
}));
};
return { messages, addMessage, clearHistory, getContext };
};
在 AI 請求中包含對話歷史:
export const generateWithContext = async (prompt, conversationHistory) => {
const response = await fetch(WORKER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt,
history: conversationHistory,
}),
});
return await response.json();
};
多模態支援(圖片輸入)
讓你的應用程式支援圖片輸入功能:
// src/components/ImageUpload.jsx
import { useState } from 'react';
export const ImageUpload = ({ onImageSelect }) => {
const [preview, setPreview] = useState(null);
const handleFileChange = async (e) => {
const file = e.target.files[0];
if (!file) return;
// 建立預覽
const reader = new FileReader();
reader.onload = (e) => setPreview(e.target.result);
reader.readAsDataURL(file);
// 轉換為 base64
const base64 = await fileToBase64(file);
onImageSelect({
data: base64.split(',')[1],
mimeType: file.type,
});
};
const fileToBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
};
return (
<div className="image-upload">
<input
type="file"
accept="image/*"
onChange={handleFileChange}
/>
{preview && (
<img src={preview} alt="預覽" style={{ maxWidth: '200px' }} />
)}
</div>
);
};
更新 API 服務以處理圖片:
export const generateWithImage = async (prompt, imageData) => {
const response = await fetch(WORKER_URL + '/vision', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt,
image: imageData,
}),
});
return await response.json();
};
本地資料持久化
使用 IndexedDB 儲存對話記錄:
// src/utils/storage.js
const DB_NAME = 'AIAppDB';
const STORE_NAME = 'conversations';
export const openDB = () => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true });
}
};
});
};
export const saveConversation = async (conversation) => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.add({
...conversation,
savedAt: Date.now(),
});
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
export const loadConversations = async () => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
測試策略
確保應用程式的穩定性需要完善的測試。
單元測試
使用 Vitest 進行單元測試:
npm install -D vitest @testing-library/react @testing-library/jest-dom
建立測試檔案 src/services/aiService.test.js:
import { describe, it, expect, vi } from 'vitest';
import { generateContent } from './aiService';
describe('AI Service', () => {
it('應該成功生成內容', async () => {
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({
candidates: [{
content: { parts: [{ text: '測試回應' }] }
}]
}),
})
);
const result = await generateContent('測試問題');
expect(result).toBe('測試回應');
});
it('應該處理錯誤情況', async () => {
global.fetch = vi.fn(() => Promise.reject(new Error('網路錯誤')));
await expect(generateContent('測試')).rejects.toThrow();
});
});
元件測試
測試 React 元件:
// src/components/AIChat.test.jsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { AIChat } from './AIChat';
describe('AIChat Component', () => {
it('應該渲染輸入框和按鈕', () => {
render(<AIChat />);
expect(screen.getByPlaceholderText(/輸入你的問題/)).toBeInTheDocument();
expect(screen.getByText(/送出/)).toBeInTheDocument();
});
it('當輸入為空時應該禁用送出按鈕', () => {
render(<AIChat />);
const button = screen.getByText(/送出/);
const input = screen.getByPlaceholderText(/輸入你的問題/);
expect(button).toBeEnabled();
fireEvent.change(input, { target: { value: '' } });
// 測試邏輯...
});
});
端對端測試
使用 Playwright 進行端對端測試:
npm install -D @playwright/test
建立 tests/e2e/basic.spec.js:
import { test, expect } from '@playwright/test';
test('使用者可以發送訊息並獲得回應', async ({ page }) => {
await page.goto('http://localhost:5173');
await page.fill('textarea', '你好,請介紹一下自己');
await page.click('button:has-text("送出")');
await expect(page.locator('.response')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.response')).toContainText('AI');
});
在 package.json 中新增測試腳本:
{
"scripts": {
"test": "vitest",
"test:e2e": "playwright test",
"test:coverage": "vitest --coverage"
}
}
CI/CD 進階配置
提升部署流程的自動化程度。
GitHub Actions 工作流程
建立 .github/workflows/deploy.yml:
name: Deploy to Cloudflare Pages
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linter
run: npm run lint
deploy:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v3
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: my-ai-app
directory: dist
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
環境分離
為不同環境建立不同的配置檔案:
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig(({ mode }) => ({
plugins: [react()],
define: {
'import.meta.env.VITE_API_ENDPOINT': JSON.stringify(
mode === 'production'
? 'https://api.myapp.com'
: 'http://localhost:8787'
),
},
}));
自動化測試報告
整合測試覆蓋率報告到 CI/CD:
- name: Generate coverage report
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/coverage-final.json
效能監控與分析
深入了解應用程式的效能表現。
整合 Google Analytics
安裝並配置 GA4:
npm install react-ga4
初始化追蹤:
// src/utils/analytics.js
import ReactGA from 'react-ga4';
export const initGA = () => {
ReactGA.initialize(import.meta.env.VITE_GA_MEASUREMENT_ID);
};
export const logPageView = () => {
ReactGA.send({ hitType: 'pageview', page: window.location.pathname });
};
export const logEvent = (category, action, label) => {
ReactGA.event({
category,
action,
label,
});
};
追蹤 AI 互動:
// 在發送 AI 請求時
logEvent('AI', 'generate', 'user_prompt');
// 追蹤回應時間
const startTime = Date.now();
await generate(prompt);
const duration = Date.now() - startTime;
logEvent('Performance', 'ai_response_time', duration);
Lighthouse CI 整合
自動化效能評分:
npm install -D @lhci/cli
建立 lighthouserc.js:
module.exports = {
ci: {
collect: {
startServerCommand: 'npm run preview',
url: ['http://localhost:4173'],
numberOfRuns: 3,
},
assert: {
preset: 'lighthouse:recommended',
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'categories:best-practices': ['error', { minScore: 0.9 }],
'categories:seo': ['error', { minScore: 0.9 }],
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
在 GitHub Actions 中執行:
- name: Run Lighthouse CI
run: |
npm run build
npm run lhci -- --upload.target=temporary-public-storage
使用者體驗優化
提升應用程式的整體使用體驗。
實作載入狀態
建立優雅的載入動畫:
// src/components/LoadingSpinner.jsx
export const LoadingSpinner = () => (
<div className="loading-spinner">
<div className="spinner"></div>
<p>AI 正在思考中...</p>
</div>
);
配合 CSS 動畫:
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
錯誤處理與重試機制
實作智慧重試邏輯:
// src/utils/retry.js
export const retryWithBackoff = async (
fn,
maxRetries = 3,
baseDelay = 1000
) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = baseDelay * Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
};
使用方式:
const response = await retryWithBackoff(
() => generateContent(prompt),
3,
1000
);
響應式設計
確保在各種裝置上都有良好體驗:
/* src/styles/responsive.css */
.ai-chat-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
@media (max-width: 768px) {
.ai-chat-container {
padding: 10px;
}
textarea {
font-size: 16px; /* 防止 iOS Safari 自動縮放 */
}
}
@media (max-width: 480px) {
.response {
font-size: 14px;
}
}
深色模式支援
實作主題切換功能:
// src/hooks/useTheme.js
import { useState, useEffect } from 'react';
export const useTheme = () => {
const [theme, setTheme] = useState(
localStorage.getItem('theme') || 'light'
);
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return { theme, toggleTheme };
};
CSS 變數定義:
:root {
--bg-color: #ffffff;
--text-color: #000000;
--border-color: #e0e0e0;
}
[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #ffffff;
--border-color: #333333;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
成本控制與配額管理
有效管理 API 使用成本。
實作使用量追蹤
在 Worker 中記錄 API 使用:
// Worker 中
const trackUsage = async (userId, tokens) => {
await env.USAGE_KV.put(
`usage:${userId}:${new Date().toISOString().split('T')[0]}`,
JSON.stringify({
requests: 1,
tokens: tokens,
timestamp: Date.now(),
}),
{ expirationTtl: 86400 * 30 } // 保留 30 天
);
};
設定使用限制
實作每日限額檢查:
const checkQuota = async (userId) => {
const today = new Date().toISOString().split('T')[0];
const usage = await env.USAGE_KV.get(`usage:${userId}:${today}`);
if (usage) {
const data = JSON.parse(usage);
if (data.requests >= 100) { // 每日限制 100 次
throw new Error('已達到每日使用限額');
}
}
return true;
};
快取策略降低成本
對常見問題實作快取:
// src/utils/cache.js
const CACHE_DURATION = 3600000; // 1 小時
export class ResponseCache {
constructor() {
this.cache = new Map();
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() - item.timestamp > CACHE_DURATION) {
this.cache.delete(key);
return null;
}
return item.data;
}
set(key, data) {
this.cache.set(key, {
data,
timestamp: Date.now(),
});
}
}
export const responseCache = new ResponseCache();
國際化支援
讓你的應用程式支援多語言。
整合 i18n
安裝 react-i18next:
npm install react-i18next i18next
配置多語言:
// src/i18n/config.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const resources = {
en: {
translation: {
"welcome": "Welcome to AI Chat",
"inputPlaceholder": "Type your question...",
"submit": "Submit",
"loading": "Generating...",
}
},
zh: {
translation: {
"welcome": "歡迎使用 AI 聊天",
"inputPlaceholder": "輸入你的問題...",
"submit": "送出",
"loading": "生成中...",
}
}
};
i18n
.use(initReactI18next)
.init({
resources,
lng: 'zh',
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
});
export default i18n;
在元件中使用:
import { useTranslation } from 'react-i18next';
export const AIChat = () => {
const { t, i18n } = useTranslation();
return (
<div>
<h1>{t('welcome')}</h1>
<textarea placeholder={t('inputPlaceholder')} />
<button>{t('submit')}</button>
</div>
);
};
常見問題排解
部署過程中可能遇到的問題及解決方案。
建置失敗
如果建置過程失敗,首先檢查建置日誌。常見原因包括環境變數未設定、依賴套件版本衝突、或記憶體不足。
解決方案包括確認所有必要的環境變數都已在 Cloudflare Pages 設定中新增,鎖定套件版本避免意外更新,或在 package.json 中增加 Node.js 記憶體限制。
API 請求失敗
CORS 錯誤是最常見的問題。確保你的 Worker 正確設定了 CORS 標頭。如果直接從前端呼叫 Google AI API,確認 API 金鑰有效且未超過配額。
效能問題
如果頁面載入緩慢,使用 Chrome DevTools 的 Performance 面板分析瓶頸。常見優化包括啟用程式碼分割、壓縮圖片、實作懶載入、以及利用 Cloudflare 的快取功能。
部署後功能異常
檢查瀏覽器控制台是否有 JavaScript 錯誤。確認生產環境和開發環境的環境變數設定一致。使用 Cloudflare Pages 的預覽部署功能在合併前測試變更。
維護與更新
持續維護確保應用程式長期穩定運行。
定期更新依賴
每月檢查並更新套件:
npm outdated
npm update
對於重大版本更新,先在開發環境測試後再部署到生產環境。
監控安全漏洞
使用 npm audit 檢查安全問題:
npm audit
npm audit fix
訂閱 GitHub Security Alerts,及時收到安全漏洞通知。
備份與恢復
定期備份重要資料和配置。Cloudflare Pages 會保留所有歷史部署,但建議額外備份環境變數配置和 Worker 程式碼。
效能基準測試
建立效能基準並定期測試:
// benchmarks/performance.test.js
import { test, expect } from '@playwright/test';
test('首頁載入時間應小於 3 秒', async ({ page }) => {
const start = Date.now();
await page.goto('https://your-app.pages.dev');
const duration = Date.now() - start;
expect(duration).toBeLessThan(3000);
});
擴展與進階應用
進一步擴展應用程式的可能性。
整合其他 AI 模型
除了 Google AI Studio,你可以整合其他 AI 服務:
// src/services/multiModelService.js
export const generateWithMultipleModels = async (prompt) => {
const results = await Promise.all([
generateWithGoogle(prompt),
generateWithOpenAI(prompt),
generateWithAnthropic(prompt),
]);
return {
google: results[0],
openai: results[1],
anthropic: results[2],
};
};
實作 WebSocket 即時通訊
對於需要即時互動的應用,可以使用 Cloudflare Durable Objects:
// Worker with Durable Objects
export class ChatRoom {
async fetch(request) {
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
server.accept();
server.addEventListener('message', async (event) => {
const response = await generateContent(event.data);
server.send(JSON.stringify({ response }));
});
return new Response(null, {
status: 101,
webSocket: client,
});
}
}
資料分析與視覺化
整合圖表庫展示使用統計:
npm install recharts
建立統計儀表板:
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
export const UsageChart = ({ data }) => (
<LineChart width={600} height={300} data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="requests" stroke="#8884d8" />
</LineChart>
);
結語
完成本教學後,你已經掌握了將 Google AI Studio 專案部署到 Cloudflare Pages 的完整流程。從基礎設定到進階優化,從安全性配置到效能調校,這些知識將幫助你建立專業級的 AI 應用程式。
部署只是開始,持續監控、優化和更新才能確保應用程式長期成功。記得定期檢查效能指標、關注使用者反饋、並保持技術堆疊的更新。隨著 AI 技術的快速發展,也要持續關注 Google AI Studio 和 Cloudflare 的新功能,適時整合到你的專案中。
最重要的是,始終將使用者體驗放在首位。快速的載入時間、直覺的介面設計、以及可靠的功能,才是一個成功 AI 應用的關鍵。祝你的 AI 專案順利上線,為使用者帶來價值!
相關資源
附錄:完整專案檢查清單
部署前確認事項:
- 所有環境變數已正確設定
- API 金鑰安全儲存且未暴露在前端
- 所有測試都通過
- 建置過程無錯誤或警告
- 效能指標符合預期(Lighthouse 分數 > 90)
- 安全性標頭已配置
- 自訂域名已設定並生效
- 監控和錯誤追蹤已啟用
- 文件和註解完整
- Git 倉庫整潔且有意義的提交訊息
遵循這份檢查清單,確保你的專案以最佳狀態上線。

