Partial View là gì?

Partial view là một Razor markup file (.cshtml) không có directive @page, dùng để render HTML output bên trong output của một markup file khác. Trong MVC, markup files được gọi là views; trong Razor Pages, chúng được gọi là pages. Thuật ngữ “partial view” dùng chung cho cả hai.

Khi nào nên dùng Partial Views

Partial views hiệu quả trong các trường hợp:
Trường hợpMô tả
Chia nhỏ file lớnTrong một markup file phức tạp gồm nhiều phần logic, có lợi khi làm việc với từng phần tách biệt trong partial view
Giảm trùng lặp markupKhi cùng markup được dùng ở nhiều file, partial view loại bỏ sự trùng lặp — thay đổi một lần trong partial view sẽ cập nhật tất cả các file dùng nó

Không nên dùng Partial Views khi

  • Duy trì layout chung → Dùng _Layout.cshtml
  • Cần logic phức tạp hoặc code execution để render → Dùng View Component

Khai báo Partial View

Một partial view là file .cshtml không có @page directive, nằm trong thư mục Views (MVC) hoặc Pages (Razor Pages).

Quy ước đặt tên

Tên file partial view thường bắt đầu bằng dấu gạch dưới _. Quy ước này không bắt buộc nhưng giúp phân biệt trực quan partial views với views và pages.
MyApp/
├── Views/
│   ├── Home/
│   │   ├── Index.cshtml
│   │   ├── About.cshtml
│   │   └── _Header.cshtml      ← Partial view
│   ├── Products/
│   │   ├── Index.cshtml
│   │   └── _ProductCard.cshtml  ← Partial view
│   └── Shared/
│       ├── _Footer.cshtml       ← Partial view dùng chung
│       └── _AuthorBio.cshtml   ← Partial view dùng chung
Lưu ý: Không giống MVC view rendering, partial view không chạy _ViewStart.cshtml.

Tham chiếu Partial View

Có nhiều cách để tham chiếu partial view trong markup file. Khuyến nghị dùng cách async.

1. Partial Tag Helper (Khuyến nghị)

Partial Tag Helper render content asynchronously và sử dụng syntax giống HTML. Yêu cầu ASP.NET Core 2.1+.
@* Cú pháp cơ bản — không cần extension *@
<partial name="_PartialName" />
@* Khi có extension, partial phải cùng folder với file gọi *@
<partial name="_PartialName.cshtml" />

Tham chiếu từ app root

@* Razor Pages *@
<partial name="~/Pages/Folder/_PartialName.cshtml" />
<partial name="/Pages/Folder/_PartialName.cshtml" />

@* MVC *@
<partial name="~/Views/Folder/_PartialName.cshtml" />
<partial name="/Views/Folder/_PartialName.cshtml" />

Tham chiếu bằng relative path

<partial name="../Account/_PartialName.cshtml" />

2. HTML Helper — Async (Khuyến nghị)

Dùng PartialAsync trả về IHtmlContent wrapped trong Task. Phải prefix với @:
@await Html.PartialAsync("_PartialName")
@await Html.PartialAsync("_PartialName.cshtml")

Tham chiếu từ app root

@await Html.PartialAsync("~/Views/Folder/_PartialName.cshtml")
@await Html.PartialAsync("/Views/Folder/_PartialName.cshtml")

Tham chiếu bằng relative path

@await Html.PartialAsync("../Account/_LoginPartial.cshtml")

3. HTML Helper — Render Async

RenderPartialAsync streams rendered output trực tiếp đến response, không trả về IHtmlContent. Phải gọi trong Razor code block:
@{
    await Html.RenderPartialAsync("_AuthorPartial");
}
RenderPartialAsynchiệu năng tốt hơn trong một số scenario vì không buffer kết quả. Benchmark để chọn cách phù hợp.

4. HTML Helper — Sync (Không khuyến nghị)

PartialRenderPartial là phiên bản synchronous. Không khuyến nghị vì có thể gây deadlock.
Cảnh báo: Visual Studio sẽ cảnh báo khi dùng Partial: “Use of IHtmlHelper.Partial may result in application deadlocks. Consider using <partial> Tag Helper or IHtmlHelper.PartialAsync.”

Partial View Discovery

Khi partial view được tham chiếu mà không có extension, thứ tự tìm kiếm như sau:

MVC

Quy ước quan trọng

  • Partial views cùng tên nhưng khác folder → được phép
  • Khi tìm không có extension: nếu partial view tồn tại ở cả caller folder và Shared folder → partial ở caller folder được ưu tiên
  • Partial views có thể chuỗi (partial gọi partial khác), miễn không tạo circular reference
  • Relative paths luôn relative với current file, không phải root
Lưu ý: Razor section định nghĩa trong partial view không thấy được bởi parent markup file. Section chỉ visible trong partial view nó được định nghĩa.

Truyền dữ liệu đến Partial Views

ViewData

Khi partial view được khởi tạo, nó nhận bản sao của ViewData dictionary từ parent. Thay đổi trong partial view không ảnh hưởng parent view.
@await Html.PartialAsync("_PartialName", customViewData)
@* Với Partial Tag Helper *@
<partial name="_PartialName" view-data="customViewData" />

Truyền Model

Model có thể là bất kỳ object nào:
@* PartialAsync — async, trả về IHtmlContent *@
@await Html.PartialAsync("_ProductCard", product)

@* RenderPartialAsync — async, stream trực tiếp *@
@{ await Html.RenderPartialAsync("_ProductCard", product); }

Ví dụ: Article với Sections

View chính

@model Article

<h2>@Model.Title</h2>

@* Partial view đơn giản — truyền string *@
@await Html.PartialAsync("_AuthorPartial", Model.AuthorName)

@Model.PublicationDate

@* Loop truyền model + ViewData *@
@{
    var index = 0;
    foreach (var section in Model.Sections)
    {
        @(await Html.PartialAsync("_ArticleSection",
                                section,
                                new ViewDataDictionary(ViewData)
                                {
                                    { "index", index }
                                }))
        index++;
    }
}

Partial 1: _AuthorPartial.cshtml

@model string

<div>
    <h3>@Model</h3>
    Tác giả từ /Views/Shared/_AuthorPartial.cshtml.
</div>

Partial 2: _ArticleSection.cshtml

@using MyApp.ViewModels
@model ArticleSection

<h3>@Model.Title — Index: @ViewData["index"]</h3>
<div>
    @Model.Content
</div>

So sánh các cách render Partial View

CáchAsyncTrả vềPerformanceKhuyến nghị
<partial> Tag Helper✅ AsyncIHtmlContentTốtƯu tiên
PartialAsync✅ AsyncIHtmlContentTốt✅ Khuyến nghị
RenderPartialAsync✅ AsyncStreamTốt nhấtDùng khi cần performance
Partial❌ SyncIHtmlContentTrung bình❌ Tránh dùng
RenderPartial❌ SyncStreamTốt❌ Tránh dùng

Tài liệu tham khảo