ASP.NET Core MVC中視圖的知識和ASP.NET MVC有很多相似之處,學習難度較低。以下內容主要體現了編程中模塊化的思想,模塊化才應是我們關注的重點。
布局用於提供各個頁面所需的公共部分,如:菜單、頁頭、頁尾等。在ASP.NET Core中默認的布局文件是位於/Views/Shared文件夾下的_Layout.cshtml
文件:
我們通常在_Layout.cshtml
中引入公共資源,如:
<link href="~/css/reset.css" rel="stylesheet" /> <link href="~/css/index.css" rel="stylesheet" /> <script src="~/js/common/net/ajaxHandler.js"></script> <environment names="Development"> <script src="~/js/lib/vue/vue.js"></script> </environment> <environment names="Production"> <script src="~/js/lib/vue/vue.min.js"></script> </environment>
指定布局文件
可以在Razor視圖(即,cshtml文件)中使用Layout
屬性來指定使用哪個布局文件:
@{ Layout="_Layout"; }
ASP.NET Core MVC搜索布局文件的方式與局部視圖一樣,下文中會詳細說明。默認情況下,在布局文件中必須調用RenderBody
方法。還可以使用RenderSection方法來引入section
。
View Import & View Start
View Import
可以在_ViewImport.cshtm
文件中添加命名空間或者Tag Helper以供其它視圖中使用,如:
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
在_ViewImport.cshtm
文件可以使用以下指令:
-
@addTagHelper
-
@removeTagHelper
-
@tagHelperPrefix
-
@using
-
@model
-
@inherits
-
@inject
_ViewImport.cshtm
文件不支持Razor文件的其它特性,如:function、section等。對於多個_ViewImports.cshtml
的情況,指令運行結果遵循如下規則:
-
@addTagHelper
,@removeTagHelper
: 按照先后順序執行 -
@tagHelperPrefix
: 后執行的結果覆蓋先執行的結果 -
@model
: 后執行的結果覆蓋先執行的結果 -
@inherits
: 后執行的結果覆蓋先執行的結果 -
@using
: 引入所指定的所有命名空間,但忽略重復引用 -
@inject
: 后注入覆蓋先注入的同名屬性
View Start
_ViewStart.cshtml
文件中的代碼會在所有完整視圖(full view,not layout,not partial view)文件被渲染之前執行。
默認情況下,ViewImports.cshtml
和ViewStart.cshtml
文件直接位於Views文件夾下:
-
相比其它位置的其它位置
ViewImports.cshtml
和ViewStart.cshtml
,直接位於Views文件夾中的ViewImports.cshtml
和ViewStart.cshtml
文件會優先執行 -
后執行的
ViewImports.cshtml
文件中的內容有可能會覆蓋先執行ViewImports.cshtml
文件中的內容 -
ViewImports.cshtml
和ViewStart.cshtml
文件的作用域是當前目錄及子目錄
Tag Helper
Tag Helper可以讓服務器端代碼參與到在Razor文件中創建和渲染HTML元素的工作中。
自定義Tag Helper:
public class XfhTagHelper : TagHelper { public string Content { set; get; } public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { output.TagName = "a"; output.Attributes.Add("href", "https://www.google.com"); output.Content.SetContent(Content); } }
使用Tag Helper:
@addTagHelper *,Web <xfh content="haha">customer tag helper</xfh>
Tag Helper與HTML Helper有些相似,二者不同之處可參考:Tag Helpers compared to HTML Helpers
Tag Helper具有以下優點:
-
類HTML語法
這一點是我喜歡Tag Helper的原因,使用Tag Helper可以使cshtml文件中后台代碼與前端的搭配更和諧,利於提升代碼可讀性
-
語法智能感知
Tag Helper 作用域
-
@addTagHelper
@addTagHelper
的第一個參數表示要加載哪些Tag Helper,使用"*"表示加載所有的Tag Helper;第二個參數表示從哪個程序集中加載Tag Helper。示例如下:@* 需指明TagHelper的完全限定名 *@ @addTagHelper Web.TagHelpers.XfhTagHelper,Web
-
@removeTagHelper
@removeTagHelper
也有兩個參數,含義同@addTagHelper
-
@tagHelperPrefix
給Tag Helper加上前綴標識,示例如下:
@addTagHelper *,Web @tagHelperPrefix th: @* 不會被當作Tag Helper處理 *@ <xfh content="haha">customer tag helper</xfh> <th:xfh content="tagHelperPrefix"></th:xfh>
Partial View
Partial view,局部視圖是一個Razor文件,它通常嵌套在另一個Razor文件中。局部視圖主要用於拆分大的Razor文件及代碼復用。但請注意:
-
局部視圖不應用來維護公共布局中的內容,考慮使用_Layout.cshtml來維護公共布局
Partial views shouldn't be used to maintain common layout elements.
-
盡量不要在局部視圖中使用復雜的渲染邏輯,或者需要執行一些代碼片段才能獲取到視圖元素。這種情況考慮使用view component來替代局部視圖。
Don't use a partial view where complex rendering logic or code execution is required to render the markup.If you need to execute code, use a view component instead of a partial view.
聲明局部視圖
局部視圖名通常以下划線_
開頭,下划線主要用於易於辨識局部視圖文件。注意一點,在渲染局部視圖時,不會執行_ViewStart.cshtml
文件中的代碼。其余與普通視圖一樣。
⚠️局部視圖中定義的
section
只對當前局部視圖可見
使用局部視圖
引用局部視圖文件而不帶擴展名cshtml
時,在MVC框架中,會從以下路徑中加載局部視圖文件,優先級從上而下依次降低:
-
/Areas/<Area-Name>/Views/<Controller-Name>
-
/Areas/<Area-Name>/Views/Shared
-
/Views/Shared
-
/Pages/Shared
當引用局部文件帶上擴展名時,局部視圖文件必須和引用方位於相同目錄下。
可使用以下方式引入局部視圖:
<partial name="partial.cshtml" model="Model"/> @* 局部視圖文件后綴可以省略,如: *@ <partial name="partial" /> @* 也可以使用局部視圖文件全名,如: *@ <partial name="~/Views/Shared/_PartialName.cshtml" />
-
HTML Helper
@await Html.PartialAsync("_PartialName")
也可以使用 RenderPartialAsync方法來渲染局部視圖,該方法直接將渲染結果寫入到response中,而不是返回 IHtmlContent,所以只能在Razor代碼塊中調用該方法:
@{ await Html.RenderPartialAsync("_PartialName"); }
相比於PartialAsync
,RenderParatialAsync
有着更好的性能。
View Component
View component,視圖組件和局部視圖類似,但它更強大。一個視圖組件包含兩部分:
ViewComponent
類和一個視圖。
視圖組件不使用模型綁定,視圖組件中所用的數據有調用者提供。視圖組件有以下特點:
-
渲染數據塊而非整個響應
-
關注點分離、易於測試
-
可以有參數和業務邏輯
MVC本身就提倡關注點分離,所以,視圖組件中應盡可能只包含與渲染視圖相關的邏輯
-
通常在層中調用
自定義視圖組件
創建視圖組件類:
-
視圖組件繼承自
ViewComponent
或使用ViewComponentAttribute
特性 -
自定義類約定以
ViewComponent
結尾(非強制)
public class FirstViewComponent : ViewComponent { // 方法名InvokeAsync是基於約定的,同步方法名為Invoke public async Task<IViewComponentResult> InvokeAsync(string descript) { return View<string>(descript); } }
⚠️ 視圖組件類中可以使用依賴注入。需注意:視圖組件不會參與到Controller的生命周期中,所以filter對它無效。
創建視圖文件:
視圖組件默認視圖名為:Default
,簡單定義視圖內容如下:
<label> @Model </label>
在運行時按照以下順序搜索視圖文件:
-
/Views/{Controller Name}/Components/{View Component Name}/{View Name}
-
/Views/Shared/Components/{View Component Name}/{View Name}
-
/Pages/Shared/Components/{View Component Name}/{View Name}
🆗 推薦使用Default作為視圖組件的視圖文件名,且視圖文件存放路徑為:Views/Shared/Components/{View Component Name}/{View Name}
可以使用如下兩種方式來調用視圖組件:
-
Component.InvokeAsync
-
Tag Helper
@addTagHelper *,Web <div class="text-center"> @await Component.InvokeAsync("First", new { descript = "invoking view component" }) <br /> @* Tag Helper方式調用ViewComponent,需以vc:作為前綴 *@ <vc:first descript="tag helper"> </vc:first> </div>
⚠️ 注意,使用Tag Helper形式調用視圖組件時,組件名和組件的方法參數使用 kebab case方式,即,組件PriorityList
有參數maxPriority
,則調用方式如下:
<vc:priority-list max-priority="2"> </vc:priority-list>
除此之外,還可以在Controller中調用視圖組件:
public IActionResult InvokeVC() { // 注意,視圖組件名稱大小寫敏感 return ViewComponent("First", new { Descript = "controller"}); }
View component methods
抄錄一段微軟官網上對於View component methods的總結,人太懶,就不翻譯了😂,留意加粗部分:
A view component defines its logic in an InvokeAsync
method that returns a Task<IViewComponentResult>
or in a synchronous Invoke
method that returns an IViewComponentResult
. Parameters come directly from invocation of the view component, not from model binding. A view component never directly handles a request. Typically, a view component initializes a model and passes it to a view by calling the View
method. In summary, view component methods:
-
Define an
InvokeAsync
method that returns aTask<IViewComponentResult>
or a synchronousInvoke
method that returns anIViewComponentResult
. -
Typically initializes a model and passes it to a view by calling the
ViewComponent
View
method. -
Parameters come from the calling method, not HTTP. There's no model binding.
-
Are not reachable directly as an HTTP endpoint. They're invoked from your code (usually in a view). A view component never handles a request.
-
Are overloaded on the signature rather than any details from the current HTTP request.
小結
本文主要對ASP.NET Core中的視圖部分做了簡要概述,相比於文中的各種概念,我們應該把注意力放到模塊化設計上。模塊化、抽象思維是程序員應該掌握的兩種能力。他們可以讓我們寫出高內聚、低耦合的代碼。
推薦閱讀
View components in ASP.NET Core