前言
當你需要讓非技術人員也能輕鬆管理資料時,Google Sheets 雖然功能強大,但對於不熟悉試算表的使用者來說,仍然存在一些挑戰:容易誤刪格式、不小心修改公式、需要記住欄位對應關係等。
最近我在思考一個問題:為什麼每次做內部工具都要從資料庫、API、後端邏輯開始搞起? 如果只是需要一個簡單的資料管理系統,能不能用更輕量的方式實現?於是我嘗試了 將 Google Sheets 當作後端資料庫使用,這樣的好處顯而易見:
- ✅ 零後端成本:不需要架設資料庫、不需要寫 API 端點、不需要處理伺服器部署
- ✅ 即開即用:Google Sheets 本身就是一個強大的資料管理工具
- ✅ 團隊協作友善:熟悉 Sheets 的人可以直接編輯資料
但這樣的架構也帶來了新的挑戰:
- ❌ 權限設定複雜:需要平衡「方便存取」和「資料安全」
- ❌ 缺乏防呆機制:使用者可能胡亂操作,導致資料混亂
- ❌ 使用者體驗不佳:直接編輯 Sheet 對非技術人員不夠友善
為了解決這些問題,我設計了 WebApp + Sheet 側邊欄雙介面系統,打造一個既能保持 Google Sheets 靈活性、又能提供防呆保護的商品管理系統(這裡的「商品管理系統」只是一個範例,你可以套用到任何需要資料管理的場景)。
雙介面設計的核心理念:
- WebApp 獨立介面:提供簡潔的表單,讓非技術人員也能快速上手,並可實作細緻的權限控制
- Sheet 側邊欄:提供進階編輯功能,適合熟悉 Google Sheets 的使用者快速修改資料
透過 AI 輔助開發,我在短時間內完成了這套系統的搭建。本文將分享整個開發過程,包含:
✨ 如何用 AI 輔助快速打造專業的 WebApp 表單 ✨ URL 自動解析功能的實作(貼上 URL → 自動擷取標題、圖片、描述) ✨ 雙介面並存的設計策略與權限管理的權衡 ✨ Vue 3 在 Google Apps Script 環境的整合技巧 ✨ 本地開發環境設定(clasp + Git 版本控制)
無論你是想用 Google Sheets 取代傳統後端,或是對 AI 輔助開發感到好奇,這篇文章都能提供實用的參考。
一、系統架構概覽
這套系統採用三層架構設計:
使用者介面層:雙介面並存
- WebApp(獨立網頁):簡化表單、URL 自動解析、即時驗證
- Sheet 側邊欄:快速編輯、批次查看、資料預填
後端邏輯層:Google Apps Script
doGet()- WebApp 入口點parseURL()- URL 自動解析saveData()/updateData()- 資料讀寫validateForm()- 表單驗證
資料儲存層:Google Sheets
- 零成本的資料庫解決方案
- 內建版本歷史記錄
- 支援多人協作
技術棧選擇
| 技術 | 用途 | 為何選擇 |
|---|---|---|
| Google Apps Script | 後端邏輯 | 與 Google Sheets 無縫整合、免費部署 |
| Vue 3 (CDN) | 前端框架 | 輕量、響應式、易於整合 |
| Tailwind CSS (CDN) | 樣式框架 | 快速打造專業介面 |
二、WebApp 獨立輸入介面
這是整個系統的核心,也是 AI 輔助開發發揮最大價值的地方。讓我們一步步來看如何打造這個介面。
2.1 基本架構設計
首先,建立 WebApp 的基本骨架。在 Google Apps Script 中,WebApp 的入口點是 doGet() 函數:
// Code.gs - WebApp 入口function doGet() { return HtmlService.createTemplateFromFile('WebApp') .evaluate() .setTitle('商品管理系統') .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);}
// 用於載入其他 HTML 檔案的輔助函數function include(filename) { return HtmlService.createHtmlOutputFromFile(filename).getContent();}WebApp 的 HTML 結構採用模組化設計,將 CSS 和 JavaScript 分離:
<!DOCTYPE html><html lang="zh-TW"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>商品管理系統</title>
<!-- 載入樣式表 --> <?!= include('Stylesheet'); ?></head><body> <div id="app"> <!-- Vue 應用將掛載在這裡 --> </div>
<!-- 載入 JavaScript --> <?!= include('JavaScript'); ?></body></html>2.2 AI 輔助打造表單介面
這是整個開發過程中 AI 協助最顯著的部分。我使用了具體的 prompt 來快速生成 Vue 表單組件:
💡 AI 協助實例
我的 Prompt:「建立一個商品輸入表單,包含以下欄位:商品名稱、商品描述、圖片 URL、價格。使用 Vue 3 Composition API 和 Tailwind CSS。需要包含即時驗證和清除按鈕。」
AI 的產出:完整的 Vue 組件結構,包含響應式資料綁定、表單驗證邏輯、以及 Tailwind 樣式。我只需要微調欄位對應和整合後端 API。
節省時間:約 60% 的開發時間,讓我能專注於業務邏輯而非語法細節。
最終的 JavaScript 結構如下:
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script><script>const { createApp, ref, watch, computed } = Vue;
createApp({ setup() { // 表單資料 const formData = ref({ name: '', description: '', imageUrl: '', price: '' });
// 驗證錯誤 const errors = ref({});
// 載入狀態 const isLoading = ref(false);
// 表單是否有效 const isValid = computed(() => { return formData.value.name && formData.value.imageUrl && formData.value.price && Object.keys(errors.value).length === 0; });
// 即時驗證 watch(() => formData.value.price, (newPrice) => { if (newPrice && isNaN(newPrice)) { errors.value.price = '價格必須是數字'; } else { delete errors.value.price; } });
// 儲存資料 const saveData = () => { if (!isValid.value) { alert('請填寫所有必填欄位'); return; }
isLoading.value = true;
google.script.run .withSuccessHandler(() => { alert('儲存成功!'); clearForm(); isLoading.value = false; }) .withFailureHandler((error) => { alert('儲存失敗:' + error); isLoading.value = false; }) .saveToSheet(formData.value); };
// 清除表單 const clearForm = () => { formData.value = { name: '', description: '', imageUrl: '', price: '' }; errors.value = {}; };
return { formData, errors, isLoading, isValid, saveData, clearForm }; }}).mount('#app');</script>2.3 URL 自動解析功能 ⭐
這是最受使用者歡迎的功能!當使用者貼上商品 URL 時,系統會自動擷取頁面的標題、圖片和描述,大幅提升輸入效率。
前端實作:
// 在 setup() 中新增const urlInput = ref('');
// 監聽 URL 輸入watch(urlInput, async (newUrl) => { if (!newUrl) return;
// 簡單的 URL 驗證 const urlPattern = /^https?:\/\/.+/; if (!urlPattern.test(newUrl)) { return; }
isLoading.value = true;
// 呼叫後端 API 解析 URL google.script.run .withSuccessHandler((result) => { if (result.success) { formData.value.name = result.title || ''; formData.value.imageUrl = result.image || ''; formData.value.description = result.description || ''; } isLoading.value = false; }) .withFailureHandler((error) => { console.error('URL 解析失敗:', error); isLoading.value = false; }) .parseURL(newUrl);});後端實作:
function parseURL(url) { try { // 擷取網頁 HTML const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true, followRedirects: true });
const html = response.getContentText();
// 解析 Open Graph meta tags const title = extractMetaTag(html, 'og:title') || extractTitle(html); const image = extractMetaTag(html, 'og:image'); const description = extractMetaTag(html, 'og:description') || extractMetaTag(html, 'description');
return { success: true, title: title, image: image, description: description }; } catch (e) { return { success: false, error: e.message }; }}
// 輔助函數:提取 meta tagfunction extractMetaTag(html, property) { // 匹配 og: 開頭的 meta tag const ogPattern = new RegExp(`<meta\\s+property=["']${property}["']\\s+content=["']([^"']+)["']`, 'i'); // 匹配 name 屬性的 meta tag const namePattern = new RegExp(`<meta\\s+name=["']${property}["']\\s+content=["']([^"']+)["']`, 'i');
const ogMatch = html.match(ogPattern); if (ogMatch) return ogMatch[1];
const nameMatch = html.match(namePattern); if (nameMatch) return nameMatch[1];
return null;}
// 輔助函數:提取 title 標籤function extractTitle(html) { const titleMatch = html.match(/<title>([^<]+)<\/title>/i); return titleMatch ? titleMatch[1] : null;}使用效果對比:
| 操作方式 | 手動輸入 | URL 自動解析 |
|---|---|---|
| 輸入時間 | 2-3 分鐘 | 5-10 秒 |
| 錯誤率 | 較高(人工輸入錯誤) | 極低(自動擷取) |
| 使用者體驗 | 繁瑣 | 快速便捷 |
| 適用場景 | 自訂商品 | 來自電商網站的商品 |
2.4 前端表單驗證
為了提供更好的使用者體驗,我實作了即時驗證機制,在使用者輸入時立即顯示錯誤提示。關鍵的實作要點:
即時驗證邏輯:
- 使用 Vue 的
watch監聽表單欄位變更 - 錯誤時在欄位下方顯示紅色提示訊息
- 使用
:class動態綁定錯誤樣式(紅框) - 加入圖片預覽功能(驗證 URL 有效性)
驗證機制對比:
| 項目 | 傳統驗證(提交後) | 即時驗證(推薦) |
|---|---|---|
| 使用者體驗 | 提交後才發現錯誤,需重新填寫 | 輸入時立即看到錯誤,可即時修正 |
| 錯誤定位 | 不明確,需使用者自行檢查 | 精準指出問題欄位並標記紅色 |
| 視覺回饋 | 僅 alert 提示 | 欄位標記、錯誤訊息、圖片預覽 |
| 實作複雜度 | 簡單 | 中等(需 watch 監聽) |
三、Sheet 側邊欄編輯功能
對於熟悉 Google Sheets 的使用者,直接在 Sheet 中編輯可能更有效率。因此我同時實作了側邊欄編輯功能。
3.1 觸發機制與授權陷阱 ⚠️
在 Google Sheets 中,我在 H 欄設定了「📝 編輯」按鈕。當使用者點擊時,會開啟側邊欄顯示編輯表單。
⚠️ 重要:Simple Trigger 無法使用 showSidebar()
showSidebar() 需要授權(scope: script.container.ui),但 Simple Trigger 不能呼叫需要授權的服務。因此必須改用 Installable Trigger:
// ❌ 錯誤:onEdit (Simple Trigger) 無法使用 showSidebar()function onEdit(e) { // 這會失敗!Simple Trigger 無法呼叫需要授權的服務 SpreadsheetApp.getUi().showSidebar(...); // 權限錯誤}
// ✅ 正確:使用 Installable Trigger(必須手動重新命名)function onEditInstallable(e) { // 檢查是否點擊了編輯按鈕(H 欄) if (e.range.getColumn() === 8 && e.value === '📝 編輯') { const row = e.range.getRow(); showUpdateModal(row); }}
function showUpdateModal(row) { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); const rowData = sheet.getRange(row, 1, 1, 7).getValues()[0];
const data = { row: row, name: rowData[0], description: rowData[1], imageUrl: rowData[2], price: rowData[3] };
const template = HtmlService.createTemplateFromFile('UpdateModal'); template.data = JSON.stringify(data);
SpreadsheetApp.getUi().showSidebar( template.evaluate().setTitle('編輯商品').setWidth(400) );}設定 Installable Trigger 步驟:
- 將函數重新命名(例如:
onEdit→onEditInstallable) - 在 Apps Script 編輯器中:「觸發條件」→「新增觸發條件」
- 選擇:
- 函數:
onEditInstallable - 事件來源:「來自試算表」
- 事件類型:「編輯時」
- 函數:
Installable Trigger 特性:
- ✅ 可使用需要授權的服務(如
showSidebar()) - ✅ 最長執行時間 6 分鐘(vs Simple Trigger 30 秒)
- ❌ 首次使用需要授權:使用者第一次觸發時必須授權
- ⚠️ 多帳號登入陷阱:如果使用者登入多個 Google 帳號,可能會用錯誤的帳號授權
3.2 與 WebApp 共用驗證邏輯
側邊欄與 WebApp 共用相同的驗證函數,確保一致性:
// Validation.html - 共用驗證函數const ValidationUtils = { isValidURL(url) { return /^https?:\/\/.+/.test(url); },
isValidPrice(price) { return !isNaN(price) && parseFloat(price) >= 0; },
validateForm(formData) { const errors = {}; if (!formData.name?.trim()) errors.name = '商品名稱為必填'; if (!this.isValidURL(formData.imageUrl)) errors.imageUrl = '請輸入有效的圖片 URL'; if (!this.isValidPrice(formData.price)) errors.price = '請輸入有效的價格'; return errors; }};在 WebApp 和 UpdateModal 中引入:<?!= include('Validation'); ?>
四、雙介面整合設計
現在我們有了兩個介面,接下來要思考如何讓它們和諧共存。
4.1 使用者分流策略
不同類型的使用者適合不同的介面:
| 使用者類型 | 推薦介面 | 適用情境 | 優勢 |
|---|---|---|---|
| 非技術人員 | WebApp | 首次使用、大量新增資料、不熟悉試算表 | ✅ 簡化介面 ✅ 防呆設計 ✅ URL 自動解析 |
| 熟悉 Sheet 的使用者 | 側邊欄 | 快速修改、批次查看、需要複製貼上 | ✅ 無需切換視窗 ✅ 可直接查看所有資料 ✅ 熟悉的操作方式 |
| 管理員 | 兩者皆可 | 依情境靈活選擇 | ✅ 彈性最大 ✅ 可選擇最適合的工具 |
實際應用場景:
- 週一上新品:行銷人員使用 WebApp 批次新增 20 個新商品,利用 URL 自動解析快速完成
- 臨時價格調整:營運人員直接在 Sheet 中修改價格,點擊「編輯」按鈕微調其他資訊
- 月底資料核對:主管在 Sheet 中瀏覽所有資料,發現錯誤後使用側邊欄編輯
4.2 權限控制與多人協作
部署設定重點:
- 執行身分:根據需求選擇(詳見 4.4 節)
- 存取權限:可設為「知道連結的所有人」或「僅限網域內使用者」
- 多人協作:使用
LockService避免同時編輯衝突
// Code.gs - 使用鎖定機制避免衝突function saveToSheet(data, userEmail) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// 記錄資料與操作者 sheet.appendRow([ data.name, data.description, data.imageUrl, data.price, new Date(), userEmail // 從前端傳入或從 Session.getActiveUser() 取得(視執行模式而定) ]);
return { success: true }; } catch (e) { return { success: false, error: '系統忙碌中,請稍後再試' }; } finally { lock.releaseLock(); }}4.3 資料同步策略
| 衝突情境 | 解決方案 |
|---|---|
| 兩人同時新增資料 | ✅ 使用 LockService 序列化寫入 |
| 同時編輯同一列 | ✅ 加入「更新時間」欄位,後寫入的覆蓋 |
| 資料格式不一致 | ✅ 統一驗證函數,確保格式一致 |
4.4 權限管理的兩難抉擇
在實作雙介面系統時,權限管理是最需要權衡的部分。兩種介面在權限設計上有截然不同的特性:
WebApp 的權限機制
WebApp 部署時有兩種執行模式,選擇不同會影響權限行為:
模式一:Execute as me(以開發者身分執行)
- ✅ 使用者無需授權,開啟即用
- ✅ 適合內部工具,降低使用門檻
- ❌ 無法取得使用者身分:
Session.getActiveUser()回傳空字串 - ❌ 所有操作都以開發者身分執行
模式二:Execute as user accessing(以存取者身分執行)
- ✅ 可追蹤操作者:
Session.getActiveUser()回傳使用者 email - ✅ 細緻權限控制(每人用自己的權限)
- ❌ 授權流程複雜:使用者首次使用需多次點擊授權
- ❌ 使用者體驗較差(非技術人員容易卡關)
// 模式二:Execute as user accessingfunction saveToSheet(data) { const user = Session.getActiveUser().getEmail(); // ✅ 可取得使用者身分 const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.appendRow([ data.name, data.description, data.imageUrl, data.price, new Date(), user // ✅ 記錄是誰操作的 ]);}Sheet 側邊欄的權限機制
Installable Trigger(如 onEditInstallable)觸發的側邊欄有以下特性:
✅ 優勢:
- 可使用授權服務:能呼叫
showSidebar()、MailApp等服務 - 執行時間較長:最長 6 分鐘(vs Simple Trigger 30 秒)
- 功能完整:沒有 Simple Trigger 的諸多限制
- ✅ 可追蹤操作者:因為基於 Apps Script 必須授權,所以 Google Sheets 的版本歷史可以知道誰修改了資料
❌ 劣勢:
- 首次需要授權:使用者第一次觸發時必須經過授權流程
- 多帳號登入問題:登入多個 Google 帳號時,容易用錯誤的帳號授權
- 授權體驗不佳:比 WebApp 模式一更複雜(需要手動設定 Trigger)
// 側邊欄中修改資料function updateSheetRow(rowIndex, data) { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.getRange(rowIndex, 1, 1, 4).setValues([[ data.name, data.description, data.imageUrl, data.price ]]);
// 💡 不需要手動記錄操作者 // Google Sheets 的「版本歷史記錄」會自動記錄是誰修改的 // 檔案 → 版本記錄 → 查看版本記錄}權限管理對比表
| 項目 | WebApp(模式一:Execute as me) | WebApp(模式二:Execute as user) | Sheet 側邊欄(Installable Trigger) |
|---|---|---|---|
| 授權流程 | ✅ 無需授權 | ❌ 需要 OAuth 授權 | ❌ 首次需要授權 |
| 使用者體驗 | ✅ 開啟即用 | ❌ 首次使用需多次點擊 | ⚠️ 首次需授權 + 多帳號易錯亂 |
| 操作者識別 | ❌ 無法取得(回傳空字串) | ✅ 可取得 Session.getActiveUser() | ✅ 透過 Sheet 版本歷史追蹤 |
| 權限細分 | ❌ 所有人用開發者權限 | ✅ 每人用自己的權限 | ⚠️ 視 Trigger 建立者權限 |
| 操作記錄 | ❌ 需額外欄位手動記錄 | ✅ 自動記錄操作者 Email | ✅ Sheet 版本歷史自動記錄 |
| 執行身分 | 開發者 | 存取者 | Trigger 建立者 |
| 修改記錄 | ⚠️ 修改記錄都是開發者 | ✅ 記錄實際操作者 | ✅ Sheet 版本歷史記錄實際操作者 |
| 設定複雜度 | 簡單(部署時選擇) | 簡單(部署時選擇) | 複雜(需手動建立 Trigger) |
OAuth 應用程式驗證機制與實務困境
除了權限管理之外,還有一個容易被忽略的重點:OAuth 應用程式驗證審核機制。
關鍵差異:
- WebApp 模式一(Execute as me):✅ 繞過 OAuth 驗證審核,因為所有操作都算是開發者自己執行的,不需要 Google 審核
- WebApp 模式二(Execute as user):❌ 可能顯示「未經驗證的應用程式」警告,使用者首次授權時會看到 Google 的安全警告畫面
為什麼模式二理論上最理想,但實務上難以採用?
✅ 模式二的優勢(看起來很完美):
- 可自動記錄操作者,無需手動記錄
- 權限控制精確,每人用自己的權限
- 符合稽核需求,適合正式系統
❌ 但實務上的致命問題:
- 多帳號登入陷阱:使用者登入多個 Google 帳號時,Google 常常抓錯權限(例如用個人帳號授權公司 Sheet)
- 授權畫面嚇人:使用者會看到「這個應用程式尚未經過 Google 驗證」的紅色警告,需要點擊「進階」→「前往 XXX(不安全)」
- 授權流程複雜:非技術人員容易在授權過程中卡關
- 難以解釋:很難跟使用者解釋「為什麼我要授權一個不安全的應用程式?」
實務痛點即使 WebApp 模式二在技術上是最完美的解決方案,但因為:
- 使用者看到「未經驗證的應用程式」會害怕
- 多帳號登入時 Google 抓錯權限的問題無法避免
- 授權流程對非技術人員來說太複雜
因此實務上很難採用,必須另闢折衷方案。
折衷方案的必要性:
這就是為什麼我們需要:
- 方案一(WebApp 模式一 + 手動記錄):犧牲自動追蹤,換取使用體驗
- 方案三(Sheet 側邊欄 + 版本歷史):適合熟悉 Sheet 的使用者,授權後可自動追蹤
OAuth 驗證 vs 權限管理
- 權限管理:決定誰的身分執行程式碼(開發者 vs 使用者)
- OAuth 驗證:Google 審核機制,確保應用程式安全性
- Execute as me:繞過驗證審核,因為都算開發者操作
- Execute as user:需要驗證審核,但實務上使用者體驗差
實務建議:
- 內部工具:優先使用「Execute as me」+ 手動記錄,避免使用者困擾
- 公開應用程式:若願意投入時間通過 OAuth 驗證審核,可考慮「Execute as user」
實務解決方案
在實務上,需要根據需求選擇合適的權限模式:
方案一:WebApp 模式二(Execute as user)- 適合需要操作記錄的場景
// 部署設定:Execute as "User accessing the web app"function saveToSheet(data) { const user = Session.getActiveUser().getEmail(); // ✅ 可取得使用者身分
// 檢查權限 if (!canCreateProduct(user)) { return { success: false, error: '您沒有新增商品的權限' }; }
// 記錄操作者 sheet.appendRow([...data, new Date(), user, '新增']);}缺點:使用者首次使用需經過 OAuth 授權流程(多次點擊)
方案二:WebApp 模式一(Execute as me)+ 額外欄位記錄當前登入 Email
⚠️ 關鍵問題:使用「Execute as me」時,修改記錄都會是開發者,因此需要另外開欄位手動記錄當前登入的 email。
// 部署設定:Execute as "Me"function saveToSheet(data, userEmail) { // ❌ Session.getActiveUser() 回傳空字串 // ✅ 需從前端傳入當前登入的 email
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// 需要額外欄位記錄實際操作者 sheet.appendRow([ data.name, data.description, data.imageUrl, data.price, new Date(), // 更新時間 userEmail // ✅ 手動記錄當前登入的使用者 ]);
return { success: true };}前端傳入登入 email:
// WebApp 前端const currentUser = await getCurrentUser(); // 從登入系統取得
await google.script.run .withSuccessHandler(onSuccess) .saveToSheet(formData, currentUser.email); // 傳入當前登入的 email優點:使用者無需授權,開啟即用 缺點:需要額外實作登入機制與手動記錄
方案三:Sheet 側邊欄 + 依賴 Google Sheets 版本歷史
✅ 不需要手動記錄操作者,因為 Sheet 側邊欄基於 Apps Script,使用者必須授權,所以 Google Sheets 的版本歷史會自動記錄誰修改了資料。
// Sheet 側邊欄:直接修改,依賴 Sheet 版本歷史function updateSheetRow(rowIndex, data) { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.getRange(rowIndex, 1, 1, 4).setValues([[ data.name, data.description, data.imageUrl, data.price ]]);
// 💡 不需要手動記錄操作者 // 透過「檔案 → 版本記錄 → 查看版本記錄」即可看到誰修改了}建議選擇策略
根據不同情境選擇合適的方案:
| 使用情境 | 建議方案 | 理由 |
|---|---|---|
| 重視操作記錄(如財務、審核系統) | WebApp 模式二(Execute as user) | 可自動記錄操作者,適合稽核 |
| 追求使用者體驗(如內部協作工具) | WebApp 模式一 + 額外欄位記錄 | 無需授權流程,但需手動記錄登入 email |
| 快速編輯資料(熟悉 Sheet 的使用者) | Sheet 側邊欄 + 版本歷史 | 授權後即可追蹤,依賴 Sheet 內建功能 |
| 混合需求(部分需記錄、部分不需要) | 雙介面並存 | WebApp 用於新增(需手動記錄)、側邊欄用於編輯(自動追蹤) |
權限管理的最佳實踐
- WebApp 模式二:可自動記錄操作者,但使用者需授權(適合正式系統)
- WebApp 模式一:修改記錄都是開發者,需額外欄位手動記錄當前登入 email
- Sheet 側邊欄:基於 Apps Script 必須授權,Sheet 版本歷史會自動記錄操作者
- 關鍵操作:若需完整稽核紀錄,務必使用 WebApp 模式二
這個權限管理的兩難,正是 Google Apps Script 開發中最容易踩坑的部分。理解不同執行模式的差異,才能選擇最適合專案需求的方案。
五、本地開發環境設定
在開發較大型的 Google Apps Script 專案時,使用瀏覽器編輯器有諸多限制:無法使用熟悉的編輯器、缺乏 Git 版本控制、難以進行程式碼審查等。幸好 Google 提供了 clasp 工具,讓我們能在本地開發環境中撰寫程式碼。
5.1 為什麼需要本地開發?
| 需求 | 瀏覽器編輯器 | 本地開發 (clasp) |
|---|---|---|
| 程式碼補全 | ❌ 基本補全 | ✅ 完整 IntelliSense |
| Git 版本控制 | ❌ 不支援 | ✅ 完整支援 |
| 多檔案管理 | ❌ 較不便 | ✅ 資料夾結構清晰 |
| 團隊協作 | ❌ 難以 Code Review | ✅ Pull Request 流程 |
| TypeScript | ❌ 不支援 | ✅ 完整支援 |
| 測試整合 | ❌ 不支援 | ✅ 可整合測試框架 |
5.2 clasp 環境設定
步驟 1:安裝 Node.js
確保你的系統已安裝 Node.js(建議 v18 以上):
node --version # 應顯示 v18.0.0 或更高步驟 2:全域安裝 clasp
npm install -g @google/clasp步驟 3:啟用 Google Apps Script API
- 前往 Google Apps Script 設定頁面
- 開啟「Google Apps Script API」選項
步驟 4:登入 Google 帳號
clasp login這會開啟瀏覽器視窗,完成 Google 帳號授權。
步驟 5:安裝型別定義(可選,但強烈建議)
為了獲得完整的程式碼補全,安裝 Google Apps Script 的型別定義:
npm install --save-dev @types/google-apps-script5.3 專案初始化與結構
情境一:從現有專案開始
如果你已經在瀏覽器中建立了專案,可以將它複製到本地:
# 1. 取得 Script ID(從 Apps Script 編輯器的 URL 中)# URL 格式:https://script.google.com/home/projects/{SCRIPT_ID}/edit
# 2. 複製專案到本地clasp clone <SCRIPT_ID>情境二:建立新專案
如果要從頭開始,可以建立新專案並綁定到特定文件:
# 建立綁定到 Google Sheets 的專案clasp create --title "商品管理系統" --type sheets --parentId <SPREADSHEET_ID>
# 或建立獨立專案(用於 Web App)clasp create --title "商品管理系統" --type standalone專案結構範例:
gas-webapp-smart-form/├── .clasp.json # clasp 設定檔├── .claspignore # 忽略清單├── .gitignore # Git 忽略清單├── appsscript.json # Apps Script 設定檔├── package.json # Node.js 依賴├── README.md # 專案說明├── src/│ ├── Code.js # 主要後端邏輯│ ├── WebApp.html # WebApp 主頁面│ ├── UpdateModal.html # 側邊欄編輯介面│ ├── Stylesheet.html # 共用樣式│ ├── JavaScript.html # 共用 JS│ └── Validation.html # 驗證函數庫└── tests/ # 測試檔案(可選)5.4 開發工作流程
本地開發 → 推送 → 測試
# 1. 編輯程式碼(使用 VS Code 或其他編輯器)
# 2. 推送到 Google Apps Scriptclasp push
# 3. 在瀏覽器中開啟專案測試clasp open
# 4. 部署 WebApp(如有需要)clasp deploy --description "v1.0.0"監聽模式(自動推送):
# 監聽檔案變更,自動推送clasp push --watch重要提示:
推送行為執行
clasp push時,會將本地的所有檔案推送到 Google 伺服器,並覆蓋伺服器上的現有檔案。因此:
- ❌ 不要在瀏覽器和本地同時編輯(會導致版本衝突)
- ✅ 建議使用 Git 作為唯一的版本控制來源
- ✅ 建議在推送前先
git commit
5.5 Git 版本控制
建立 .gitignore 檔案:
node_modules/package-lock.json
# clasp.clasp.json # 包含敏感的 scriptId,不應提交
# 系統檔案.DS_Store建立 .claspignore 檔案(排除不需要推送的檔案):
**/**!src/**!appsscript.json.git/**node_modules/**Git 工作流程:
# 初始化 Gitgit init
# 建立 .gitignoreecho "node_modules/\n.clasp.json\n.DS_Store" > .gitignore
# 首次提交git add .git commit -m "feat: 初始化專案結構"
# 開發時git add src/Code.jsgit commit -m "feat: 新增 URL 自動解析功能"
# 推送到 Google Apps Scriptclasp push
# 推送到 GitHub(可選)git remote add origin <your-github-repo>git push -u origin main5.6 TypeScript 支援(進階)
clasp 支援 TypeScript,提供更好的型別檢查:
1. 初始化 TypeScript 專案:
# 安裝 TypeScriptnpm install --save-dev typescript @types/google-apps-script
# 建立 tsconfig.jsonnpx tsc --init2. 設定 tsconfig.json:
{ "compilerOptions": { "target": "ES2019", "module": "None", "lib": ["ES2019"], "strict": true, "esModuleInterop": true }, "include": ["src/**/*"]}3. 撰寫 TypeScript 程式碼:
interface ProductData { name: string; description: string; imageUrl: string; price: number;}
function saveToSheet(data: ProductData): { success: boolean } { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.appendRow([ data.name, data.description, data.imageUrl, data.price, new Date() ]);
return { success: true };}4. 推送時自動編譯:
# clasp 會自動將 .ts 編譯為 .js 再推送clasp pushTypeScript 限制使用 TypeScript 後,你只能在本地編輯
.ts檔案,無法在 Google Apps Script 瀏覽器編輯器中修改。因此更加強調 Git 版本控制的重要性。
5.7 本地開發的最佳實踐
✅ 建議的開發流程
本地編輯 (VS Code) ↓Git commit ↓clasp push ↓瀏覽器測試 ↓部署上線✅ 團隊協作流程
Developer A: 本地開發 → Git push ↓GitHub Pull Request ↓Code Review ↓Merge to main ↓Developer B: Git pull → clasp push → 部署常用命令速查表:
| 命令 | 說明 |
|---|---|
clasp create | 建立新專案 |
clasp clone <scriptId> | 複製現有專案 |
clasp push | 推送程式碼到 Google |
clasp push --watch | 監聽模式(自動推送) |
clasp pull | 從 Google 拉取程式碼 |
clasp open | 在瀏覽器開啟專案 |
clasp deploy | 部署新版本 |
clasp deployments | 查看所有部署 |
clasp logs | 查看執行日誌 |
透過 clasp 和 Git 的結合,我們能建立更專業的開發流程,讓 Google Apps Script 專案也能享有現代化的開發體驗。
六、開發心得與總結
經過這個專案的實作,我對 AI 輔助開發有了更深的體會。
AI 輔助開發的實際價值
快速生成 UI 組件(節省約 60% 開發時間)
過去我需要:
- 查找 Vue 3 Composition API 文件
- 學習 Tailwind CSS 類別名稱
- 手動撰寫每個表單欄位和驗證邏輯
現在有了 AI:
- 用自然語言描述需求
- AI 生成完整的組件程式碼
- 我只需微調業務邏輯
提供架構建議
AI 幫助我:
- 選擇合適的設計模式(Vue Composition API)
- 建議錯誤處理機制(try-catch + 使用者友善的訊息)
- 提醒安全性考量(URL 驗證、XSS 防護)
加速而非取代
重要的是,AI 並沒有取代我的工作,而是讓我能專注於:
- 理解使用者需求
- 設計系統架構
- 最佳化使用者體驗
- 做出技術決策
程式碼的語法和結構由 AI 處理,我則負責「為什麼這樣做」和「如何做得更好」。
關鍵收穫
✅ WebApp 適合非技術使用者 透過簡化介面和防呆設計,讓不熟悉試算表的團隊成員也能輕鬆管理資料。
✅ URL 自動解析大幅提升效率 使用者回饋最好的功能,從 2-3 分鐘的手動輸入縮短到 5-10 秒。
✅ 雙介面設計兼顧不同需求 WebApp 提供友善體驗,Sheet 側邊欄保留進階功能,滿足不同使用者偏好。
✅ AI 協助讓開發更聚焦於邏輯而非語法 減少查文件和試錯的時間,更多時間思考使用者需求和系統設計。
下一步建議
如果你想進一步優化這套系統,可以考慮:
增加更多智能功能
- 圖片自動壓縮(減少載入時間)
- 重複商品檢測(避免重複建立)
- AI 自動生成商品描述(基於商品名稱)
效能優化
- 資料快取機制(減少 Sheet 讀取次數)
- 延遲載入(大量資料時分頁載入)
- 圖片 CDN 整合(加速圖片載入)
使用者體驗提升
- 批次匯入功能(CSV/Excel 匯入)
- 資料篩選與搜尋
- 歷史記錄與復原功能
進階功能
- 權限管理(不同角色有不同權限)
- 審核流程(新增資料需主管審核)
- 資料分析儀表板(統計圖表、趨勢分析)
參考資料
官方文件:
開發工具:
clasp - Google Apps Script CLI
How to Develop Google Apps Script Code in Your Local Code Editor
相關文章:
回報錯字、失效連結,或告訴我你想看的延伸主題。