🎯 系列目標:用 30 天時間,從零開始打造一個專屬輔大學生的課表生成 Chrome 擴充功能
👨💻 作者:輔大智慧資安 412580084
📅 Day 18:Background Script 分頁控制機制
昨天我們學會了 Chrome Extension 各組件間的基本協作,今天我們要專注實作 Background Script 的分頁控制機制,這是課表爬取的核心基礎!
今天我們要完成:
在開始實作分頁控制之前,我們需要先理解完整的課表生成流程。這個流程涉及多個步驟的協調工作,每個步驟都需要精確的分頁管理來確保系統穩定運行。
在完整的課表生成流程中,我們需要:
分頁控制是 Chrome Extension 中的核心技術,它讓我們能夠以程式化方式管理分頁。它讓我們可以在背景中執行複雜的操作,而不會干擾用戶的正常使用。
graph TD
A[用戶點擊按鈕] --> B[Background 接收消息]
B --> C[開啟課表頁面]
C --> D[監聽頁面載入]
D --> E[執行資料爬取]
E --> F[關閉工作分頁]
F --> G[開啟結果頁面]
在開始實作分頁控制之前,我們需要先更新 Background Script 的基礎架構。這個更新將把原本簡單的模擬處理升級為完整的分頁控制流程。我們會在 handleBasicScheduleGeneration
函數中加入分頁開啟、狀態監聽和資源清理等功能。
首先,我們來更新 handleBasicScheduleGeneration
函數,加入分頁控制邏輯:
// 基本的課表生成處理函數(新增分頁控制)
async function handleBasicScheduleGeneration(request, sendResponse) {
try {
console.log('🗺️ 開始課表生成流程 - 分頁控制版本');
// 步驟 1:記錄請求資訊
console.log('📝 步驟 1: 記錄用戶請求');
console.log('來源頁面:', request.data.url);
console.log('請求時間:', request.data.timestamp);
// 步驟 2:開啟課表頁面
console.log('🌐 步驟 2: 開啟課表頁面');
const tabResult = await openSchedulePage();
if (!tabResult.success) {
throw new Error('無法開啟課表頁面: ' + tabResult.error);
}
console.log('✅ 課表頁面已開啟,分頁 ID:', tabResult.tabId);
// 步驟 3:等待頁面載入(模擬)
console.log('⏳ 步驟 3: 等待頁面載入');
await simulatePageProcessing(tabResult.tabId);
// 步驟 4:關閉工作分頁
console.log('🧹 步驟 4: 清理工作分頁');
await closeWorkingTab(tabResult.tabId);
// 步驟 5:開啟結果頁面
console.log('🎉 步驟 5: 開啟課表結果頁面');
chrome.tabs.create({ url: 'schedule.html' });
// 成功回應
sendResponse({
success: true,
message: '分頁控制流程完成',
data: {
processedAt: new Date().toISOString(),
fromUrl: request.data.url,
tabId: tabResult.tabId
}
});
} catch (error) {
console.error('❌ 分頁控制流程失敗:', error);
sendResponse({
success: false,
error: error.message,
timestamp: new Date().toISOString()
});
}
}
分頁開啟是整個流程的第一步,我們需要在背景中開啟輔大的選課清單系統。這個函數的設計重點在於錯誤處理和狀態回傳,確保在各種情況下都能給出正確的回應。chrome.tabs.create()
API 的使用需要特別注意 active: false
參數,這樣可以確保新分頁在背景開啟(不會突然切換到新分頁)。
// 開啟課表頁面函數
function openSchedulePage() {
return new Promise((resolve) => {
console.log('🌐 正在開啟輔大課表頁面...');
// 輔大課表系統網址
const scheduleUrl = 'http://estu.fju.edu.tw/CheckSelList/HisListNew.aspx';
chrome.tabs.create({
url: scheduleUrl,
active: false, // 在背景開啟,不打擾用戶
pinned: false // 不固定分頁
}, (tab) => {
// 檢查是否有錯誤
if (chrome.runtime.lastError) {
console.error('❌ 開啟分頁失敗:', chrome.runtime.lastError.message);
resolve({
success: false,
error: chrome.runtime.lastError.message
});
return;
}
// 檢查分頁是否成功建立
if (!tab || !tab.id) {
console.error('❌ 分頁建立失敗:無法獲取分頁資訊');
resolve({
success: false,
error: '分頁建立失敗'
});
return;
}
console.log('✅ 課表頁面開啟成功');
console.log('📊 分頁資訊:', {
id: tab.id,
url: tab.url,
status: tab.status
});
resolve({
success: true,
tabId: tab.id,
tabInfo: {
url: tab.url,
status: tab.status
}
});
});
});
}
分頁關閉功能在資源管理中很重要,讓我們不會留下不必要的工作分頁。這邊添加異常情況排除,讓分頁已經被用戶手動關閉或出現其他異常情況也不影響整個程式執行
// 關閉工作分頁函數
function closeWorkingTab(tabId) {
return new Promise((resolve) => {
console.log('🧹 正在關閉工作分頁,ID:', tabId);
// 檢查分頁 ID 是否有效
if (!tabId || typeof tabId !== 'number') {
console.warn('⚠️ 無效的分頁 ID,跳過關閉操作');
resolve();
return;
}
chrome.tabs.remove(tabId, () => {
if (chrome.runtime.lastError) {
// 分頁可能已經被用戶關閉,這是正常情況
console.warn('⚠️ 關閉分頁時出現提示:', chrome.runtime.lastError.message);
} else {
console.log('✅ 工作分頁已成功關閉');
}
// 無論成功或失敗都繼續流程
resolve();
});
});
}
頁面載入狀態監聽是分頁控制中最難又最重要的地方,我們需要等待遠端網頁完全載入完成。這個函數使用 chrome.tabs.onUpdated
事件監聽器來追蹤分頁的載入狀態。關鍵在於在正確的時機清理監聽器和超時設定,以防止內存洩漏和無種等待。
// 等待分頁載入完成
function waitForTabLoad(tabId, timeout = 15000) {
return new Promise((resolve, reject) => {
console.log('⏳ 開始監聽分頁載入狀態,ID:', tabId);
// 設定超時處理
const timeoutId = setTimeout(() => {
console.error('❌ 等待分頁載入超時');
reject(new Error('分頁載入超時'));
}, timeout);
// 監聽分頁更新事件
const listener = (updatedTabId, changeInfo, tab) => {
// 只處理目標分頁的更新
if (updatedTabId !== tabId) {
return;
}
console.log('📊 分頁狀態更新:', {
tabId: updatedTabId,
status: changeInfo.status,
url: changeInfo.url
});
// 當分頁載入完成時
if (changeInfo.status === 'complete') {
console.log('✅ 分頁載入完成');
// 清理資源
clearTimeout(timeoutId);
chrome.tabs.onUpdated.removeListener(listener);
resolve(tab);
}
};
// 註冊監聽器
chrome.tabs.onUpdated.addListener(listener);
// 檢查分頁是否已經載入完成
chrome.tabs.get(tabId, (tab) => {
if (chrome.runtime.lastError) {
clearTimeout(timeoutId);
chrome.tabs.onUpdated.removeListener(listener);
reject(new Error('無法獲取分頁資訊: ' + chrome.runtime.lastError.message));
return;
}
if (tab.status === 'complete') {
console.log('✅ 分頁已經載入完成');
clearTimeout(timeoutId);
chrome.tabs.onUpdated.removeListener(listener);
resolve(tab);
}
});
});
}
現階段我們先用模擬處理來取代真實的資料爬取。複雜的資料解析邏輯之後會再添加。
// 模擬頁面處理流程
async function simulatePageProcessing(tabId) {
try {
console.log('🔄 開始模擬頁面處理流程');
// 等待頁面載入
console.log('⏳ 等待頁面載入完成...');
await waitForTabLoad(tabId);
// 額外等待確保頁面完全渲染
console.log('🎨 等待頁面渲染完成...');
await new Promise(resolve => setTimeout(resolve, 3000));
// 模擬資料處理
console.log('📊 模擬資料處理中...');
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('✅ 頁面處理流程完成');
return {
success: true,
message: '模擬處理完成',
processedAt: new Date().toISOString()
};
} catch (error) {
console.error('❌ 頁面處理流程失敗:', error);
throw error;
}
}
在了解前面分頁控制機制後,我們需要將這些新功能整合到現有的 Background Script 中。這個整合過程需要特別注意函數的順序和相互依賴關係,確保並驗證所有函數都能正確載入和執行。
完整的更新後程式碼結構:
// background.js - Day 18 分頁控制版本
console.log('🚀 輔大課表生成器背景腳本已載入 - Day 18');
// 擴充功能安裝事件
chrome.runtime.onInstalled.addListener((details) => {
console.log('📦 擴充功能安裝事件:', details.reason);
if (details.reason === 'install') {
console.log('✨ 歡迎使用輔大課表生成器!');
chrome.storage.local.set({
'settings': {
'autoSave': true,
'theme': 'default'
}
});
}
});
// 主要消息監聽器
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('📨 Background 收到消息:', request);
if (request.action === 'generateSchedule') {
console.log('🚀 開始課表生成流程');
handleBasicScheduleGeneration(request, sendResponse);
return true;
}
if (request.action === 'getSettings') {
handleGetSettings(sendResponse);
return true;
}
console.log('❓ 未知的消息類型:', request.action);
sendResponse({ success: false, error: '未知的操作類型' });
});
// 在這裡添加上面實作的所有函數:
// - handleBasicScheduleGeneration
// - openSchedulePage
// - closeWorkingTab
// - waitForTabLoad
// - simulatePageProcessing
// - handleGetSettings
將今天學習的分頁控制函數添加到 fju-schedule-extension/background.js
在 Chrome 擴充功能管理頁面重新載入
https://portal.fju.edu.tw/student/
schedule.html
(會顯示錯誤,這是正常的)在 schedule.html
頁面按 F12
,查看 Console 中的詳細日誌,應該看到完整的分頁控制流程。
明天我們將學習精確分頁狀態監聽機制