靜態頁面生成可減輕資料庫負載、利於 SEO。本文介紹一個輕量級模板引擎 TextTemplate,採用預先分析模板、分割為靜態片段與標記陣列的策略,渲染時僅做字串拼接,效能遠優於重複 Replace。
{TagName} 為預留位置。_contentParts (靜態字串) 與 _tags (標記資訊) 陣列。
public class TextTemplate
{
private readonly string[] _contentParts;
private readonly TextTemplateTag[] _tags;
private readonly int _tagCount;
// 內部標記結構
private class TextTemplateTag
{
public string Name { get; }
public int Position { get; }
public int Length { get; }
public TextTemplateTag(string name, int pos, int len)
{ Name = name; Position = pos; Length = len; }
}
// 從字串建構
public TextTemplate(string content) => Parse(content);
// 從檔案建構 (支援編碼)
public TextTemplate(string filePath, Encoding encoding)
{
using (var sr = new StreamReader(filePath, encoding))
Parse(sr.ReadToEnd());
}
private void Parse(string content)
{
var matches = Regex.Matches(content, @"\{\w+\}");
_tagCount = matches.Count;
_tags = new TextTemplateTag[_tagCount];
_contentParts = new string[_tagCount + 1];
int start = 0;
for (int i = 0; i < _tagCount; i++)
{
var m = matches[i];
_tags[i] = new TextTemplateTag(
m.Value.Substring(1, m.Value.Length - 2), // 去掉 { }
m.Index, m.Length);
_contentParts[i] = content.Substring(start, m.Index - start);
start = m.Index + m.Length;
}
_contentParts[_tagCount] = content.Substring(start);
}
// 依 Hashtable 鍵值渲染
public string Render(Hashtable values)
{
var sb = new StringBuilder(8192);
for (int i = 0; i < _tagCount; i++)
{
sb.Append(_contentParts[i]);
object val = values[_tags[i].Name];
sb.Append(val?.ToString() ?? $"{{{_tags[i].Name}}}"); // 找不到保留原標記
}
sb.Append(_contentParts[_tagCount]);
return sb.ToString();
}
// 依參數順序渲染
public string Render(params object[] args)
{
var sb = new StringBuilder(2048);
for (int i = 0; i < _tagCount; i++)
{
sb.Append(_contentParts[i]);
sb.Append(args[i]?.ToString() ?? "");
}
sb.Append(_contentParts[_tagCount]);
return sb.ToString();
}
// 直接存檔
public void SaveAs(string file, Encoding encoding, Hashtable values)
{
File.WriteAllText(file, Render(values), encoding);
}
public void SaveAs(string file, Encoding encoding, params object[] args)
{
File.WriteAllText(file, Render(args), encoding);
}
}
var tpl = new TextTemplate("
");
// 依順序
string html1 = tpl.Render("pic.jpg", "產品圖");
// 依名稱
var ht = new Hashtable { ["src"] = "pic.jpg", ["alt"] = "產品圖" };
string html2 = tpl.Render(ht);
// 模板檔 Web.config.template
/*
<connectionStrings>
<add name="MainDb" connectionString="{CONN_STR}" providerName="System.Data.SqlClient" />
</connectionStrings>
*/
var cfgTpl = new TextTemplate("Web.config.template", Encoding.UTF8);
cfgTpl.SaveAs("Web.config", Encoding.UTF8,
new Hashtable { ["CONN_STR"] = "Server=.;Database=MyDb;Integrated Security=True" });
比起 XmlDocument 操作節點,模板法更直觀、易維護、支援任意文字格式 (HTML、XML、JSON、SQL、CodeGen)。
支援以特定標記為界,將大模板拆分為多個子模板 (如分段生成頁首/內容/頁尾)。
| 指標 | String.Replace 迴圈 | TextTemplate |
|---|---|---|
| 模板解析次數 | 每次渲染都解析 | 僅初始化一次 |
| 字串配置 | 每次 Replace 產生新字串 | 單次 StringBuilder 組裝 |
| 標記值含標記風險 | 高 (巢狀替換錯誤) | 無 (不遞迴替換) |
| 適用場景 | 極簡單、單次渲染 | 高頻、大模板、多頁生成 |
TextTemplate 展示了「預編譯模板」的經典模式,程式碼精簡無相依,適合嵌入專案作為輕量靜態頁生成、程式碼產生、設定檔模板化的基礎設施。