标签助手简介
在本课程中,你将学习 Razor 如何通过新增标签以及为现有 HTML 标签添加新属性来增强 HTML。
Razor 通过新增标签和属性丰富了 HTML。借助名为 标签助手(tag helpers) 的功能,我们可以添加自定义标签和属性。
为了理解标签助手的工作原理以及如何利用它,我们需要了解 Razor 如何解析代码中的标签:
- 它解析开始标签,并将标签名称及标签中包含的所有属性-值对存储在一个数据结构中。在计算所有值时,可能会执行用于提供动态值的 C# 表达式。
- 它递归地解析标签内容,直到找到对应的闭合标签,从而构建一棵树,其中每个节点包含一个带有属性-值对的标签,或者如果节点代表的是文本内容,则为纯文本。
- 它递归地将标签内容树转换为 HTML 字符串,然后渲染开始标签、其属性、内容,最后是闭合标签。
在第 2 步之后,我们可以添加钩子来处理节点的数据结构,可能在 Razor 进入第 3 步之前修改标签名称或某些属性。
例如,假设 Razor 正在解析下面这个自定义的 a 标签:
1<a asp-controller="Products" asp-action="Index">Show products</a>
该 a 标签接收控制器和操作方法,而不是 URL。我们如何将其转换为带有正确 href 属性的合法 HTML a 标签?
只需在 Razor 解析算法的第 2 步中,为所有包含 asp-action 属性的 a 标签添加钩子,并执行以下操作:
- 获取
a节点中asp-controller和asp-action属性的值,并用它们计算出对应控制器和操作方法的 URL。如果未提供asp-controller,可以使用调用视图的控制器名称。 - 将
asp-controller和asp-action属性替换为包含上一步计算出的 URL 的普通href属性。
完成上述钩子后,Razor 解析算法进入第 3 步,将修改后的标签节点渲染为:
1<a href="<computed url>">Show products</a>
就这样!
使用标签助手定义解析钩子
标签助手是继承自框架 TagHelper 抽象类的 C# 类,该类定义了 Razor 解析算法中的各个钩子。每个标签助手都指定一个钩子条件以及如何修改原始节点。例如,前文描述的钩子可以通过一个标签助手实现,其钩子条件为:标签名称必须是 a,且标签必须包含非空的 asp-action 属性。
由于标签助手在不改变节点结构的前提下修改现有节点,因此多个标签助手可以链式作用于同一个节点。为此,标签助手还指定了优先级,用于决定它们的执行顺序。
标签助手还可以使标签支持非字符串类型的属性。例如,我们可以定义一个 display-customer 标签,其 asp-instance 属性接受一个客户实例作为值:
1<display-customer asp-instance="myCustomer">Show products</display-customer>
非字符串值像普通 HTML 字符串属性一样用引号包围。这不会引起解析器混淆,因为所有属性的类型都在标签助手定义中明确声明。
在 Razor 解析某个标签时,所有满足钩子条件的可用标签助手会根据其优先级排序,并依次应用。
我们将在专门的课程中讲解如何编写标签助手。本课程剩余部分介绍如何让某些标签助手在 Razor 文件中可用。
为页面和视图添加标签助手
可以通过 @addTagHelper 指令将标签助手添加到页面和视图中:
1@model MyViewModel 2@using MyNamespace 3... 4@addTagHelper {标签助手类型的完整名称}, {标签助手程序集名称}
@addTagHelper 需要标签助手的完整类型名称及其所在的程序集名称。该程序集必须被包含 @addTagHelper 指令的 Razor 文件所在项目引用。
虽然程序集名称始终是必需的,但我们可以对整个标签助手类型名称或其部分使用通配符。
例如,考虑以下指令:
1@addTagHelper *, MyTagHelpersAssembly
它添加了 MyTagHelpersAssembly 程序集中所有的标签助手。
再看以下指令:
1@addTagHelper MyNameSpace.*, MyTagHelpersAssembly
它添加了 MyTagHelpersAssembly 程序集中命名空间为 MyNameSpace 的所有标签助手。
将标签助手添加到 _ViewImports.cshtml
@addTagHelper 指令也可以添加到位于 Views 和 Pages 文件夹中的 _ViewImports.cshtml 文件中,此时它适用于所有视图和页面。以下是创建 ASP.NET Core MVC 项目时自动生成的默认 _ViewImports.cshtml 文件内容:
1@using MvcProject 2@using MvcProject.Models 3@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
它自动添加了 Microsoft.AspNetCore.Mvc.TagHelpers 程序集中所有的标签助手,这些是 ASP.NET Core 内置的默认标签助手。我们将在下一课中分析大部分内容,其他内容将在后续课程中介绍。
由于嵌套在 Views 和 Pages 文件夹中的子文件夹也可以拥有自己的 _ViewImports.cshtml 文件,因此我们可以为不同文件夹下的所有 Razor 视图和页面添加不同的标签助手。
此外,在嵌套文件夹的 _ViewImports.cshtml 文件中,还可以使用 @removeTagHelper 指令移除之前文件夹的 _ViewImports.cshtml 中添加的部分标签助手。
例如,假设 Views 文件夹中的 _ViewImports.cshtml 包含以下指令:
1@addTagHelper *, MyTagHelpersAssembly
我们可以在 Views 的子文件夹(如 Products)中添加一个 _ViewImports.cshtml 文件,并加入以下指令:
1@removeTagHelper MyNameSpace.*, MyTagHelpersAssembly
上述指令将移除 MyTagHelpersAssembly 中命名空间为 MyNameSpace 的所有标签助手。
选择何时触发标签助手钩子
我们还可以决定在每个特定标签调用时是否查找标签助手钩子。为此,可以使用 @tagHelperPrefix 指令声明一个标签助手前缀:
1@tagHelperPrefix th:
在此声明之后,只有以 th: 为前缀的标签才会启用标签助手钩子。因此,在此声明后,以下标签将不再被转换为普通的 a 标签:
1<a asp-controller="Products" asp-action="Index">Show products</a>
通过添加 th: 前缀,可以重新启用标签助手钩子:
1<th:a asp-controller="Products" asp-action="Index">Show products</th:a>
即使没有使用 @tagHelperPrefix 指令,也可以通过在标签名称前加上 ! 来禁用特定标签实例的标签助手钩子:
1<!a asp-controller="Products" asp-action="Index">Show products</!a>
上述标记将阻止 asp-controller 和 asp-action 属性被替换为相应的 href 属性,因此在浏览器的 HTML 源码中,我们将看到未转换的原始标记:
1<a asp-controller="Products" asp-action="Index">Show products</a>