開發自訂伺服器控制項 (WebCustomControl) 時,繼承 WebControl 基類可獲得 Attributes 與 Attributes.CssStyle 兩個集合屬性,用於支援未強型別的 HTML 屬性與 CSS 樣式。然而,這兩個屬性的內部實作機制若不慎使用,會導致 ViewState 暴增、Postback 效能嚴重下降。
' WebControl 基類片段
Protected ReadOnly Property Attributes As AttributeCollection
Get
If _attributes Is Nothing Then
_attributes = New AttributeCollection(ViewState) ' 關鍵:傳入 ViewState (StateBag)
End If
Return _attributes
End Get
End Property
Protected ReadOnly Property Style As CssStyleCollection
Get
If _style Is Nothing Then
_style = New CssStyleCollection(ViewState) ' 同樣綁定 ViewState
End If
Return _style
End Get
End Property
AttributeCollection 與 CssStyleCollection 建構子皆接受 StateBag (即 ViewState),導致所有透過這兩個集合設定的鍵值對**自動持久化至 ViewState**。
某自訂報表控制項 AnalysisReport 設定少量自訂屬性:
Me.Attributes("data-report-id") = "RPT-2024-001"
Me.Attributes("data-user-role") = "Admin"
Me.Style("border-radius") = "4px"
Me.Style("box-shadow") = "0 2px 4px rgba(0,0,0,0.1)"
結果:
LoadViewState 耗時從 2ms 增至 18ms (模擬高延遲環境下更明顯)。WebControl 未提供關閉 Attributes/Style 寫入 ViewState 的選項 (如 ViewStateMode 對此無效)。Attributes("key") = value 傳遞資料給前端 JS,殊不知全寫入 ViewState。ASP.NET 頁面生命週期中,SaveViewState 發生於 PreRender 之後、Render 之前。在 Render 或 PreRender 之後設定 Attributes/Style,即不會被序列化進 ViewState。
Protected Overrides Sub Render(writer As HtmlTextWriter)
' 此時 ViewState 已儲存完畢,設定不會進入 ViewState
Me.Attributes("data-report-id") = ReportID
Me.Style("border-radius") = "4px"
MyBase.Render(writer)
End Sub
<ViewStateMode(UI.ViewStateMode.Disabled)> _
Public Property ReportID As String
Get : Return CStr(ViewState("ReportID")) : End Get
Set : ViewState("ReportID") = Value : End Set
End Property
Protected Overrides Sub Render(writer As HtmlTextWriter)
Me.Attributes("data-report-id") = Me.ReportID ' 只在渲染時輸出
MyBase.Render(writer)
End Sub
若控制項不需 Style、ToolTip、TabIndex 等 WebControl 功能,直接繼承 Control 基類,完全避開 Attributes/Style 集合。
實作 IControlState,但僅適用於控制項核心運作不可或缺的極少量資料。
Render 或 PreRenderComplete 之後?data-* 屬性在 Render 時輸出,或註冊 JSON 給 JS 變數。Trace.Write("ViewStateSize", ViewState.Length) 監控 ViewState 大小。<pages viewStateEncryptionMode="Auto"> 並壓縮 ViewState (Gzip 模組)。WebControl.Attributes 與 CssStyle 自動寫入 ViewState 的設計,是 ASP.NET 早期為了簡化狀態管理而做的妥協,但在現代高效能 Web 應用中已成為隱藏效能殺手。掌握生命週期時機點 (Render 後設定)、改用強型別屬性、或調整繼承鏈,即可徹底避開此陷阱,讓 ViewState 保持精簡高效。