鐵人賽已經倒數兩天了!最後想教教大家一個有趣且進階的應用,就是部署靜態網頁!
大家相信下面這張網頁圖是用 GAS + google sheet 架出來的嗎~~

讓我們一步一步拆解 GAS 部署網頁的過程吧!
讓我們先用一個最簡單的 hello world 按鈕作為範例~
GAS 架網頁只需要兩個元素:
HTML 範例:
按左上檔案的加號,建立一個名為 index 的 html 檔案
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>用 Google Apps Script 部署網頁好簡單~</p>
  </body>
</html>
Apps Script 後端程式碼:
建立一個 gs 檔案,輸入以下程式碼:
function doGet() {
  // 載入並返回 HTML 頁面
  return HtmlService.createHtmlOutputFromFile('index'); // index 為網頁 html 名稱
}
Note:GAS 網頁部署跟 github 有點類似,並不會每分每秒立馬同步程式碼
如果有修改程式碼,需要即時看其所造成的網頁效果,可點擊「測試部署作業」> 複製「網頁應用程式」的網址連結至網址列,重新整理此連結便可瀏覽最新修改情形!
沒錯,這樣你就能獲得一個超簡樸的 Hello World 網頁~
但這樣太無聊~竟然都連結 Google Sheet 了
我們就來用 Google sheet 儲存資料,利用 GAS 來做CRUD 並幫我們呈現成網頁吧!
Task (任務)Due Date(截止日期)Creation Date(新增日期)你的 Google Sheets 應該如下:
| Task | Due Date | Creation Date | 
|---|---|---|
| Task 1 | 2024-10-10 | 2024-10-03 | 
按左上角檔案的「+」號,新增一個 HTML 檔案,命名為 index,並輸入以下程式碼:
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>To-Do List</h1>
    <input type="text" id="taskInput" placeholder="輸入任務">
    <input type="date" id="dueDateInput" placeholder="到期日">
    <button onclick="addTask()">新增任務</button>
    <table id="taskTable">
        <thead>
            <tr>
                <th>任務</th>
                <th>截止日期</th>
                <th>新增日期</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody id="taskBody">
            <!-- 任務清單會被 JavaScript 動態新增到這裡 -->
        </tbody>
    </table>
    <script>
      
      function loadTasks() {
          google.script.run.withSuccessHandler(function(tasks) {
              var taskTable = document.getElementById('taskTable');
              var taskBody = document.getElementById('taskBody');
              taskBody.innerHTML = '';  // 清空表格內容
              
              if (tasks.length > 0) {
                  tasks.forEach(function(task) {
                      var row = document.createElement('tr'); 
                      var taskCell = document.createElement('td'); // 任務單元格
                      var dueDateCell = document.createElement('td'); // 截止日期單元格
                      var creationDateCell = document.createElement('td'); // 新增日期單元格
                      var deleteCell = document.createElement('td'); // 刪除按鈕單元格
                      
                      taskCell.textContent = task.task; // 設定任務內容
                      dueDateCell.textContent = task.dueDate; // 設定截止日期
                      creationDateCell.textContent = task.creationDate; // 設定新增日期
                      
                      // 創建刪除按鈕
                      var deleteBtn = document.createElement('button');
                      deleteBtn.textContent = '刪除';
                      deleteBtn.className = 'delete-btn';
                      deleteBtn.onclick = function() {
                          deleteTask(task.task);
                      };
                      
                      deleteCell.appendChild(deleteBtn); // 將刪除按鈕新增到單元格
                      row.appendChild(taskCell); // 將任務單元格新增到行
                      row.appendChild(dueDateCell); // 將截止日期單元格新增到行
                      row.appendChild(creationDateCell); // 將新增日期單元格新增到行
                      row.appendChild(deleteCell); // 將刪除按鈕單元格新增到行
                      
                      taskBody.appendChild(row); // 將整行新增到表格主體中
                  });
              } else {
                  console.log('沒有找到任務');
              }
          }).getTasks();  // 呼叫 GAS 後端函數取得任務
      }
      // 新增任務
      function addTask() {
        var task = document.getElementById('taskInput').value;
        var dueDate = document.getElementById('dueDateInput').value;
        if (task && dueDate) {
          google.script.run.withSuccessHandler(loadTasks).addTask(task, dueDate);  // 新增後重新載入任務列表
          document.getElementById('taskInput').value = '';  // 清空輸入框
          document.getElementById('dueDateInput').value = '';  // 清空日期框
        } else {
          alert('請輸入任務和截止日期');
        }
      }
      // 刪除任務
      function deleteTask(task) {
        google.script.run.withSuccessHandler(loadTasks).deleteTask(task);  // 刪除後重新載入任務列表
      }
      // 當頁面載入完成後,自動載入任務
      window.onload = loadTasks;
    </script>
  </body>
</html>
google.script.run.withSuccessHandler(loadTasks).addTask(task, dueDate); 意思是會等後端 GAS addTask() 成功後,再執行 js loadTask 的 function.// 取得 Google 試算表
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('{你的工作表名稱}');;
// 返回 HTML 頁面
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}
// 從 Google Sheets 中取得所有任務
function getTasks() {
  var data = sheet.getRange(2, 1, sheet.getLastRow() - 1, 3).getValues();  // 讀取所有行的三列數據
  var tasks = [];
  for (var i = 0; i < data.length; i++) {
    var row = data[i];
    tasks.push({
      task: row[0],
      dueDate: row[1].toISOString().split('T')[0],
      creationDate: row[2].toISOString().split('T')[0]
    });
  }
  return tasks;
}
// 新增任務,並包含到期日與創建日
function addTask(task, dueDate) {
  var creationDate = new Date();  // 自動生成新增日期
  sheet.appendRow([task, dueDate, creationDate]);  // 將新任務、截止日期和新增日期寫入試算表
}
// 刪除任務
function deleteTask(task) {
  var range = sheet.getDataRange();  // 取得所有資料範圍
  var values = range.getValues();
  for (var i = 1; i < values.length; i++) {
    if (values[i][0] == task) {
      sheet.deleteRow(i + 1);  // 刪除對應列
      break;
    }
  }
}
getTasks 取得所有任務資料,注意!從 google sheet 讀取回來的資料格式為 Date Obj, 需把它轉型為 string 再傳回前端顯示!addTask() 接收來自前端資料,並將其儲存到 Google 試算表中。deleteTask() 接收來自前端的命令,並這列資料從 Google 試算表中刪除。查看網頁就會得到像這樣的畫面:
建立幾個簡單的任務清單後的畫面:
目前只有單純的 html,顯示起來相對單調,讓我們加入一些 css 語法讓他變得好看吧!
更新 html 檔:
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            margin: 20px;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 20px 0;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
        th, td {
            padding: 12px;
            text-align: left;
            border: 1px solid #ddd;
        }
        th {
            background-color: #4CAF50; /* 標題行顏色 */
            color: white;
        }
        tr:nth-child(even) {
            background-color: #f2f2f2; /* 偶數行顏色 */
        }
        tr:hover {
            background-color: #ddd; /* 滑鼠懸停效果 */
        }
        caption {
            margin: 10px;
            font-size: 1.5em;
            font-weight: bold;
            color: #333;
        }
    </style>
  </head>
  <body>
    <h1>To-Do List</h1>
    <input type="text" id="taskInput" placeholder="輸入任務">
    <input type="date" id="dueDateInput" placeholder="到期日">
    <button onclick="addTask()">新增任務</button>
    <table id="taskTable">
        <thead>
            <tr>
                <th>任務</th>
                <th>截止日期</th>
                <th>新增日期</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody id="taskBody">
            <!-- 任務清單會被 JavaScript 動態新增到這裡 -->
        </tbody>
    </table>
    <script>
      
      function loadTasks() {
          google.script.run.withSuccessHandler(function(tasks) {
              var taskTable = document.getElementById('taskTable');
              var taskBody = document.getElementById('taskBody');
              taskBody.innerHTML = '';  // 清空表格內容
              
              if (tasks.length > 0) {
                  tasks.forEach(function(task) {
                      var row = document.createElement('tr'); 
                      var taskCell = document.createElement('td'); // 任務單元格
                      var dueDateCell = document.createElement('td'); // 截止日期單元格
                      var creationDateCell = document.createElement('td'); // 新增日期單元格
                      var deleteCell = document.createElement('td'); // 刪除按鈕單元格
                      
                      taskCell.textContent = task.task; // 設定任務內容
                      dueDateCell.textContent = task.dueDate; // 設定截止日期
                      creationDateCell.textContent = task.creationDate; // 設定新增日期
                      
                      // 創建刪除按鈕
                      var deleteBtn = document.createElement('button');
                      deleteBtn.textContent = '刪除';
                      deleteBtn.className = 'delete-btn';
                      deleteBtn.onclick = function() {
                          deleteTask(task.task);
                      };
                      
                      deleteCell.appendChild(deleteBtn); // 將刪除按鈕新增到單元格
                      row.appendChild(taskCell); // 將任務單元格新增到行
                      row.appendChild(dueDateCell); // 將截止日期單元格新增到行
                      row.appendChild(creationDateCell); // 將新增日期單元格新增到行
                      row.appendChild(deleteCell); // 將刪除按鈕單元格新增到行
                      
                      taskBody.appendChild(row); // 將整行新增到表格主體中
                  });
              } else {
                  console.log('沒有找到任務');
              }
          }).getTasks();  // 呼叫 GAS 後端函數取得任務
      }
      // 新增任務
      function addTask() {
        var task = document.getElementById('taskInput').value;
        var dueDate = document.getElementById('dueDateInput').value;
        if (task && dueDate) {
          google.script.run.withSuccessHandler(loadTasks).addTask(task, dueDate);  // 新增後重新載入任務列表
          document.getElementById('taskInput').value = '';  // 清空輸入框
          document.getElementById('dueDateInput').value = '';  // 清空日期框
        } else {
          alert('請輸入任務和截止日期');
        }
      }
      // 刪除任務
      function deleteTask(task) {
        google.script.run.withSuccessHandler(loadTasks).deleteTask(task);  // 刪除後重新載入任務列表
      }
      // 當頁面載入完成後,自動載入任務
      window.onload = loadTasks;
    </script>
  </body>
</html>
Note: GAS 並不是專門為了網頁所設計,所以只能支援最單純的 css 語法,且只能加入至 html 檔案當中,沒辦法另外建立 css file 唷~
網頁呈現:
而背後 Google Sheet 的資料對應如下:

這樣就建立好一個 To-Do List 的網頁啦 🚀🚀🚀
你可以透過網頁做增刪改減,也可以透過 google sheet 管理資料!
如此你就學會如何用 GAS 部署網站啦~
快依照這個範例做出類似的應用,漂亮的顯示你想呈現的資料吧!
👋Hi,單純打個招呼
剛剛在今年的鐵人賽引用了妳的文章:
https://ithelp.ithome.com.tw/articles/10382513
感謝 分かるカモ さん 的引用 ~
你的文筆比我好太多了! 而且還深入探究到 GAS 背後的執行環境等等
我會持續追蹤的! 祝你成功完賽唷~!