7245 字
36 分鐘
Google Apps Script WebApp 開發實戰:打造防呆輸入的商品管理系統

前言#

當你需要讓非技術人員也能輕鬆管理資料時,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 分離:

WebApp.html
<!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 結構如下:

JavaScript.html
<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);
});

後端實作

Code.gs
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 tag
function 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 步驟

  1. 將函數重新命名(例如:onEditonEditInstallable
  2. 在 Apps Script 編輯器中:「觸發條件」→「新增觸發條件」
  3. 選擇:
    • 函數: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 accessing
function 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 建立者權限
操作記錄需額外欄位手動記錄✅ 自動記錄操作者 EmailSheet 版本歷史自動記錄
執行身分開發者存取者Trigger 建立者
修改記錄⚠️ 修改記錄都是開發者✅ 記錄實際操作者✅ Sheet 版本歷史記錄實際操作者
設定複雜度簡單(部署時選擇)簡單(部署時選擇)複雜(需手動建立 Trigger)

OAuth 應用程式驗證機制與實務困境#

除了權限管理之外,還有一個容易被忽略的重點:OAuth 應用程式驗證審核機制

關鍵差異

  • WebApp 模式一(Execute as me):✅ 繞過 OAuth 驗證審核,因為所有操作都算是開發者自己執行的,不需要 Google 審核
  • WebApp 模式二(Execute as user):❌ 可能顯示「未經驗證的應用程式」警告,使用者首次授權時會看到 Google 的安全警告畫面

為什麼模式二理論上最理想,但實務上難以採用?

✅ 模式二的優勢(看起來很完美):

  • 可自動記錄操作者,無需手動記錄
  • 權限控制精確,每人用自己的權限
  • 符合稽核需求,適合正式系統

❌ 但實務上的致命問題

  1. 多帳號登入陷阱:使用者登入多個 Google 帳號時,Google 常常抓錯權限(例如用個人帳號授權公司 Sheet)
  2. 授權畫面嚇人:使用者會看到「這個應用程式尚未經過 Google 驗證」的紅色警告,需要點擊「進階」→「前往 XXX(不安全)」
  3. 授權流程複雜:非技術人員容易在授權過程中卡關
  4. 難以解釋:很難跟使用者解釋「為什麼我要授權一個不安全的應用程式?」
實務痛點

即使 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 以上):

Terminal window
node --version # 應顯示 v18.0.0 或更高

步驟 2:全域安裝 clasp

Terminal window
npm install -g @google/clasp

步驟 3:啟用 Google Apps Script API

  1. 前往 Google Apps Script 設定頁面
  2. 開啟「Google Apps Script API」選項

步驟 4:登入 Google 帳號

Terminal window
clasp login

這會開啟瀏覽器視窗,完成 Google 帳號授權。

步驟 5:安裝型別定義(可選,但強烈建議)

為了獲得完整的程式碼補全,安裝 Google Apps Script 的型別定義:

Terminal window
npm install --save-dev @types/google-apps-script

5.3 專案初始化與結構#

情境一:從現有專案開始

如果你已經在瀏覽器中建立了專案,可以將它複製到本地:

Terminal window
# 1. 取得 Script ID(從 Apps Script 編輯器的 URL 中)
# URL 格式:https://script.google.com/home/projects/{SCRIPT_ID}/edit
# 2. 複製專案到本地
clasp clone <SCRIPT_ID>

情境二:建立新專案

如果要從頭開始,可以建立新專案並綁定到特定文件:

Terminal window
# 建立綁定到 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 開發工作流程#

本地開發 → 推送 → 測試

Terminal window
# 1. 編輯程式碼(使用 VS Code 或其他編輯器)
# 2. 推送到 Google Apps Script
clasp push
# 3. 在瀏覽器中開啟專案測試
clasp open
# 4. 部署 WebApp(如有需要)
clasp deploy --description "v1.0.0"

監聽模式(自動推送)

Terminal window
# 監聽檔案變更,自動推送
clasp push --watch

重要提示

推送行為

執行 clasp push 時,會將本地的所有檔案推送到 Google 伺服器,並覆蓋伺服器上的現有檔案。因此:

  • 不要在瀏覽器和本地同時編輯(會導致版本衝突)
  • 建議使用 Git 作為唯一的版本控制來源
  • 建議在推送前先 git commit

5.5 Git 版本控制#

建立 .gitignore 檔案:

Node.js
node_modules/
package-lock.json
# clasp
.clasp.json # 包含敏感的 scriptId,不應提交
# 系統檔案
.DS_Store

建立 .claspignore 檔案(排除不需要推送的檔案):

**/**
!src/**
!appsscript.json
.git/**
node_modules/**

Git 工作流程

Terminal window
# 初始化 Git
git init
# 建立 .gitignore
echo "node_modules/\n.clasp.json\n.DS_Store" > .gitignore
# 首次提交
git add .
git commit -m "feat: 初始化專案結構"
# 開發時
git add src/Code.js
git commit -m "feat: 新增 URL 自動解析功能"
# 推送到 Google Apps Script
clasp push
# 推送到 GitHub(可選)
git remote add origin <your-github-repo>
git push -u origin main

5.6 TypeScript 支援(進階)#

clasp 支援 TypeScript,提供更好的型別檢查:

1. 初始化 TypeScript 專案

Terminal window
# 安裝 TypeScript
npm install --save-dev typescript @types/google-apps-script
# 建立 tsconfig.json
npx tsc --init

2. 設定 tsconfig.json

{
"compilerOptions": {
"target": "ES2019",
"module": "None",
"lib": ["ES2019"],
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}

3. 撰寫 TypeScript 程式碼

src/Code.ts
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. 推送時自動編譯

Terminal window
# clasp 會自動將 .ts 編譯為 .js 再推送
clasp push
TypeScript 限制

使用 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% 開發時間)

過去我需要:

  1. 查找 Vue 3 Composition API 文件
  2. 學習 Tailwind CSS 類別名稱
  3. 手動撰寫每個表單欄位和驗證邏輯

現在有了 AI:

  1. 用自然語言描述需求
  2. AI 生成完整的組件程式碼
  3. 我只需微調業務邏輯

提供架構建議

AI 幫助我:

  • 選擇合適的設計模式(Vue Composition API)
  • 建議錯誤處理機制(try-catch + 使用者友善的訊息)
  • 提醒安全性考量(URL 驗證、XSS 防護)

加速而非取代

重要的是,AI 並沒有取代我的工作,而是讓我能專注於:

  • 理解使用者需求
  • 設計系統架構
  • 最佳化使用者體驗
  • 做出技術決策

程式碼的語法和結構由 AI 處理,我則負責「為什麼這樣做」和「如何做得更好」。

關鍵收穫#

WebApp 適合非技術使用者 透過簡化介面和防呆設計,讓不熟悉試算表的團隊成員也能輕鬆管理資料。

URL 自動解析大幅提升效率 使用者回饋最好的功能,從 2-3 分鐘的手動輸入縮短到 5-10 秒。

雙介面設計兼顧不同需求 WebApp 提供友善體驗,Sheet 側邊欄保留進階功能,滿足不同使用者偏好。

AI 協助讓開發更聚焦於邏輯而非語法 減少查文件和試錯的時間,更多時間思考使用者需求和系統設計。

下一步建議#

如果你想進一步優化這套系統,可以考慮:

增加更多智能功能

  • 圖片自動壓縮(減少載入時間)
  • 重複商品檢測(避免重複建立)
  • AI 自動生成商品描述(基於商品名稱)

效能優化

  • 資料快取機制(減少 Sheet 讀取次數)
  • 延遲載入(大量資料時分頁載入)
  • 圖片 CDN 整合(加速圖片載入)

使用者體驗提升

  • 批次匯入功能(CSV/Excel 匯入)
  • 資料篩選與搜尋
  • 歷史記錄與復原功能

進階功能

  • 權限管理(不同角色有不同權限)
  • 審核流程(新增資料需主管審核)
  • 資料分析儀表板(統計圖表、趨勢分析)

參考資料#

官方文件:

Google Apps Script 官方文件

開發工具:

clasp - Google Apps Script CLI

How to Develop Google Apps Script Code in Your Local Code Editor

相關文章:

Google Apps Script × Sheets 實戰指南:從側邊欄到 Web App 部署

Google Apps Script WebApp 開發實戰:打造防呆輸入的商品管理系統
https://laplusda.com/posts/gas-webapp-smart-form/
作者
Zero
發佈於
2025-12-30
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。