作者:Steve Smith 和 Dave Brock
本文件說明 ASP.NET Core MVC 應用程式中所使用的檢視。 如需 Pages 的相關 Razor 資訊,請參閱 Razor ASP.NET Core 中的 Pages 架構和概念。
在模型檢視控制器 (MVC) 模式中,「檢視」會處理應用程式的資料呈現和使用者互動。 檢視是具有內嵌 Razor 標記的 HTML 範本。 Razor 標記是與 HTML 標記互動的程式碼,可以產生傳送至用戶端的網頁。
在 ASP.NET Core MVC 中,視圖是在 .cshtml 標記中使用 C# 程式設計語言 的 Razor 檔案。 通常,檢視檔案會分組成針對每個應用程式之控制器而命名的資料夾。 資料夾會儲存在應用程式根目錄的 Views 資料夾中:
資料夾Views中的一個Home資料夾代表Home控制器。
Home 資料夾包含 About、Contact 和 Index (首頁) 網頁的檢視。 使用者要求這三個網頁中的其中一個時,Home 控制器中的控制器動作可決定使用這三個檢視的哪一個來建置網頁並將其傳回給使用者。
使用配置,提供一致的網頁區段,並減少程式碼重複。 配置通常包含標頭、導覽和功能表項目,以及頁尾。 標頭和頁尾通常包含許多中繼資料項目以及與指令碼及樣式資源的連結的樣版標記。 配置可協助您避免在檢視中使用此未定案標記。
部分檢視透過管理檢視的可重複使用部分,來減少程式碼重複。 例如,部分檢視可用於出現在多個檢視中的部落格網站上的作者簡介。 作者自傳是一般檢視內容,不需要執行程式碼就能產生網頁內容。 透過模型繫結,作者自傳內容可以直接供視圖使用,因此,使用部分視圖來呈現這類型內容是理想的選擇。
檢視元件與部分檢視類似,可讓您減少重複的程式碼,但它們適用於需要程式碼在伺服器上執行才能轉譯網頁的檢視內容。 轉譯的內容需要資料庫互動時 (例如網站購物車),檢視元件十分有用。 檢視元件不僅限於模型繫結,還可以用於產生網頁輸出。
使用檢視的優點
檢視可協助在 MVC 應用程式內建立關注點分離,方法是區隔使用者介面標記與應用程式的其他部分。 遵循 SoC 設計可讓您的應用程式模組化,以提供數個優點:
- 應用程式較容易維護,因為其組織性較佳。 檢視一般會依應用程式功能分組。 這可讓您在處理功能時更輕鬆地找到相關檢視。
- 應用程式的組件是鬆散耦合的。 您可以將應用程式的視圖與業務邏輯和資料存取元件分開建置和更新。 您可以修改應用程式的檢視,而不一定需要更新應用程式的其他部分。
- 因為檢視是個別單位,所以可以更輕鬆地測試應用程式的使用者介面部分。
- 由於更佳的編排,使用者介面的區段較不容易被重複。
建立檢視
Views/[ControllerName] 資料夾中會建立控制器特有的檢視。 在控制器間共用的檢視會放在 Views/Shared 資料夾中。 若要建立檢視,請新增檔案,並讓它與建立關聯的控制器動作同名,且副檔名為 .cshtml。 若要建立與 About 控制器中 Home 動作對應的檢視,請在 About.cshtml 資料夾中建立 Views/Home 檔案:
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>
<p>Use this area to provide additional information.</p>
Razor 標記的開頭為 @ 符號。 在以大括弧 (ViewData["Title"]。 只要使用 @ 符號參考值,就可以在 HTML 內顯示值。 請參閱之前<h2>和<h3>元素的內容。
前面顯示的瀏覽內容只是整個網頁呈現給使用者的一部分。 其餘頁面的版面配置及檢視的常見層面都在其他檢視檔案中指定。 若要深入了解,請參閱配置主題。
控制器設定檢視的方式
檢視通常會從動作傳回為 ViewResult,這是 ActionResult 的類型。 您的操作方法可以直接創建並返回ViewResult,但這樣做並不常見。 因為大部分的控制器都是繼承自Controller,所以您只需要使用 View 協助方法傳回 ViewResult:
HomeController.cs:
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
當此操作回傳時,在最後一個區段中顯示的 About.cshtml 檢視將呈現為以下網頁:
View 輔助方法有多種多載版本。 您可以選擇性地指定:
要傳回的明確檢視:
return View("Orders");要傳遞至檢視的模型:
return View(Orders);視圖和模型:
return View("Orders", Orders);
檢視探索
動作傳回檢視時,會進行稱為「檢視探索」的程序。 此程序根據檢視名稱來決定使用的檢視檔案。
View 方法的預設行為 (return View();) 是返回名稱與呼叫它的動作方法相同的檢視。 例如,控制器的 AboutActionResult 方法名稱會用於搜尋名為 About.cshtml 的檢視檔案。 首先,執行階段會在 Views/[ControllerName] 資料夾中尋找檢視。 如果在這裡找不到相符的檢視,則會搜尋檢視的 Shared 資料夾。
無論是使用 ViewResult 以隱含方式傳回 return View();,還是使用 View 將檢視名稱明確地傳遞至 return View("<ViewName>"); 方法,都是沒有差別的。 在這兩種情況下,檢視探索會依此順序搜尋相符的檢視檔案:
Views/\[ControllerName]/\[ViewName].cshtmlViews/Shared/\[ViewName].cshtml
可以提供檢視檔案路徑,而不是檢視名稱。 如果使用從應用程式根目錄開始的絕對路徑 (選擇性地開始於 "/" 或 "~/"),則必須指定 .cshtml 副檔名:
return View("Views/Home/About.cshtml");
您也可以使用相對路徑來指定不同目錄中的檢視,但不包含 .cshtml 副檔名。 在 HomeController 內,您可以使用相對路徑返還 Index 的 Manage 檢視:
return View("../Manage/Index");
同樣地,您可以使用 "./" 前置詞來指出目前的控制器特定目錄:
return View("./About");
您可以使用自訂的 IViewLocationExpander,來變更應用程式內檢視定位的預設規則。
檢視探索依賴於透過檔案名稱尋找檢視檔案。 如果基礎檔案系統對大小寫敏感,則檢視名稱可能也會對大小寫敏感。 基於作業系統的相容性,控制器和動作名稱以及相關的檢視資料夾和檔案名稱的大小寫必須相符。 如果您在使用區分大小寫的檔案系統時遇到找不到檢視檔案的錯誤,請確認所請求的檢視檔案與實際檢視檔案名稱之間的大小寫一致。
請遵循最佳實踐來組織視圖的檔案結構,以便在控制器、動作和視圖間的關係中實現可維護性和清晰度。
將資料傳遞至檢視
使用數種方法將資料傳遞至檢視:
- 強型別資料:viewmodel
- 弱型別資料
-
ViewData(ViewDataAttribute) ViewBag
-
強型別資料 (viewmodel)
最穩健的方法是在檢視中指定 model 類型。 此模型通常稱為 viewmodel。 您將 viewmodel 類型的實例從動作傳遞到檢視。
使用 viewmodel 將資料傳遞到檢視,讓檢視能夠利用強型別檢查。
強式型別 (或強型別) 代表每個變數與常數都有明確定義的類型(例如,string、int 或 DateTime)。 在編譯時期檢查檢視中所使用之類型的有效性。
Visual Studio 與 Visual Studio Code 會使用稱為 IntelliSense 的功能,列出強型別類別成員。 當您想要查看 viewmodel 的屬性時,請鍵入 viewmodel 的變數名稱,後面接著句號 (.)。 這有助於更快速地撰寫程式碼,並且減少錯誤。
使用 @model 指示詞來指定模型。 使用該模型與 @Model:
@model WebApplication1.ViewModels.Address
<h2>Contact</h2>
<address>
@Model.Street<br>
@Model.City, @Model.State @Model.PostalCode<br>
<abbr title="Phone">P:</abbr> 425.555.0100
</address>
若要將模型提供給檢視,控制器會將它當成參數傳遞:
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
var viewModel = new Address()
{
Name = "Microsoft",
Street = "One Microsoft Way",
City = "Redmond",
State = "WA",
PostalCode = "98052-6399"
};
return View(viewModel);
}
您可以提供給檢視的模型類型沒有任何限制。 建議您使用行為(方法)定義最少或未定義的普通 CLR 物件 (POCO) 的視圖模型。 通常,viewmodel 類別會儲存在應用程式根目錄的 Models 資料夾或個別 ViewModels 資料夾。 前一個範例中使用的 Address 視圖模型是一個 POCO 視圖模型,儲存在一個名為 Address.cs: 的檔案中:
namespace WebApplication1.ViewModels
{
public class Address
{
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
}
沒有什麼阻止您將相同的類別用於 viewmodel 類型和商務模型類型。 不過,使用個別的模型可讓您的視圖與應用程式的商務邏輯和資料存取部分獨立地變動。 當模型使用模型繫結和驗證來處理使用者傳送至應用程式的資料時,模型與 viewmodel 的分離提供了額外的安全性優勢。
弱類型資料 (ViewData、[ViewData] 屬性和 ViewBag)
ViewBag
預設不適用於 Razor PagesPageModel類別。
除了強型別檢視之外,檢視還可以存取弱型別 (也稱為鬆散型別) 資料集合。 與強式型別不同,「弱式型別」 (或「鬆散型別」) 表示您未明確宣告所使用資料的類型。 您可以使用弱型別的資料集合,用來在控制器與檢視之間傳遞少量資料。
| 在 ... 之間傳遞資料 | 範例 |
|---|---|
| 控制器和視圖 | 使用資料填充下拉式清單。 |
| 檢視和版面配置視圖 | 透過檢視檔案,在配置檢視中設定 <title> 元素內容。 |
| 部分視圖和視圖 | 一種小工具,可根據使用者所要求的網頁來顯示資料。 |
此集合可以透過控制器和檢視上的 ViewData 或 ViewBag 屬性進行參考。
ViewData 屬性是一個具有弱型別物件的字典。
ViewBag 屬性是 ViewData 中提供基礎 ViewData 集合之動態屬性的包裝函式。 注意:ViewData 和 ViewBag 的鍵值查閱皆不區分大小寫。
ViewData 和 ViewBag 在執行階段會被動態解析。 因為它們未提供編譯時間類型檢查,所以兩者通常會比使用 viewmodel 更容易發生錯誤。 因此,有些開發人員會盡量少使用或不使用 ViewData 和 ViewBag。
ViewData
ViewData 是透過 string 鍵存取的 ViewDataDictionary 物件。 字串資料可以直接儲存和使用,而不需要轉換;但必須在擷取其他 ViewData 物件值時將其轉換為特定類型。 您可以使用 ViewData 將資料從控制器傳遞至檢視以及在檢視內傳遞資料,包括部分檢視和配置。
下列範例使用運作中 ViewData 來設定問候語和地址的值:
public IActionResult SomeAction()
{
ViewData["Greeting"] = "Hello";
ViewData["Address"] = new Address()
{
Name = "Steve",
Street = "123 Main St",
City = "Hudson",
State = "OH",
PostalCode = "44236"
};
return View();
}
使用視圖中的資料:
@{
// Since Address isn't a string, it requires a cast.
var address = ViewData["Address"] as Address;
}
@ViewData["Greeting"] World!
<address>
@address.Name<br>
@address.Street<br>
@address.City, @address.State @address.PostalCode
</address>
[ViewData] 屬性
另一個使用 ViewDataDictionary 的方法是 ViewDataAttribute。 控制器或 Razor 頁面模型上以 [ViewData] 屬性標示的屬性,其值會儲存並從字典載入。
在下列範例中,Home 控制器包含了一個以 [ViewData] 標示的 Title 屬性。
About 方法會設定 [About] 檢視的標題:
public class HomeController : Controller
{
[ViewData]
public string Title { get; set; }
public IActionResult About()
{
Title = "About Us";
ViewData["Message"] = "Your application description page.";
return View();
}
}
在此配置中,標題會從 ViewData 字典中讀取:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
ViewBag
ViewBag
預設不適用於 Razor PagesPageModel類別。
ViewBag 是 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData 物件,可供動態存取 ViewData 中儲存的物件。
ViewBag 的使用更為方便,因為它不需要進行轉換。 以下範例展示了如何使用ViewBag以達到與先前使用ViewData相同的結果:
public IActionResult SomeAction()
{
ViewBag.Greeting = "Hello";
ViewBag.Address = new Address()
{
Name = "Steve",
Street = "123 Main St",
City = "Hudson",
State = "OH",
PostalCode = "44236"
};
return View();
}
@ViewBag.Greeting World!
<address>
@ViewBag.Address.Name<br>
@ViewBag.Address.Street<br>
@ViewBag.Address.City, @ViewBag.Address.State @ViewBag.Address.PostalCode
</address>
同時使用 ViewData 和 ViewBag
ViewBag
預設不適用於 Razor PagesPageModel類別。
因為 ViewData 和 ViewBag 參照相同的基礎 ViewData 集合,所以您可以同時使用 ViewData 和 ViewBag,並在讀取和寫入值時於其間混合使用和比對。
使用 ViewBag 設定位於 About.cshtml 檢視頂端的標題,並使用 ViewData 設定描述:
@{
Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About Contoso";
ViewData["Description"] = "Let us tell you about Contoso's philosophy and mission.";
}
讀取屬性,但反向使用 ViewData 和 ViewBag。 在 _Layout.cshtml 檔案中,使用 ViewData 取得標題,並使用 ViewBag 取得描述:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"]</title>
<meta name="description" content="@ViewBag.Description">
...
請記住,字串不需要對 ViewData 進行類型轉換。 您可以使用 @ViewData["Title"],而不需要轉換。
可以同時使用 ViewData 和 ViewBag,也可以混合讀取和寫入屬性來一起使用。 以下標記將被渲染:
<!DOCTYPE html>
<html lang="en">
<head>
<title>About Contoso</title>
<meta name="description" content="Let us tell you about Contoso's philosophy and mission.">
...
ViewData 與 ViewBag 之間的差異摘要
ViewBag
預設不適用於 Razor PagesPageModel類別。
ViewData- 衍生自 ViewDataDictionary,因此其具有很實用的字典屬性,例如
ContainsKey、Add、Remove和Clear。 - 字典中的索引鍵是字串,因此允許空白字元。 範例:
ViewData["Some Key With Whitespace"] - 在檢視中必須對任何
string以外的類型進行型別轉換,才能使用ViewData。
- 衍生自 ViewDataDictionary,因此其具有很實用的字典屬性,例如
ViewBag- 衍生自
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData,因此允許使用點標記法 (@ViewBag.SomeKey = <value or object>) 來建立動態屬性,而不需要轉換。ViewBag的語法可以更快速地新增至控制器和檢視。 - 檢查 Null 值更簡單。 範例:
@ViewBag.Person?.Name
- 衍生自
使用 ViewData 或 ViewBag 的時機
ViewData 和 ViewBag 是同樣有效的方式,都可以在控制器與檢視之間傳遞少量資料。 使用哪一個取決於個人喜好。 您可以混合搭配 ViewData 和 ViewBag 物件;然而,使用一致的方式操作物件,更容易讀取和維護程式碼。 這兩種方法是在執行階段動態解析,因此容易導致執行階段錯誤。 有些開發小組會避免使用它們。
動態檢視
如果檢視未使用 @model 來宣告模型類型,但具有傳遞給它們的模型執行個體 (例如,return View(Address);),則可以動態參考執行個體的屬性:
<address>
@Model.Street<br>
@Model.City, @Model.State @Model.PostalCode<br>
<abbr title="Phone">P:</abbr> 425.555.0100
</address>
此功能提供彈性,但未提供編譯保護或 IntelliSense。 如果屬性不存在,則網頁產生會在執行階段失敗。
其他檢視功能
標籤協助程式可讓您輕鬆地將伺服器端行為新增至現有 HTML 標籤。 使用標籤協助程式就不需要在檢視內撰寫自訂程式碼或協助程式。 標籤協助程式會以屬性形式套用至 HTML 項目,而且無法處理它們的編輯器會忽略它們。 這可讓您在各種工具中編輯和轉譯檢視標記。
許多內建 HTML 協助程式都可以產生自訂 HTML 標記。 View Components 可以處理更複雜的使用者介面邏輯。 檢視元件提供的 SoC 與控制器和檢視提供的 SoC 相同。 它們可以消除處理一般使用者介面元素所用資料的操作和視圖的需求。
與許多其他 ASP.NET Core 層面一樣,檢視支援相依性插入,允許將服務插入檢視。
CSS 隔離
將 CSS 樣式隔離至個別頁面、檢視和元件,以減少或避免:
- 依賴於可能難以維護的全域樣式。
- 巢狀內容中的樣式衝突。
若要新增頁面或檢視的限定範圍 CSS 檔案,請將 CSS 樣式放在符合 .cshtml.css 檔案名稱的隨附 .cshtml 檔案中。 在下列範例中,Index.cshtml.css 檔案會提供只套用至 Index.cshtml 頁面或檢視的 CSS 樣式。
Pages/Index.cshtml.css (Razor Pages) 或 Views/Index.cshtml.css (MVC):
h1 {
color: red;
}
在建置時會發生 CSS 隔離。 架構會重新編寫 CSS 選取器,以符合應用程式頁面或檢視產生的標記。 重寫的 CSS 樣式會被打包並生成為靜態資產 ({APP ASSEMBLY}.styles.css)。 預留位置 {APP ASSEMBLY} 是專案的組件名稱。 組合 CSS 樣式的連結會放在應用程式的版面配置中。
在應用程式 <head> (Pages/Shared/_Layout.cshtml Pages) 的 Razor 內容或 Views/Shared/_Layout.cshtml (MVC) 中,新增或確認組合 CSS 樣式的連結是否存在:
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
在下列範例中,應用程式的組件名稱為 WebApp:
<link rel="stylesheet" href="WebApp.styles.css" />
範圍 CSS 檔案中定義的樣式只會套用至相符檔案的轉譯輸出。 在上述範例中,應用程式其他位置所定義的任何 h1 CSS 宣告都不會與 Index 的標題樣式衝突。 CSS 樣式串聯和繼承規則對於限定範圍的 CSS 檔案仍有效。 例如,直接套用至 <h1> 檔案中 Index.cshtml 元素的樣式會覆寫 Index.cshtml.css 中限定範圍 CSS 檔案的樣式。
在組合 CSS 檔案內,每個頁面、檢視或 Razor 元件都會與格式為 b-{STRING} 的範圍識別碼相關聯,其中 {STRING} 預留位置是架構所產生的十個字元字串。 下列範例提供在 Razor Pages 應用程式的 Index 頁面中對於前述 <h1> 元素的樣式:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
在從組合檔案套用 CSS 樣式的 Index 頁面中,範圍識別碼會附加為 HTML 屬性:
<h1 b-3xxtam6d07>
識別碼對於應用程式而言是唯一的。 在建置階段,會使用慣例 {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css 來建立專案組合,其中預留位置 {STATIC WEB ASSETS BASE PATH} 是靜態 Web 資產基底路徑。
如果使用其他專案 (例如 NuGet 套件或 Razor 類別庫),則組合的檔案:
- 參考使用 CSS 匯入的樣式。
- 不會作為應用程式中使用樣式的靜態 Web 資源發佈。
CSS 前置處理器支援
CSS 前置處理器可用來利用變數、巢狀、模組、混合和繼承等功能來改善 CSS 開發。 雖然 CSS 隔離原生不支援 Sass 或 Less 等 CSS 前置處理器,但只要架構在建置程序期間重寫 CSS 選取器之前進行前置處理器編譯,整合 CSS 前置處理器就會很順暢。 例如,使用 Visual Studio,在 Visual Studio 工作執行器瀏覽器中將現有的前置處理器編譯程序配置為 [建置之前] 工作。
許多協力廠商 NuGet 套件 (例如 AspNetCore.SassCompiler) 可以在 CSS 隔離發生之前,先在建置程序開始時編譯 SASS/SCSS 檔案,而且不需要額外的設定。
CSS 隔離設定
CSS 隔離允許某些進階案例的設定,例如當相依於現有工具或工作流程時。
自訂範圍識別碼格式
在本節中,{Pages|Views} 預留位置為 Pages (適用於 Razor Pages 應用程式) 或為 Views (適用於 MVC 應用程式)。
根據預設,範圍識別碼會使用 b-{STRING} 格式,其中 {STRING} 預留位置是架構所產生的十個字元字串。 若要自訂範圍識別碼格式,請將專案檔更新為所需的模式:
<ItemGroup>
<None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
在上述範例中,針對 Index.cshtml.css 產生的 CSS 會將其範圍識別碼從 b-{STRING} 變更為 custom-scope-identifier。
使用範疇識別符號來實現具範疇的 CSS 檔案的繼承。 在下列專案檔範例中,BaseView.cshtml.css 檔案包含跨檢視的通用樣式。
DerivedView.cshtml.css 檔案會繼承這些樣式。
<ItemGroup>
<None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
<None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
使用萬用字元 (*) 運算子,跨多個檔案共用範圍識別碼:
<ItemGroup>
<None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
變更靜態 Web 資產的基底路徑
會在應用程式的根目錄產生限定範圍 CSS 檔案。 在專案檔中,使用 StaticWebAssetBasePath 屬性來變更預設路徑。 下列範例會將限定範圍的 CSS 檔案和應用程式的其餘資產放在 _content 路徑:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
停用自動組合
若要退出架構在執行階段發佈及載入限定範圍檔案的方式,請使用 DisableScopedCssBundling 屬性。 使用此屬性時,其他工具或程序會負責從 obj 目錄取得隔離的 CSS 檔案,並在執行階段將其發佈及載入:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Razor 類別庫 (RCL) 支援
當 Razor 類別庫 (RCL) 提供隔離樣式時,<link> 標籤的 href 屬性會指向 {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css,其中預留位置為:
-
{STATIC WEB ASSET BASE PATH}:靜態 Web 資產基底路徑。 -
{PACKAGE ID}:程式庫的套件識別碼。 如果未在專案檔中指定封裝識別碼,則封裝識別碼預設為專案的組件名稱。
在以下範例中:
- 靜態 Web 資產基底路徑為
_content/ClassLib。 - 類別庫的組件名稱為
ClassLib。
Pages/Shared/_Layout.cshtml (Razor Pages) 或 Views/Shared/_Layout.cshtml (MVC):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
如需有關 RCL 的詳細資訊,請參閱下列文件:
如需 Blazor CSS 隔離的相關資訊,請參閱 ASP.NET Core Blazor CSS 隔離。