页面、路由和布局

小窍门

此内容摘自电子书《面向 Azure ASP.NET Web Forms 开发人员的 Blazor》,可在 .NET 文档上获取,也可作为免费可下载的 PDF 脱机阅读。

《面向 ASP NET Web Forms 开发人员的 Blazor》电子书封面缩略图。

ASP.NET Web 窗体应用由 .aspx 文件中定义的页面组成。 每个页面的地址都基于项目中的物理文件路径。 当浏览器向页面发出请求时,页面的内容将在服务器上动态呈现。 渲染同时考虑了页面的 HTML 标记及其服务器控件。

在 Blazor应用中,每个页面都是一个组件,通常在 .razor 文件中定义,其中包含一个或多个指定的路由。 路由主要发生在客户端,而无需涉及特定的服务器请求。 浏览器首先向应用的根地址发出请求。 然后,应用中的Router根Blazor组件处理截获导航请求并将其转发到正确的组件。

Blazor 还支持 深层链接。 当浏览器向应用根目录以外的特定路由发出请求时,将发生深层链接。 发送到服务器的深层链接请求将路由到 Blazor 应用,然后将请求客户端路由到正确的组件。

ASP.NET Web 窗体中的简单页面可能包含以下标记:

Name.aspx

<%@ Page Title="Name" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Name.aspx.cs" Inherits="WebApplication1.Name" %>

<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
    <div>
        What is your name?<br />
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        <asp:Button ID="Button1" runat="server" Text="Submit" OnClick="Button1_Click" />
    </div>
    <div>
        <asp:Literal ID="Literal1" runat="server" />
    </div>
</asp:Content>

Name.aspx.cs

public partial class Name : System.Web.UI.Page
{
    protected void Button1_Click1(object sender, EventArgs e)
    {
        Literal1.Text = "Hello " + TextBox1.Text;
    }
}

在 Blazor 应用中,等效页面如下所示:

Name.razor

@page "/Name"
@layout MainLayout

<div>
    What is your name?<br />
    <input @bind="text" />
    <button @onclick="OnClick">Submit</button>
</div>
<div>
    @if (name != null)
    {
        @:Hello @name
    }
</div>

@code {
    string text;
    string name;

    void OnClick() {
        name = text;
    }
}

创建页面

若要在其中 Blazor创建页面,请创建组件并添加 @page Razor 指令以指定组件的路由。 该 @page 指令采用单个参数,即要添加到该组件的路由模板。

@page "/counter"

路由模板参数是必需的。 与 ASP.NET Web 窗体不同,Blazor从其文件位置推断到组件的路由(尽管这可能是将来添加的功能)。

路由模板语法是用于在 ASP.NET Web 窗体中路由的相同基本语法。 使用大括号在模板中指定路由参数。 Blazor 将路由值绑定到具有相同名称的组件参数(不区分大小写)。

@page "/product/{id}"

<h1>Product @Id</h1>

@code {
    [Parameter]
    public string Id { get; set; }
}

还可以指定路由参数值的约束。 例如,若要将产品 ID 限制为:int

@page "/product/{id:int}"

<h1>Product @Id</h1>

@code {
    [Parameter]
    public int Id { get; set; }
}

有关支持的Blazor路由约束的完整列表,请参阅路由约束

路由器组件

在 Blazor 中的路由由 Router 组件处理。 该 Router 组件通常用于应用的根组件(App.razor)。

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

Router组件在指定的AppAssembly和可选地指定的AdditionalAssemblies中发现可路由组件。 浏览器导航时,Router会截获导航并使用提取的Found呈现其RouteData参数内容;如果有路由与地址匹配,则呈现该内容,否则Router会呈现其NotFound参数内容。

RouteView 组件负责呈现由 RouteData 指定的匹配组件及其布局(如果有)。 如果匹配的组件没有布局,则使用可选指定的 DefaultLayout 组件。

组件 LayoutView 在指定的布局中呈现其子内容。 本章稍后将更详细地介绍布局。

在 ASP.NET Web 窗体中,通过向浏览器返回重定向响应来触发到其他页面的导航。 例如:

protected void NavigateButton_Click(object sender, EventArgs e)
{
    Response.Redirect("Counter");
}

通常情况下,在Blazor中返回重定向响应是不可能的。 Blazor 不使用请求-回复模型。 但是,可以像使用 JavaScript 一样直接触发浏览器导航。

Blazor 提供的 NavigationManager 服务可用于:

  • 获取当前浏览器地址
  • 获取基址
  • 触发导航
  • 地址更改时收到通知

若要导航到其他地址,请使用 NavigateTo 以下方法:

@page "/"
@inject NavigationManager NavigationManager

<button @onclick="Navigate">Navigate</button>

@code {
    void Navigate() {
        NavigationManager.NavigateTo("counter");
    }
}

有关所有 NavigationManager 成员的说明,请参阅 URI 和导航状态帮助程序

基 URL

如果 Blazor 应用部署在基路径下,那么需要在页面元数据中使用 <base> 标记指定用于路由到工作属性的基 URL。 如果使用 Razor 呈现应用的主机页,则可以使用 ~/ 语法指定应用的基址。 如果主机页是静态 HTML,则需要显式指定基 URL。

<base href="~/" />

页面布局

ASP.NET Web 窗体中的页面布局由母版页处理。 母版页定义了一个模板,其中包含一个或多个内容占位符,供各个页面填充。 母版页在 .master 文件中定义,以指令开头 <%@ Master %>.master 文件的内容是像 .aspx 页面一样进行编码的,但添加了 <asp:ContentPlaceHolder> 控件以标记页面可以提供内容的位置。

Site.master

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="WebApplication1.SiteMaster" %>

<!DOCTYPE html>
<html lang="en">
<head runat="server">
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%: Page.Title %> - My ASP.NET Application</title>
    <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body>
    <form runat="server">
        <div class="container body-content">
            <asp:ContentPlaceHolder ID="MainContent" runat="server">
            </asp:ContentPlaceHolder>
            <hr />
            <footer>
                <p>&copy; <%: DateTime.Now.Year %> - My ASP.NET Application</p>
            </footer>
        </div>
    </form>
</body>
</html>

在 Blazor 中,您使用布局组件处理页面布局。 布局组件继承自 LayoutComponentBase,它定义一 Body 个类型的 RenderFragment属性,可用于呈现页面的内容。

MainLayout.razor

@inherits LayoutComponentBase
<h1>Main layout</h1>
<div>
    @Body
</div>

呈现具有布局的页面时,页面将呈现在指定布局的内容中,位于布局呈现其 Body 属性的位置。

若要将布局应用于页面,请使用 @layout 指令:

@layout MainLayout

可以使用 _Imports.razor 文件为文件夹和子文件夹中的所有组件指定布局。 还可以使用 路由器组件为所有页面指定默认布局。

母版页可以定义多个内容占位符,但是 Blazor 中的布局仅有单个 Body 属性。 布局组件的这一限制 Blazor 有望在未来版本中得到解决。

在 ASP.NET Web Forms 中,母版页可以嵌套。 即母版页也可能使用母版页。 Blazor 中的布局组件也可能是嵌套的。 可以将一个布局组件应用于另一个布局组件。 内部布局的内容将在外部布局内呈现。

ChildLayout.razor

@layout MainLayout
<h2>Child layout</h2>
<div>
    @Body
</div>

Index.razor

@page "/"
@layout ChildLayout
<p>I'm in a nested layout!</p>

然后,页面的呈现输出将为:

<h1>Main layout</h1>
<div>
    <h2>Child layout</h2>
    <div>
        <p>I'm in a nested layout!</p>
    </div>
</div>

中的 Blazor 布局通常不定义页面(<html><body><head>等)的根 HTML 元素。 根 HTML 元素是在应用的主机页中 Blazor 定义的,用于呈现应用的初始 HTML 内容(请参阅 Bootstrap Blazor)。 主机页面可以渲染应用的多个根组件,并添加周围的标记。

Blazor中的组件,包括页面,无法呈现 <script> 标记。 存在此呈现限制,因为 <script> 标记加载一次,然后无法更改。 如果尝试使用 Razor 语法动态呈现标记,则可能会发生意外行为。 相反,所有 <script> 标记都应添加到应用的主机页。