下面我将详细解释这个概念,并提供一个基于 ASP.NET MVC 的具体实现示例,因为这个场景是该框架最常见的应用之一。

mvc 模板页和内容页局部刷新
(图片来源网络,侵删)

核心概念:模板页与内容页

我们明确一下这两个概念在 MVC 中的角色:

  • 模板页:通常指网站的布局页,_Layout.cshtml,它定义了网站的整体结构,如页头、导航栏、页脚、侧边栏等,它包含了一些“占位符”(@RenderBody(), @RenderSection()),用来动态插入具体页面的内容。
  • 内容页:指具体的 Action 对应的 View,Home/Index.cshtml,这个页面本身不包含完整的 HTML 结构(如 <html>, <head>, <body>),它只包含需要显示在模板页 @RenderBody() 占位符中的核心内容。

传统全页面刷新流程: 浏览器请求 /Home/Index -> 服务器返回完整的 HTML(包含 _LayoutIndex 的内容) -> 浏览器重新渲染整个页面。

局部刷新目标: 我们希望只更新 @RenderBody() 对应的部分,而页头、导航栏、页脚等保持不变,从而提供更流畅、更快的用户体验。


实现局部刷新的技术方案

实现局部刷新主要有两种主流技术:

mvc 模板页和内容页局部刷新
(图片来源网络,侵删)

jQuery + AJAX (经典且易于理解)

这是最传统、最广泛使用的方法,其核心流程是:

  1. 初始加载:第一次访问页面时,服务器仍然返回完整的 HTML 页面(包含 _Layout),让用户看到整个网站。
  2. 触发局部刷新:用户执行某个操作(如点击链接、提交表单、选择下拉框等)。
  3. AJAX 请求:使用 JavaScript (如 jQuery 的 $.ajax$.get) 向服务器发送一个异步请求,这个请求通常指向一个特定的 Action。
  4. 服务器返回:服务器接收到请求后,只生成并返回内容页的 HTML 片段,而不是完整的布局页。
  5. 前端渲染:JavaScript 接收到这个 HTML 片段后,用它替换掉页面中 <div id="main-content"> (或其他容器) 的 innerHTML

关键点:如何让服务器只返回内容页的 HTML 而不带布局?答案是 Partial View (部分视图)

ASP.NET MVC AJAX Helper (更“MVC”的方式)

ASP.NET MVC 提供了一套内置的 AJAX Helper,可以简化方案一的很多手动操作,它基于 Microsoft AJAX Library 和 jQuery。

  • Ajax.ActionLink:生成一个可以发起 AJAX 请求的链接。
  • Ajax.BeginForm:生成一个可以提交 AJAX 请求的表单。
  • AjaxOptions:配置 AJAX 请求的各种选项,如更新目标、加载提示、成功回调等。

这种方式本质上还是 AJAX,但将大量的 JavaScript 代码封装到了 Razor 语法中,使代码更整洁。

mvc 模板页和内容页局部刷新
(图片来源网络,侵删)

具体实现示例 (ASP.NET MVC + jQuery)

假设我们有一个简单的博客首页,顶部有导航栏,中间是文章列表(内容页),底部是页脚,我们希望点击“分类”链接时,只刷新文章列表区域。

步骤 1:修改布局页 (_Layout.cshtml)

@RenderBody() 的外部包裹一个 div,并给它一个唯一的 id,这个 div 将成为我们局部刷新的目标容器。

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <!-- 普通链接会导致全页面刷新 -->
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <!-- AJAX 链接,点击后只刷新内容区 -->
                    <li>@Html.ActionLink("技术", "PostsByCategory", "Home", new { category = "tech" }, new { @class = "ajax-link" })</li>
                    <li>@Html.ActionLink("生活", "PostsByCategory", "Home", new { category = "life" }, new { @class = "ajax-link" })</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        <!-- 这个 div 是局部刷新的目标区域 -->
        <div id="main-content">
            @RenderBody()
        </div>
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    <!-- 1. 引入 jQuery Unobtrusive AJAX 库 -->
    <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
    <!-- 2. 编写自定义 JavaScript 来处理 AJAX 成功后的回调 -->
    <script>
        $(document).ready(function () {
            // 监听所有 class 为 "ajax-link" 的链接点击事件
            // 注意:这里我们使用传统的 click 事件,而不是 .ajaxLink(),因为后者需要 unobtrusive-ajax 库的特殊支持
            // 我们手动处理,更灵活
            $(document).on("click", ".ajax-link", function (e) {
                e.preventDefault(); // 阻止链接的默认跳转行为
                var url = $(this).attr("href");
                var $targetDiv = $("#main-content"); // 找到目标容器
                // 显示加载提示(可选)
                $targetDiv.html("<p>Loading...</p>");
                // 使用 jQuery AJAX 发送请求
                $.ajax({
                    url: url,
                    type: "GET",
                    success: function (result) {
                        // 请求成功后,将返回的 HTML 片段插入到目标 div 中
                        $targetDiv.html(result);
                    },
                    error: function (error) {
                        $targetDiv.html("<p>Error loading content.</p>");
                        console.log(error);
                    }
                });
            });
        });
    </script>
</body>
</html>

代码解释:

  1. <div id="main-content">: 我们给 @RenderBody() 包裹了一个 div,并设置 id="main-content",这是局部刷新的“靶子”。
  2. 导航链接: 我们为需要局部刷新的链接(如“技术”、“生活”)设置了 class="ajax-link",并指定了它们的 Action (PostsByCategory) 和参数 (category)。
  3. jquery.unobtrusive-ajax.min.js: 这个库是 ASP.NET MVC AJAX Helper 的依赖,它可以将 data-* 属性自动转换为 AJAX 请求,虽然我们这里手动写了 AJAX,但引入它总没错,并且能与其他 MVC AJAX 功能兼容。
  4. JavaScript 代码:
    • $(document).on("click", ".ajax-link", ...):监听所有带有 ajax-link 类的元素的点击事件,使用 document 作为父元素可以处理动态加载的内容。
    • e.preventDefault()至关重要,阻止了浏览器默认的页面跳转行为。
    • $.ajax({...}):发起 AJAX GET 请求。
    • success: function(result) {...}:当服务器成功返回数据后,执行此回调函数。result 就是服务器返回的 HTML 片段,我们用 $targetDiv.html(result) 来更新页面内容。

步骤 2:创建 Action 和 Partial View

我们需要创建一个 Action,它只返回文章列表的 HTML 片段。

Controller (HomeController.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcPartialRefreshDemo.Controllers
{
    public class HomeController : Controller
    {
        // 用于首次加载的完整页面
        public ActionResult Index()
        {
            return View(); // 返回完整的 Index.cshtml,它会使用 _Layout.cshtml
        }
        // 用于 AJAX 请求的 Action,返回部分视图
        public ActionResult PostsByCategory(string category)
        {
            // 模拟从数据库获取数据
            var posts = GetPostsByCategory(category);
            // 返回一个 Partial View,而不是完整的 View
            // MVC 会自动寻找 ~/Views/Home/PostsByCategory.cshtml
            // 并且渲染它时不会使用 _Layout.cshtml
            return PartialView("_PostList", posts); 
        }
        private List<Post> GetPostsByCategory(string category)
        {
            // 这里是模拟数据
            var allPosts = new List<Post>
            {
                new Post { Title = "ASP.NET MVC 入门", Category = "tech", Content = "ASP.NET MVC 是一个强大的框架..." },
                new Post { Title = "如何学习 C#", Category = "tech", Content = "C# 是一门优雅的语言..." },
                new Post { Title = "我的周末生活", Category = "life", Content = "这个周末我去爬山了..." },
                new Post { Title = "美食探店", Category = "life", Content = "发现了一家超好吃的拉面店..." }
            };
            return allPosts.Where(p => p.Category == category).ToList();
        }
    }
    public class Post
    {
        public string Title { get; set; }
        public string Category { get; set; }
        public string Content { get; set; }
    }
}

代码解释:

  • PostsByCategory(string category) Action 是专门为 AJAX 请求设计的。
  • return PartialView("_PostList", posts); 是关键。
    • PartialView() 方法告诉 MVC,这次渲染不要使用 _Layout.cshtml 布局页。
    • "_PostList" 指定了要渲染的部分视图的文件名,约定上,Partial View 的文件名以下划线 _ 开头,以区别于常规的 View。
    • posts 是传递给 Partial View 的 Model。

Partial View (_PostList.cshtml)

这个文件非常简单,它只包含渲染文章列表所需的 HTML,没有任何布局结构。

@model IEnumerable<MvcPartialRefreshDemo.Post>
<h2>文章列表 (@Model.FirstOrDefault()?.Category)</h2>
@foreach (var post in Model)
{
    <div class="post-item">
        <h3>@post.Title</h3>
        <p>@post.Content</p>
    </div>
}

步骤 3:运行测试

  1. 启动项目,访问 /Home/Index,你会看到一个完整的页面,包含导航和初始的文章列表。
  2. 点击导航栏中的“技术”链接,你会发现,只有中间的文章列表区域(id="main-content"发生了变化,而页头和页脚保持不动,URL 也没有改变,这就是局部刷新!

现代化方案:SPA (Single-Page Application)

对于更复杂的交互,上述方法可能会因为大量的 JavaScript 和 DOM 操作而变得难以维护,现代 Web 开发更倾向于使用 SPA 框架(如 React, Angular, Vue.js)。

SPA 的思路是:

  1. 首次加载:服务器只返回一个几乎为空的 HTML 页面和一个巨大的 JavaScript 包。
  2. 前端接管:JavaScript 框架接管整个页面的渲染。
  3. 数据交互:所有后续的数据请求都通过 API (通常是 RESTful API) 以 JSON 格式进行,前端拿到 JSON 数据后,再用自己的模板引擎(如 JSX, Vue Template)动态生成 HTML 并更新到页面上。

在这种模式下,MVC 的 Controller 可以专注于提供 JSON 数据(API Controller),而 View 的渲染工作几乎完全由前端框架完成,这实现了前后端更彻底的分离。

特性 传统 MVC 局部刷新 (jQuery + AJAX) 现代化 SPA (React/Vue/Angular)
核心思想 后端渲染 HTML 片段,前端替换 DOM 前端渲染,后端提供 JSON 数据 API
技术栈 ASP.NET MVC, jQuery, Partial View SPA Framework, Webpack, RESTful API
优点 学习曲线低,适合现有 MVC 项目升级;服务器渲染利于 SEO。 用户体验极致流畅;前后端职责清晰,易于维护和扩展。
缺点 前端逻辑耦合度高,复杂应用难以维护;仍有一定服务器负载。 首次加载慢;SEO 需要额外处理(如 SSR);技术栈更复杂。
适用场景 中小型项目,有大量现有 MVC 代码,需要局部优化性能。 复杂的交互式 Web 应用,如后台管理系统、社交平台等。

对于你的问题,使用 jQuery + AJAX 配合 MVC 的 Partial View 是最直接、最符合 MVC 架构的解决方案,它完美地解决了模板页和内容页局部刷新的需求。