這個「城中校區 ↔ 校總區」各交通工具到站時間的專案,最早的構想始於 2022 年 9 月。
當時剛剛搬進男二舍、還沒修過網服、沒有任何專案架構的我,用硬幹的方式生出了第一版程式碼。有關這坨用 Vue 2 寫成的義大利麵,可以到 kinoras/m2transit-ninja 細細「品嚐」,這裡就不再贅述了。
縱使心中早已有重寫的念頭,但礙於各種瑣事和自身的惰性,一直沒有付諸行動。如今已是 2025 年 9 月,已經離開男二舍與台大的我明白——要是此刻仍不開始,恐怕就再也沒有機會了。
在開始重構之前,要先釐清這個程式的預期使用情境。整體功能不多,大致包含以下幾個步驟:
藉由以上功能定義,可以整理出各個資料類別和他們之間的關係圖:

| 類別 | 定義 | 例子 |
|---|---|---|
| Area | 校區 | 校總區 |
| Location | 站點 | 傅鐘 |
| Direction | 移動方向 | 傅鐘 → 基醫大樓 |
| Method | 運具 | 公車 |
| Route | 具體路線 | 208 公車 (公館站 → 開南中學) |
不過上圖仍有考慮不夠周全的地方,修正如下:
至此,靜態資料架構就大致完整了。
這邊主要用到的外部資料有兩類:
台大交通車採定時定點發車,雖然沒有提供路線與發車時間的 API(其實也合理),但時刻表還是有公布在總務處的網頁上。因此,可以直接從班表上取得路線資料,並依目前時間推算下一班車的抵達時間。
不過仍有兩點需要注意:
目前交通部的 TDX 平台上已有公車的路線、停靠站、旅程時間、站牌路線 ETA 等資訊可供使用。
至於點對點的交通路線選項,雖然可以採進階做法:先透過 Google Maps API 查詢最合適的路線,再介接 TDX 查詢路線參數。不過,考量總區和城中之間的交通方法相對匱乏單純,目前還是以手動方式來取得路線,並由 TDX 取得該路線的 ETA。
台北捷運的路線、旅程時間和時刻表同樣有在 TDX 上公開,因此可比照公車路線資料的方式進行整理。
不過在到站時間方面,台北捷運並沒有提供公開的 ETA API,而是必須先填妥使用企劃書向北捷申請,待審核通過後才能取得 API 存取權。
沒關係,山不轉路轉,路不轉人轉。最後我還是有想到一些誤差值相對較小的替代方案:
這些替代方案,頂多只能算是「推測」甚至「猜測」,長久來看還是得仰賴台北捷運公開 ETA 資料才行。另外,「時刻表解法」也會遇到與校車相同的「平/假日判斷」問題,這部分同樣有待解決。
目前所有路線資料皆為人工整理,因此先以 JSON 檔案儲存。
若之後改以自動爬取時刻表或整合 Google Maps 路線,考量到線變動相對頻繁,可以再改用資料庫。屆時也只要把資料輸出成現行的 JSON 格式,就能確保相容性。
雖然到站時間是動態的,但還是有暫時儲存的需要。主要是因為 TDX 對存取頻率有限制(依方案有所不同,最低付費區間是 5 次/秒)。若查詢太頻繁,除了有成本上的負擔,還可能被回傳 429 使服務中斷。
因此,按照各類運具的資料來源,可採用以下快取機制:
要實作預計的功能,會需要使用到以下兩類工具:
這裡我的挑選重點是「低成本」、「便利性」和「整合度」。
需要注意的是,這裡所說的「整合度」指的是與部署平台的契合度,而非與程式邏輯的耦合度。即使工具本身有提供方便的的介面,使用時還是需要在工具與業務邏輯之間實作另一層介面,以維持設計的模組化,也避免將來替換時牽一髮而動全身。
時隔數年再次打開過去的專案,愈發能感受到當時界面的混亂。主要問題如下:
舊版設計將所有運具資訊集中於同一頁面,每一列又被硬性劃分為左右兩區,各自包含主、副兩行文字。這樣的結構固然在實作上更方便,實際上卻犧牲了使用者體驗。為了在這種版面配置下維持視覺平衡,甚至連「今日未營運」這類狀態文字都要附上不必要的英文標示。
此外,資訊呈現位置也缺乏一致性,導致整體資訊層級混亂。例如:校車與捷運的搭乘地點名稱都置於每行左側,但公車的站名卻被移到區塊上方,只為了騰出空間放置路線名稱。至於捷運的路線名稱呢?因為實在找不到地方,只好塞進目的地車站的左邊。,
舊版為追求「視覺統一」,幾乎僅使用黑白色系,連捷運路線標誌都被改成灰階,嚴重影響了辨識度。
另外,在中文介面中,文字為了排版平衡往往略大於英數字,但在到站查詢系統中,「數字」才是使用者最關注的資訊,因此數字相關的字級也需要重新調整。

新版介面作出了以下更動,以達到劃分資訊層級、強化視覺辨識、提升使用體驗與效率的目標:
儘管事前已經盡量規劃準備,但到了實際實作時,還是出現了一些意想不到的狀況。
台北捷運在午夜 12 點後的班次,仍算在當天(午夜前)的營運時段,並且會把 00 時的班次列在 23 時的下方。因此,若在凌晨查詢列車,可能會查到「隔天」的班次。而且在排序時,這些「00:xx」的班次會被排在首班車之前,造成判斷上的困難。
這裡的解法是以「當天內的秒數」來記錄時間,並將一天的結束時間從 23:59 延長至 26:59。如此一來,隔天 00:00(即 24:00,第 86,401 秒) 就會確實排在當天 23:59(第 86,400 秒) 之後。
不過,這種作法也有副作用:就是無法正確表達那些在 00:00 ~ 03:00 發出「首班車」的時刻。考量到台北捷運的首班車皆在 06:00 之後,這個問題實際上可以忽略。
由於開發過程中一直在本機環境進行,因此即使後端有大量時間相關的操作(如查詢時刻表、依據星期設定 query parameters 等),也完全沒想過會有時差問題。直到部署上去後才發現,伺服器是在美東時間,而所有靜態資料都是台北時間,導致校車與捷運的時間全都對不上;只有公車因全程使用 ETA API,才成功避開這個坑。
因為所有日期處理都透過 dayjs 完成,所以最後的解法是建立一個已經設定時區的 dayjs instance,並全面替換原本的用法。唯獨取用 Unix timestamp 的部分維持不變,因為時差不會影響 timestamp 本身,但會影響格式化後的日期及時間數值(即 YMDdHms 的值)。
以上便是整個重構與實作過程的紀錄。
整個開發歷時一個多月,幾乎有一半時間都花在試錯上。從架構設計、工具選擇,到組件切分與路徑規劃,都經歷了反覆探索和調整。雖然成果遠稱不上完美,但確實讓專案重獲新生,也讓我從中獲益良多。