我们将遵循现代 Web 开发的最佳实践,主要介绍 ASP.NET Core 中的布局方式,因为它是目前的主流和未来方向,我也会简要提及传统的 ASP.NET Web Forms 的布局方式作为对比。

asp.net 网站布局教程
(图片来源网络,侵删)

目录

  1. 为什么需要布局?
  2. 核心概念: Razor Pages, Views, 和 Layouts
  3. 实战教程:创建你的第一个布局
    • 步骤 1:创建 Layout 文件
    • 步骤 2:定义内容占位符 @RenderBody()
    • 步骤 3:创建一个 View 并应用布局
    • 步骤 4:运行并查看效果
  4. 进阶布局技巧
    • Section(节):实现布局中的可变部分(如页脚脚本、侧边栏)
    • Partial Views(局部视图):复用 UI 组件(如导航栏、卡片)
    • Tag Helpers:简化 HTML 标签的编写
    • 组件化与 Blazor:未来的 UI 构建方式
  5. 传统 ASP.NET Web Forms 布局(Master Pages)
  6. 最佳实践与总结

为什么需要布局?

想象一下,如果你的网站有 100 个页面,每个页面都包含相同的头部(Logo、导航菜单)和底部(版权信息、联系方式),当你需要修改导航菜单时,难道要打开并修改 100 个文件吗?

布局 正是为了解决这个问题而生,它定义了一个网站的“外壳”或“模板”,所有页面内容都填充在这个外壳中,这样做的好处显而易见:

  • 代码复用:将公共部分(Header, Footer, Navigation)提取到布局文件中,避免重复编写。
  • 易于维护:只需修改布局文件,即可更新整个网站的外观和结构。
  • 一致性:确保所有页面拥有统一的结构和风格。

核心概念:Razor Pages, Views, 和 Layouts

在 ASP.NET Core MVC 和 Razor Pages 模型中,布局的实现依赖于三个核心概念:

  • Layout (布局文件):一个包含 HTML 结构的模板文件,通常位于 /Views/Shared/_Layout.cshtml/Pages/Shared/_Layout.cshtml,文件名 _ 开头是一种约定,表示它是一个“部分”或“辅助”文件,不应被直接请求。
  • View (视图):显示特定数据的 HTML 模板,一个关于页面的视图文件可能是 /Views/Home/About.cshtml
  • @RenderBody():这是布局文件中最重要的一个指令,它是一个占位符,当用户请求一个页面时,ASP.NET Core 会将对应视图的 HTML 内容“渲染”并插入到 @RenderBody() 所在的位置。

工作流程图解:

asp.net 网站布局教程
(图片来源网络,侵删)
[用户请求 /Home/About]
      |
      V
[控制器找到 About.cshtml 视图]
      |
      V
[系统查找 _Layout.cshtml 布局]
      |
      V
[将 About.cshtml 的内容填充到 @RenderBody() 的位置]
      |
      V
[生成最终的完整 HTML 页面,返回给用户]

实战教程:创建你的第一个布局

我们将使用 Visual Studio 和 ASP.NET Core Razor Pages 项目模板来完成这个教程。

步骤 1:创建 Layout 文件

  1. 在 Visual Studio 中创建一个新的 ASP.NET Core Web 应用程序。
  2. 选择 “ASP.NET Core Web App” 模板,确保选择的是 Razor Pages
  3. 在解决方案资源管理器中,右键点击 Pages 文件夹 -> 添加 -> 新建文件夹,命名为 Shared
  4. 右键点击刚刚创建的 Shared 文件夹 -> 添加 -> 新建项
  5. 选择 “Razor Layout”,命名为 _Layout.cshtml,然后点击“添加”。

步骤 2:定义内容占位符 @RenderBody()

打开 _Layout.cshtml 文件,你会看到一个基础的 HTML5 模板,在 <body> 标签内,找到 @RenderBody() 指令,这就是我们内容将要插入的地方。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />@ViewData["Title"] - MyWebApp</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-dark bg-dark border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-page="/Index">MyWebApp</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-light" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-light" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            <!-- 
              这是核心!所有页面的内容都会被渲染到这里。
              比如你访问 /Privacy.cshtml,Privacy.cshtml 的内容就会出现在这里。
            -->
            @RenderBody() 
        </main>
    </div>
    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2025 - MyWebApp
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    <!-- 在 RenderBody 之后渲染的脚本 -->
    @RenderSection("Scripts", required: false)
</body>
</html>

关键点:

  • @ViewData["Title"]:我们可以在每个页面中设置 ViewData["Title"] 的值,来动态改变页面标题。
  • @RenderBody()的核心插入点。
  • @RenderSection("Scripts", required: false):这是一个可选的节,用于在布局的底部插入特定页面所需的脚本。required: false 表示不是所有页面都需要提供这个节。

步骤 3:创建一个 View 并应用布局

默认情况下,新项目会自动为你创建 Index.cshtmlPrivacy.cshtml,并且它们已经配置好了使用 _Layout.cshtml,我们来看看 Privacy.cshtml 是如何配置的。

asp.net 网站布局教程
(图片来源网络,侵删)

打开 Pages/Privacy.cshtml

@page
@model PrivacyModel
@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
  • @page:指令,表明这是一个 Razor Page,可以直接通过 URL 访问。
  • @model PrivacyModel:指定此页面使用的 C# 模型。
  • 代码块,我们在这里设置了 ViewData["Title"]

注意:你不需要在 View 中显式指定使用哪个 Layout,ASP.NET Core 会按照约定自动查找 /Pages/Shared/_Layout.cshtml/Views/Shared/_Layout.cshtml,如果你想使用不同的布局,可以在 View 的代码块中设置 Layout = "_AnotherLayout";

步骤 4:运行并查看效果

F5 运行你的应用程序。

  1. 访问首页 (),你会看到 _Layout.cshtml 中的所有元素(Header, Footer),@RenderBody() 的位置被替换成了 Index.cshtml 的内容。
  2. 访问隐私页 (/Privacy),你会看到 Header 和 Footer 保持不变,但 @RenderBody() 的内容变成了 Privacy.cshtml 的内容,并且页面标题也变成了 "Privacy Policy"。

进阶布局技巧

Section(节)

假设你希望在某个特定页面(比如一个包含复杂图表的页面)的底部加载一个额外的 JavaScript 文件,但你不想在每个页面都加载它,这时就可以使用 Section

  1. _Layout.cshtml 中定义一个节: 我们已经看到了 @RenderSection("Scripts", required: false),这定义了一个名为 "Scripts" 的节,并且它是可选的。

  2. 在 View 中为这个节提供内容: 修改 Privacy.cshtml,在文件末尾添加以下代码:

    @* ... 其他代码 ... *@
    @section Scripts {
        <script>
            console.log("This is a script specific to the Privacy page!");
        </script>
    }
  3. 运行效果: 当你访问 /Privacy 时,这个 <script> 标签会被插入到 _Layout.cshtml@RenderSection("Scripts") 的位置,而当你访问首页时,这个脚本不会被加载。

Partial Views(局部视图)

局部视图是可重用的 UI 片段,你的网站可能在多个地方都需要显示一个“热门文章列表”。

  1. 创建局部视图: 在 Shared 文件夹下,添加一个新项,选择 “Razor View”,命名为 _ArticleList.cshtml。 在这个文件中,编写你的列表 HTML 代码:

    @* _ArticleList.cshtml *@
    <h3>热门文章</h3>
    <ul>
        <li><a href="#">如何学习 ASP.NET Core</a></li>
        <li><a href="#">Razor 语法详解</a></li>
        <li><a href="#">Entity Core 使用指南</a></li>
    </ul>
  2. 在布局或其他视图中使用局部视图: 使用 @await Html.PartialAsync("_ArticleList")@await Component.InvokeAsync(...) 来渲染它。

    _Layout.cshtml<main> 标签内添加:

    <main role="main" class="pb-3">
        <div class="row">
            <div class="col-md-9">
                @RenderBody()
            </div>
            <div class="col-md-3">
                <h2>侧边栏</h2>
                @* 调用局部视图 *@
                @await Html.PartialAsync("_ArticleList")
            </div>
        </div>
    </main>

你的布局就多了一个显示热门文章的侧边栏。

Tag Helpers

Tag Helpers 是一种让服务器端代码参与生成 HTML 的语法,它让 HTML 更加清晰和易于阅读。

  • <a asp-page="/About">:比 <a href="/About"> 更好,因为它会自动处理 URL 路径,即使你以后更改了路由配置,它也能正常工作。
  • <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />asp-append-version="true" 会在 URL 后面添加一个查询字符串,如 ?v=xxxxxxxx,这对于缓存非常有益,当文件内容改变时,这个版本号也会改变,从而强制浏览器重新下载文件。

传统 ASP.NET Web Forms 布局(Master Pages)

如果你还在维护旧的 ASP.NET Web Forms 项目,布局是通过 Master Pages 实现的,其概念与 Layouts 非常相似。

  • Master Page 文件:扩展名是 .master (Site.master),它和 _Layout.cshtml 结构一样,也包含一个内容占位符。
  • 内容占位符:Web Forms 中使用 <asp:ContentPlaceHolder> 标签。
    <!-- Site.master -->
    <body>
        <form id="form1" runat="server">
            <div>
                <asp:ContentPlaceHolder ID="MainContent" runat="server">
                </asp:ContentPlaceHolder>
            </div>
        </form>
    </body>
  • Content Page (内容页):扩展名是 .aspx,它通过 <asp:Content> 标签来提供内容,并通过 ContentPlaceID 属性指定要填充到哪个占位符。
    <!-- About.aspx -->
    <asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
        <h2>关于我们</h2>
        <p>这里是关于我们的内容。</p>
    </asp:Content>

虽然语法不同,但核心思想是完全一样的:模板 + 内容占位符。


最佳实践与总结

  1. 遵循约定:将布局文件放在 /Views/Shared//Pages/Shared/ 目录下,并命名为 _Layout.cshtml,这是 ASP.NET Core 的默认约定,能简化配置。
  2. 保持布局简洁:布局文件应该只包含网站的公共结构(Header, Footer, Navigation),不要在布局中放置复杂的业务逻辑。
  3. 合理使用 Sections:将页面特定的脚本、样式或 HTML 块放在 Sections 中,以保持布局的通用性。
  4. 拥抱组件化:对于可重用的 UI 片段(如导航栏、卡片、用户信息框),优先使用 Partial ViewsView Components,而不是在多个地方复制粘贴代码。
  5. 从 Layout 开始:在开发任何新页面之前,先设计并实现好你的 _Layout.cshtml,这能确保整个网站的一致性。
  6. 展望未来:对于全新的项目,可以关注 Blazor,Blazor 是一个使用 C# 而不是 JavaScript 来构建交互式 Web UI 的框架,它有自己的组件模型,你可以在一个 .razor 文件中创建可复用的 UI 组件,这代表了 Web UI 开发的未来方向,其组件化思想比传统的布局更进一步。

希望这份详细的教程能帮助你掌握 ASP.NET 网站布局!从基础布局到高级技巧,逐步实践,你很快就能构建出结构清晰、易于维护的专业网站。