11---
2+ // astro: ... 引入 astro 原生的虛擬模組, slug: 通常只包含 小寫英文、數字 和 連字號 -
23import { type CollectionEntry , getCollection , render } from " astro:content" ;
34
45import BlogLayout from " ../../layouts/BlogLayout.astro" ;
56
7+ // 動態路由的靜態路徑產生函式
68export async function getStaticPaths() {
7- const posts = await getCollection (" blog" );
9+ const posts = await getCollection (" blog" ); // 抓取所有文章
810 return posts .map ((post , index ) => ({
9- params: { slug: post .id },
11+ params: { slug: post .id }, // slug variable = post.id
1012 props: {
13+ // 把文章資料傳給頁面使用
1114 post ,
1215 prev: posts [index - 1 ] || null ,
1316 next: posts [index + 1 ] || null ,
1417 },
1518 }));
1619}
20+ // 定義頁面接收的 props 類型,同時能做到 Error checking
1721type Props = {
1822 post: CollectionEntry <" blog" >;
1923 prev: CollectionEntry <" blog" > | null ;
2024 next: CollectionEntry <" blog" > | null ;
2125};
2226
23- const { post, prev, next } = Astro .props ;
27+ const { post, prev, next } = Astro .props ; // 藉由 Astro 框架來傳遞 props
28+ /* Equals to: (所以 Astro.props 就是一個 JavaScript 物件)
29+ const post = Astro.props.post;
30+ const prev = Astro.props.prev;
31+ const next = Astro.props.next;
32+ */
2433const { Content, headings } = await render (post );
2534---
2635
@@ -38,10 +47,15 @@ const { Content, headings } = await render(post);
3847 import PhotoSwipeLightbox from "photoswipe/lightbox";
3948 import "photoswipe/style.css";
4049
50+ // 啟用了 View Transitions ,當使用者按上一頁或連結時,不會整頁刷新。
51+ // 所以使用 astro:page-load 確定每次頁面內容更新後,
52+ // 程式碼都會被執行一次,來重新綁定圖片點擊事件。
4153 document.addEventListener("astro:page-load", () => {
4254 document
4355 .querySelectorAll(".article-entry img")
4456 // @ts-expect-error - change from Element to HTMLImageElement
57+ // PhotoSwipe 套件要求圖片必須被包在 <a> 連結標籤內才能運作
58+ // 所以這裡的邏輯是把每一張圖片都包在 <a> 標籤內,並且把圖片的 src 設為連結的 href
4559 .forEach((element: HTMLImageElement) => {
4660 if (
4761 element.parentElement?.tagName === "A" ||
@@ -50,12 +64,13 @@ const { Content, headings } = await render(post);
5064 return;
5165 const a = document.createElement("a");
5266 a.href ? (a.href = element.src) : a.setAttribute("href", element.src);
67+ // 確認圖片是否載入完成,因為 PhotoSwipe 需要知道圖片的寬高才能正常運作
5368 if (element.naturalWidth || element.naturalHeight) {
5469 a.dataset.pswpWidth = element.naturalWidth + "";
5570 a.dataset.pswpHeight = element.naturalHeight + "";
5671 } else {
5772 console.warn(
58- "Image naturalWidth and naturalHeight cannot be obtained right now, fallback to onload."
73+ "Image naturalWidth and naturalHeight cannot be obtained right now, fallback to onload.",
5974 );
6075 element.onload = () => {
6176 a.dataset.pswpWidth = element.naturalWidth + "";
@@ -73,10 +88,12 @@ const { Content, headings } = await render(post);
7388 gallery: ".article-entry",
7489 children: "a.article-gallery-item",
7590 pswpModule: () => import("photoswipe"),
91+ //// pswpModule 動態引入模組 (Code Splitting),節省效能
7692 }).init();
7793 });
7894</script >
7995
96+ { /* 針對流程圖,目的是解決「深色模式切換」與「頁面跳轉」的問題 */ }
8097<script >
8198 import mermaid from "mermaid";
8299
@@ -130,6 +147,7 @@ const { Content, headings } = await render(post);
130147 querySelector: "pre.mermaid",
131148 });
132149 };
150+ // 當使用者切換主題時,會觸發 theme-set 事件,邏輯是重新載入 Mermaid 圖表
133151 const themeSetHandler = (e: any) => {
134152 let theme = e.detail.theme;
135153 if (e.detail.theme === "light") {
@@ -149,6 +167,7 @@ const { Content, headings } = await render(post);
149167 .catch(console.error);
150168 };
151169 const mermaidInit = () => {
170+ // 為了確保事件不會被重複綁定,先移除再添加
152171 document.body.removeEventListener("theme-set", themeSetHandler);
153172 document.body.addEventListener("theme-set", themeSetHandler);
154173
0 commit comments