因為逛了一下別人的 GitHub,發現大家的 profile README 都在比 hi-hua(稱讚意味)的,所以想說也來做點東西塞進去 XDD
於是做了這個!隨機每日金句(有些很靠杯 XD):
用下面這段 HTML 就可以插入隨機每日金句:
<img src="https://testing-quotes.goescat1024.workers.dev/" width="500" />
這個 API 放在 Cloudflare Workers。Cloudflare Workers 是 Cloudflare 提供的 Serverless 執行環境,可以把 JavaScript / TypeScript 程式直接部署到 Cloudflare 的網路上執行。
它的概念很簡單:
收到 HTTP Request
↓
執行你的程式
↓
回傳 HTTP Response
不需要自己管理傳統 Server,也不用處理 VM、作業系統或 Nginx。Cloudflare 負責底層的執行環境與網路基礎設施,你只需要寫要執行的程式。
建立 Worker
進入 Cloudflare Dashboard:Workers & Pages → Create → Worker。
建立後可以直接在 Cloudflare 的線上 Editor 編輯程式。Deploy 後,Cloudflare 會提供一個像這樣的網址:
https://<worker-name>.<subdomain>.workers.dev
最基本的 Worker
Worker 最核心的概念就是:
export default {
async fetch(request) {
return new Response("Hello World!");
}
};
每次有人 request Worker,都會經過:
HTTP Request
↓
fetch(request)
↓
HTTP Response
所以 Worker 本質上就是一個可以直接接收 HTTP Request、執行程式邏輯,再回傳 Response 的 Serverless endpoint。
這次的 Testing Quote of the Day,就是在這個 fetch() 裡隨機選一句 Quote,再動態產生 SVG 回傳。
用陣列管理 Quote
所有名言直接定義在 QUOTES 陣列中:
const QUOTES = [
{
quote: "Testing shows the presence, not the absence of bugs.",
translation: "測試可以證明 Bug 存在,卻不能證明 Bug 不存在。",
source: "Edsger W. Dijkstra"
}
];
每次收到 Request 時,利用 Math.random() 隨機挑選一筆:
const item = QUOTES[Math.floor(Math.random() * QUOTES.length)];
因此每次請求都有機會得到不同的 Quote(沒快取的話,放在 Github profile 會有一層他們的 CDN,不會每次都 request 一次)。
直接產生 SVG
沒有準備圖片素材,而是讓 Worker 即時產生 SVG。
SVG 本身就是文字格式,所以可以直接透過 template string 組出完整的 <svg>,再用 <text>、<tspan> 等元素控制文字與排版。
這樣做的好處是圖片中的文字仍然是 SVG 內容,不需要預先產生幾十張圖片。
處理文字排版
不同 Quote 的長度不一樣,因此簡單做了一層排版:
fontSize():依英文長度調整字體大小wrapText():超過指定長度時自動換行translationFontSize():中文翻譯另外調整字體大小
例如較長的 Quote 就會自動縮小:
if (text.length <= 40) return 27; if (text.length <= 60) return 24; if (text.length <= 80) return 21; return 18;
雖然不是完整的排版引擎,但對這種固定尺寸的小型 SVG 已經夠用。
不要忘記 Escape
因為 Quote 最後會被插入 SVG/XML,所以不能直接把原始文字塞進去。
這裡用 escapeXML() 處理:
.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">")
避免 Quote 裡面的特殊字元破壞 SVG markup。
Worker 最後只需要回傳 SVG
產生完成後直接:
return new Response(svg, {
headers: {
"Content-Type": "image/svg+xml; charset=utf-8",
"Cache-Control": "no-store"
}
});
因此這個 Worker 本質上就是一個很小的 dynamic SVG endpoint:
Request → 隨機選 Quote → 產生 SVG → Response
沒有資料庫、沒有前端,也沒有圖片儲存最後得到的是一個可以直接嵌入 README、網站或其他地方的動態圖片 URL~