<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Next-Js on 技術、遊戲、生活通俗易懂的解釋</title><link>https://takao.blog/zh-tw/tags/next-js/</link><description>Recent content in Next-Js on 技術、遊戲、生活通俗易懂的解釋</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><copyright>Commentary of Takao</copyright><lastBuildDate>Tue, 05 May 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://takao.blog/zh-tw/tags/next-js/index.xml" rel="self" type="application/rss+xml"/><item><title>2026 靜態站點產生器：Astro、Hugo 與 11ty</title><link>https://takao.blog/zh-tw/web/static-site-generators-2026-comparison/</link><pubDate>Tue, 05 May 2026 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/static-site-generators-2026-comparison/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/static-site-generators-2026-comparison-zh-tw.png" alt="Featured image of post 2026 靜態站點產生器：Astro、Hugo 與 11ty" /&gt;&lt;h2 id="2026-靜態站點產生器astrohugo-與-11ty"&gt;2026 靜態站點產生器：Astro、Hugo 與 11ty
&lt;/h2&gt;&lt;p&gt;2026 年的靜態站點生成器格局比以往任何時候都更加多樣化。開發人員可以在基於 Go 的速度極快的 &lt;strong&gt;Hugo&lt;/strong&gt;、島嶼架構先驅 &lt;strong&gt;Astro&lt;/strong&gt; 和極簡、靈活的 &lt;strong&gt;11ty&lt;/strong&gt; (Eleventy) 之間進行選擇。每個工具都在不同的場景中表現出色——了解它們的權衡對於為您的專案選擇正確的工具至關重要。&lt;/p&gt;
&lt;p&gt;此比較從建置速度、範本、內容管理、影像最佳化、i18n 和部署方面對它們進行評估。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="建構速度"&gt;建構速度
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;SSG&lt;/th&gt;
					&lt;th&gt;語言&lt;/th&gt;
					&lt;th&gt;建置時間（10k 頁）&lt;/th&gt;
					&lt;th&gt;冷啟動&lt;/th&gt;
					&lt;th&gt;開發伺服器重新載入&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;雨果&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;去&lt;/td&gt;
					&lt;td&gt;〜3-5秒&lt;/td&gt;
					&lt;td&gt;即時&lt;/td&gt;
					&lt;td&gt;〜50ms&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;天文學&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Node.js（打字稿）&lt;/td&gt;
					&lt;td&gt;~45-90 年代&lt;/td&gt;
					&lt;td&gt;中&lt;/td&gt;
					&lt;td&gt;約 200 毫秒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;11歲&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Node.js（JavaScript）&lt;/td&gt;
					&lt;td&gt;〜60-120秒&lt;/td&gt;
					&lt;td&gt;中&lt;/td&gt;
					&lt;td&gt;約 300 毫秒&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Hugo&lt;/strong&gt; 在建造速度方面擊敗了競爭對手。其基於 Go 的架構可在幾秒鐘內處理數千個頁面，使其成為大型文件網站和部落格的理想選擇。 &lt;strong&gt;Astro&lt;/strong&gt; 為其功能集提供了出色的速度，尤其是其部分補水方法。 &lt;strong&gt;11ty&lt;/strong&gt; 在大型站點上速度較慢，但其簡單性意味著小型專案的開銷更少。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="模板化和靈活性"&gt;模板化和靈活性
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Hugo&lt;/strong&gt; 使用 Go 範本及其自己的 &lt;code&gt;.RenderShortcodes&lt;/code&gt; 管道。學習曲線很陡——Go 模板語法很特殊——但短代碼系統很強大。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-hugo-html" data-lang="hugo-html"&gt;{{ $pages := where .Site.RegularPages &amp;#34;Section&amp;#34; &amp;#34;blog&amp;#34; }}
{{ range $pages }}
 &amp;lt;article&amp;gt;
 &amp;lt;h2&amp;gt;&amp;lt;a href=&amp;#34;{{ .RelPermalink }}&amp;#34;&amp;gt;{{ .Title }}&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
 &amp;lt;p&amp;gt;{{ .Summary }}&amp;lt;/p&amp;gt;
 &amp;lt;/article&amp;gt;
{{ end }}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Astro&lt;/strong&gt; 使用 &lt;code&gt;.astro&lt;/code&gt; 元件文件，將 HTML、JavaScript 和 CSS 混合在單一文件元件模型中。它透過其島嶼架構支援 UI 框架（React、Vue、Svelte、Solid）。&lt;/p&gt;</description></item><item><title>PPR（部分預先渲染）：合併靜態和動態路由</title><link>https://takao.blog/zh-tw/web/nextjs-partial-prerendering-ppr/</link><pubDate>Tue, 10 Feb 2026 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/nextjs-partial-prerendering-ppr/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/nextjs-partial-prerendering-ppr-zh-tw.png" alt="Featured image of post PPR（部分預先渲染）：合併靜態和動態路由" /&gt;&lt;h2 id="問題靜態與動態的權衡"&gt;問題：靜態與動態的權衡
&lt;/h2&gt;&lt;p&gt;傳統上，Next.js 開發人員面臨著一個二元選擇：&lt;strong&gt;靜態生成&lt;/strong&gt;（快速但陳舊）或&lt;strong&gt;伺服器端渲染&lt;/strong&gt;（新鮮但較慢）。部分預先渲染（PPR）透過將兩種方法結合在一個頁面中消除了這種權衡——靜態 HTML shell 立即串流傳輸，而動態內容透過 React Suspense 載​​入。&lt;/p&gt;
&lt;h2 id="ppr-的工作原理"&gt;PPR 的工作原理
&lt;/h2&gt;&lt;p&gt;PPR 的工作原理是使用 React Suspense 識別&lt;strong&gt;靜態和動態邊界&lt;/strong&gt;。在建置期間，Next.js 預先渲染靜態 shell。在請求時，它會用串流動態內容取代 Suspense 回退：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tsx" data-lang="tsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// page.tsx — PPR enabled
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;Suspense&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;@/components/header&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;PersonalizedGreeting&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;@/components/greeting&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;ProductList&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;@/components/products&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HomePage() {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#75715e"&gt;/* Static — prerendered at build time */&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;Header&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#75715e"&gt;/* Dynamic — streamed at request time */&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;Suspense&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fallback&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&amp;lt;&lt;span style="color:#f92672"&gt;GreetingSkeleton&lt;/span&gt; /&amp;gt;}&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;PersonalizedGreeting&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;Suspense&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#75715e"&gt;/* Static — prerendered at build time */&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;ProductList&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="配置"&gt;配置
&lt;/h2&gt;&lt;p&gt;在 &lt;code&gt;next.config.ts&lt;/code&gt; 中啟用 PPR：&lt;/p&gt;</description></item><item><title>Next.js 15 Stable Release: 最佳 Practices in Production</title><link>https://takao.blog/zh-tw/web/nextjs-15-stable-features-overview/</link><pubDate>Sun, 05 Oct 2025 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/nextjs-15-stable-features-overview/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/nextjs-15-stable-features-overview-zh-tw.png" alt="Featured image of post Next.js 15 Stable Release: 最佳 Practices in Production" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Next.js 15 ships as a stable release with &lt;strong&gt;React 19 支援&lt;/strong&gt;, a redesigned caching model, and major API improvements for async request handling. This version defaults to &lt;strong&gt;no caching&lt;/strong&gt; for fetch requests and GET Route handlers, simplifying behavior predictability. It also introduces &lt;code&gt;async&lt;/code&gt; variants for &lt;code&gt;cookies()&lt;/code&gt;, &lt;code&gt;headers()&lt;/code&gt;, and &lt;code&gt;params()&lt;/code&gt;, aligning with React 19&amp;rsquo;s async rendering paradigm. This article covers every breaking change and best practice for production adoption.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="react-19-stable-compatibility"&gt;React 19 Stable Compatibility
&lt;/h2&gt;&lt;p&gt;Next.js 15 pairs with &lt;strong&gt;React 19 stable&lt;/strong&gt;, enabling the new &lt;strong&gt;React Compiler&lt;/strong&gt; to automatically memoize components and hooks. This eliminates the need for manual &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;useCallback&lt;/code&gt;, and &lt;code&gt;React.memo&lt;/code&gt; in many cases.&lt;/p&gt;</description></item><item><title>揭秘 Next.js 應用路由器快取機制</title><link>https://takao.blog/zh-tw/web/nextjs-app-router-caching/</link><pubDate>Thu, 10 Apr 2025 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/nextjs-app-router-caching/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/nextjs-app-router-caching-zh-tw.png" alt="Featured image of post 揭秘 Next.js 應用路由器快取機制" /&gt;&lt;h2 id="介紹"&gt;介紹
&lt;/h2&gt;&lt;p&gt;Next.js App Router 具有強大的快取架構，旨在優化載入速度並減少後端 API 開銷。&lt;/p&gt;
&lt;p&gt;然而，由於 Next.js 整合了多個快取層，開發人員經常遇到除錯問題。常見問題包括頁面顯示陳舊資料或 API 觸發過於頻繁。&lt;/p&gt;
&lt;p&gt;本文透過解釋 Next.js 中的&lt;strong&gt;四種快取機制&lt;/strong&gt;以及配置、刷新和無效它們的技術來消除混亂。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-nextjs-app-router-的四個快取層"&gt;1. Next.js App Router 的四個快取層
&lt;/h2&gt;&lt;p&gt;Next.js App Router 使用四個獨立的快取層：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Client Side]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 1. Router Cache (Temporary in-browser navigation cache)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Server Side]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 2. Request Memoization (React component render-cycle memory)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 3. Data Cache (Persistent API request data across users)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 4. Full Route Cache (Static HTML and RSC payloads at build-time)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以下總結各層的特色：&lt;/p&gt;</description></item><item><title>Next.js 國際化：路由與內容策略</title><link>https://takao.blog/zh-tw/web/nextjs-i18n-routing/</link><pubDate>Tue, 08 Oct 2024 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/nextjs-i18n-routing/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/nextjs-i18n-routing-zh-tw.png" alt="Featured image of post Next.js 國際化：路由與內容策略" /&gt;&lt;h2 id="介紹"&gt;介紹
&lt;/h2&gt;&lt;p&gt;國際化是現代 Web 應用程式的基本要求。 Next.js 已將其 i18n 支援從 Pages Router 中的整合路由系統發展為 App Router 中靈活的基於中間件的方法。本文涵蓋多語言 Next.js 應用程式的路由策略、區域設定偵測、內容管理和 SEO 最佳化。&lt;/p&gt;
&lt;p&gt;關鍵概念是區域設定、預設區域設定、區域設定偵測以及子路徑路由和域路由之間的權衡。 App Router 方法提供了更大的靈活性，但比其前身需要更多的手動設定。&lt;/p&gt;
&lt;h2 id="路由策略子路徑與域"&gt;路由策略－子路徑與域
&lt;/h2&gt;&lt;p&gt;子路徑和網域路由之間的選擇對 SEO 和基礎設施有重大影響。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;標準&lt;/th&gt;
					&lt;th&gt;子路徑 (&lt;code&gt;/en/about&lt;/code&gt;)&lt;/th&gt;
					&lt;th&gt;域 (&lt;code&gt;en.example.com&lt;/code&gt;)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;SEO權威&lt;/td&gt;
					&lt;td&gt;共享域權限&lt;/td&gt;
					&lt;td&gt;每個語言環境獨立&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;地理定位&lt;/td&gt;
					&lt;td&gt;訊號較弱&lt;/td&gt;
					&lt;td&gt;更強的訊號&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;基礎架構&lt;/td&gt;
					&lt;td&gt;單一部署&lt;/td&gt;
					&lt;td&gt;多重部署&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;分析&lt;/td&gt;
					&lt;td&gt;統一&lt;/td&gt;
					&lt;td&gt;分段&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;對於大多數項目，建議使用子路徑路由，因為它簡單且共享網域權限。 App Router 中的實作使用中間件來處理區域設定前綴重寫。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;next/server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;NextRequest&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;next/server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;ja&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;fr&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;defaultLocale&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;middleware&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;NextRequest&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt; } &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;nextUrl&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pathnameHasLocale&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;some&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#a6e22e"&gt;locale&lt;/span&gt;) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;startsWith&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;locale&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/`&lt;/span&gt;) &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;locale&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;pathnameHasLocale&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;locale&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cookies&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;NEXT_LOCALE&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;defaultLocale&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;redirect&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;URL&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;locale&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;matcher&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;/((?!api|_next|favicon.ico).*)&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="區域設定檢測和協商"&gt;區域設定檢測和協商
&lt;/h2&gt;&lt;p&gt;Next.js 透過協商鏈確定區域設定：cookie 到 &lt;code&gt;Accept-Language&lt;/code&gt; 標頭到預設區域設定。 &lt;code&gt;NEXT_LOCALE&lt;/code&gt; cookie 約定允許使用者保留其語言首選項。&lt;/p&gt;</description></item><item><title>Next.js 中間件：路由、驗證和邊緣運算</title><link>https://takao.blog/zh-tw/web/nextjs-middleware/</link><pubDate>Tue, 18 Jun 2024 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/nextjs-middleware/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/nextjs-middleware-zh-tw.png" alt="Featured image of post Next.js 中間件：路由、驗證和邊緣運算" /&gt;&lt;h2 id="介紹"&gt;介紹
&lt;/h2&gt;&lt;p&gt;Next.js 中間件在每個請求之前執行，在邊緣或無伺服器區域運行。專案根目錄下的單一 &lt;code&gt;middleware.ts&lt;/code&gt; 檔案會攔截傳入要求，並且可以在請求到達匹配路由之前重定向、重寫、操作標頭或執行驗證檢查。&lt;/p&gt;
&lt;p&gt;中間件非常適合在頁面渲染之前進行快速、基於邊緣的決策。預設情況下，它不在靜態資源或 &lt;code&gt;_next/static&lt;/code&gt; 上運作。&lt;/p&gt;
&lt;h2 id="基本結構"&gt;基本結構
&lt;/h2&gt;&lt;p&gt;中間件檔案匯出一個接收 &lt;code&gt;NextRequest&lt;/code&gt; 並傳回 &lt;code&gt;NextResponse&lt;/code&gt; 的函數，以及定義哪些路徑觸發執行的 &lt;code&gt;config&lt;/code&gt; 物件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;next/server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;NextRequest&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;next/server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;middleware&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;NextRequest&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`Request: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;nextUrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;matcher&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;/((?!api|_next/static|_next/image|favicon.ico).*)&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;matcher&lt;/code&gt; 配置使用帶有負向前瞻的路徑到正規表示式語法來排除靜態檔案。對於特定於部分的中間件，請使用多種模式，例如 &lt;code&gt;['/dashboard/:path*', '/admin/:path*']&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="認證與授權"&gt;認證與授權
&lt;/h2&gt;&lt;p&gt;在中間件中處理身份驗證優於每頁檢查，因為它提供了單一事實來源並消除了頁面轉換時的閃爍。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;middleware&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;NextRequest&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;token&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cookies&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;session&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt; } &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;nextUrl&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;token&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;startsWith&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/dashboard&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;redirect&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;URL&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/login&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;token&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pathname&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;startsWith&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/login&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;redirect&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;URL&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/dashboard&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NextResponse&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;對於基於角色的訪問，請在允許訪問管理路由之前解碼 JWT 或會話令牌並檢查使用者的角色。 NextAuth.js、Clerk 和 Auth0 都提供了簡化此過程的中間件助手。&lt;/p&gt;</description></item><item><title>Next.js Image 優化: From 設定 to 進階 Patterns</title><link>https://takao.blog/zh-tw/web/nextjs-image-optimization/</link><pubDate>Tue, 26 Mar 2024 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/nextjs-image-optimization/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/nextjs-image-optimization-zh-tw.png" alt="Featured image of post Next.js Image 優化: From 設定 to 進階 Patterns" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Images account for over 50% of total webpage bytes on average, making optimization essential for performance. Next.js provides a comprehensive image optimization pipeline through the &lt;code&gt;next/image&lt;/code&gt; component and its built-in Image Optimization API. This article covers setup, configuration, and advanced patterns with a focus on Core Web Vitals impact.&lt;/p&gt;
&lt;h2 id="the-nextimage-component"&gt;The next/image Component
&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;next/image&lt;/code&gt; component extends the HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element with automatic optimization features: lazy loading, responsive srcset generation, width and height enforcement to prevent Cumulative Layout Shift, blur-up placeholders, and automatic format negotiation.&lt;/p&gt;</description></item><item><title>React 伺服器元件：Web 渲染的未來</title><link>https://takao.blog/zh-tw/web/react-server-components/</link><pubDate>Mon, 08 Jan 2024 00:00:00 +0900</pubDate><guid>https://takao.blog/zh-tw/web/react-server-components/</guid><description>&lt;img src="https://takao.blog/img/thumbnail/react-server-components-zh-tw.png" alt="Featured image of post React 伺服器元件：Web 渲染的未來" /&gt;&lt;p&gt;React 服务器组件 (RSC) 代表了 React 应用程序渲染方式的范式转变。组件专门在服务器上运行，向客户端发送零 JavaScript。這與伺服器端渲染（SSR）不同，後者在伺服器上渲染元件，但仍然將它們的所有 JavaScript 發送到客戶端進行補水。 RSC 產生一種特殊的序列化格式——RSC 有效負載——客戶端上的 React 協調器使​​用它來重建元件樹，而不執行元件程式碼。&lt;/p&gt;
&lt;p&gt;核心好處是客戶端 JavaScript 的顯著減少。不需要互動性的佈局元件、資料取得邏輯和表示元素可以完全在伺服器上執行。更小的捆綁包意味著更快的頁面加載、更低的記憶體使用量以及改進的 Core Web Vitals。&lt;/p&gt;
&lt;h2 id="伺服器與客戶端的邊界"&gt;伺服器與客戶端的邊界
&lt;/h2&gt;&lt;p&gt;指令 &lt;code&gt;'use client'&lt;/code&gt; 和 &lt;code&gt;'use server'&lt;/code&gt; 定義邊界。在支援 RSC 的框架（Next.js App Router、Hydrogen 等）中，任何沒有指令的元件預設都是伺服器元件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This is a server component by default
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;db&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;@/lib/db&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ProductList&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;products&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;query&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;SELECT * FROM products&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ul&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#a6e22e"&gt;products&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;) =&amp;gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;li&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;}&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;}&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/li&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ))}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/ul&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This is a client component
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;use client&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;useState&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LikeButton&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;productId&lt;/span&gt; }) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; [&lt;span style="color:#a6e22e"&gt;liked&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;setLiked&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;useState&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;button&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onClick&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{() =&amp;gt; &lt;span style="color:#a6e22e"&gt;setLiked&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;liked&lt;/span&gt;)}&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#a6e22e"&gt;liked&lt;/span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;♥&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;♡&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/button&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;關鍵規則：伺服器元件不能使用狀態、效果或瀏覽器 API。從伺服器傳遞到客戶端的道具必須是可序列化的－沒有函數，沒有類別實例，沒有循環引用。客戶端元件可以透過組合匯入伺服器元件（將伺服器元件作為子元件或 props 傳遞），但不能直接匯入它們。&lt;/p&gt;</description></item></channel></rss>