每頁不同的 <title>、<meta keywords>、<meta description> 是 SEO 基礎。本文比較四種動態設定方案,並解決用戶控件巢狀層級不固定的傳遞難題。
| 方案 | 實作 | 優點 | 缺點 |
|---|---|---|---|
| 1. Response.Write 整頁 | 手寫所有 HTML | 完全控制 | 喪失 WebForms 優勢、維護困難 |
| 2. <title runat="server" id="t"> | 伺服器端控制 title | 簡單 | 輸出 HTML 含 id="t"、不支援 meta |
| 3. Literal 控件佔位 (推薦) | <asp:Literal ID="ltTitle" runat="server" /> | 乾淨輸出、支援任意標籤、無額外屬性 | 需手動寫完整標籤字串 |
| 4. HtmlHead 控件 (ASP.NET 2.0+) | <head runat="server"> + Page.Header.Title/Controls.Add | 強型別 API、自動編碼、支援樣式/腳本註冊 | 需完整 head runat=server |
<head>
<asp:Literal ID="ltTitle" runat="server" />
<asp:Literal ID="ltKeywords" runat="server" />
<asp:Literal ID="ltDescription" runat="server" />
</head>
// 後端
ltTitle.Text = "動態標題 ";
ltKeywords.Text = "";
ltDescription.Text = "";
問題:頁面 → UserControlA → UserControlB (孫代) 需設定 title,層級不固定。
解決:建立基類 BaseUserControl,提供 SetHeadLiteral 向上尋找最近的 HtmlForm 再 FindControl:
public class BaseUserControl : UserControl
{
public void SetTitle(string title)
{
SetHeadLiteral("ltTitle", $"{HttpUtility.HtmlEncode(title)} ");
}
public void SetKeywords(string keywords)
{
SetHeadLiteral("ltKeywords", $"");
}
private void SetHeadLiteral(string literalId, string html)
{
Control ctrl = this;
// 向上找到 HtmlForm (頁面根容器)
while (ctrl != null && !(ctrl is HtmlForm))
ctrl = ctrl.Parent;
if (ctrl != null)
{
Literal lit = ctrl.FindControl(literalId) as Literal;
lit?.Text = html; // 找到則覆寫,最後執行者勝出
}
}
}
所有用戶控件繼承 BaseUserControl,在任意層級呼叫 base.SetTitle("新標題") 即可生效。
<head runat="server" id="Head1">
<title><asp:Literal ID="ltTitle" runat="server" /></title>
</head>
// 後端
Page.Title = "動態標題"; // 自動輸出到 title
// 或
HtmlMeta kw = new HtmlMeta { Name = "keywords", Content = "關鍵字" };
Page.Header.Controls.Add(kw);
ASP.NET 2.0+ 專案優先使用 Page.Header / Page.Title / Page.MetaKeywords / Page.MetaDescription 內建屬性,最乾淨且標準。