ASP.NET MVC 动态 Bundle 介绍






4.87/5 (34投票s)
将构成页面或组件的 HTML、CSS、JavaScript 和图像文件放在一起。自动生成 MVC bundle 以确保以正确的顺序加载正确的文件。
引言
当您的网站变得复杂时,通过使用组件(例如局部视图、DisplayTemplates 和 EditorTemplates)来保持其可维护性是有意义的。
问题是,虽然您可以这样组织 HTML,但您的 CSS、JavaScript 和图像仍然存储在它们自己的目录中——没有明确指示哪些 CSS 等被哪些局部视图使用。本质上,每个局部视图的实现分散在最多 4 个目录中。
这使得在更改 CSS 或 JavaScript 文件时很容易破坏局部视图——因为 CSS、JavaScript 和 HTML 之间的依赖关系并不总是很清楚。这也使得在其他地方重用局部视图变得困难。
您可以通过使用 MVC 区域在一定程度上解决此问题,但这些区域主要用于创建子站点。
解决方案是将支持某些 HTML 的所有资产(CSS、JavaScript 和图像)与该 HTML 一起放在同一个目录中。支持控制器中所有页面的资产可以放在该控制器的视图目录中。站点中共享的资产可以放在顶层目录中。
这会导致许多小型、集中的 CSS 和 JavaScript 文件。这是一件好事,但这使得创建 MVC bundle 变得更加困难——您的页面仍然必须以正确的顺序加载正确的 CSS 和 JavaScript。为了帮助解决这个问题,当页面加载时,Dynamic Bundles 会为您生成 bundle。缓存将开销保持在最低限度。
主要优点
Dynamic Bundles 是 Razor 视图引擎和 MVC bundle 的扩展。它大大提高了 ASP.NET MVC 站点的可维护性和代码重用性
- 将构成页面或组件的 HTML、CSS、JavaScript 和图像放在同一个目录中,而不是按类型将这些文件组织在单独的目录中。这清楚地揭示了依赖关系。
- 根据文件组织自动生成包含正确文件并按正确顺序排列的 CSS 和 JavaScript bundle。添加或删除文件时无需重新编译。缓存使 CPU 使用率和磁盘访问量降至最低。
- 提供与标准 MVC bundle 相同的缩小和文件合并功能。
经典 MVC
- HTML、CSS、JavaScript 和图像文件按类型组织在单独的目录中。
- 支持不同页面和组件的 CSS 和 JavaScript 通常放在相同的物理文件中,从而创建隐藏的依赖关系。
- 从 CSS 文件到背景图像的冗长易碎 URL。
- 不清楚给定组件需要哪些 CSS、JavaScript 和图像,使得重用更加困难。

动态 Bundle
- HTML、CSS、JavaScript 和图像文件属于同一目录。
- Dynamic Bundles 中包含的视图引擎允许您将局部视图和布局文件放在它们自己的子目录中。
- 按组件拆分 CSS、JavaScript 鼓励开发人员保持这些文件小巧且专注。
- CSS 中短而简单的图像背景图像 URL。
- 将构成组件的所有资产放在一起使得重用变得容易得多。

经典 MVC
- 您必须自己创建和维护 bundle。
- 您必须确保包含正确的文件,并按正确的顺序。
- 添加或删除 CSS 和 JavaScript 文件时,需要重新编译站点。
public static void RegisterBundles(BundleCollection bundles) { // Need to create bundles yourself, in code. To make any change, // you have to recompile. Must always make sure to include the // right files in the right order. bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/shared/js").Include( "~/Scripts/SharedCode.js", "~/Scripts/VariousCode.js")); bundles.Add(new ScriptBundle("~/bundles/pile/js").Include( "~/Scripts/PileOfCode.js")); bundles.Add(new StyleBundle("~/Content/shared/css").Include( "~/Content/Reset.css", "~/Content/Site.css")); bundles.Add(new StyleBundle("~/Content/account/css").Include( "~/Content/Account.css")); }
@Styles.Render("~/Content/shared/css") @Styles.Render("~/Content/account/css") ... @RenderBody() ... @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/shared/js") @Scripts.Render("~/bundles/pile/js")
动态 Bundle
- Bundle 自动生成。无需自己创建 bundle。
- 确保只加载所需的 CSS 和 JavaScript 文件,并按正确的顺序。
- 通过按区域、控制器、共享和布局将文件组合成 bundle 来优化客户端缓存。
- 添加或删除 CSS 和 JavaScript 文件时,会自动生成新的 bundle,无需重新编译。
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { // No need to register bundles in BundleConfig } }
@*Nominate where to load the bundles. The bundles themselves are automatically generated.*@ @DynamicBundlesTopRender() ... @RenderBody() ... @DynamicBundlesBottomRender()
安装
1. 安装动态 Bundle
从 NuGet 安装 DynamicBundles 包
Install-Package DynamicBundles
2. 将视图引擎添加到 global.asax
更新您的 global.asax.cs 或 global.asax.vb,以添加 DynamicBundles 视图引擎
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);// Add DynamicBundles view engine. This functions the same as the Razor view engine, // but can find views sitting in their own directories, such as ~/Views/Home/Index/Index.cshtml // Note: this leaves the other view engines in place, so they can still be used. ViewEngines.Engines.Add(new DynamicBundles.DynamicBundlesViewEngine()); } ... }
3. 添加布局容器
在经典 MVC 站点中,页面位于 _Layout.cshtml 或 _Layout.vbhtml 文件中,该文件包含共享的页眉、页脚等。
引入 Dynamic Bundles 时的问题是,您希望将特定于布局的 CSS、JavaScript 和图片与整个站点共享的那些分离。
为了实现这种分离,创建一个新文件 _LayoutContainer.cshtml(稍后您将看到其内容 )。此文件和 _Layout.cshtml 放在它们自己的目录中。结果如下
经典 MVC

动态 Bundle

_LayoutContainer.cshtml 的内容
<!DOCTYPE html> <html> @*Nominate where to load the bundles. The bundles themselves are automatically generated.*@ @DynamicBundlesTopRender() @RenderBody() @DynamicBundlesBottomRender() </html>
_Layout.cshtml 的更改
- 将 Layout 设置为 _LayoutContainer.cshtml,它充当站点的整体容器。
- 删除 doctype 和 html 标签。
- 删除所有样式和脚本渲染,包括脚本部分的渲染。
@{ // Add _LayoutContainer as the container for the _Layout.cshtml file itself. Layout = "../_LayoutContainer/_LayoutContainer.cshtml"; }<!DOCTYPE html> <html><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Dynamic Bundles for ASP.NET MVC</title>@Styles.Render("~/Content/shared/css")@Styles.Render("~/Content/account/css") </head> <body> @RenderBody()@Scripts.Render("~/bundles/jquery")@Scripts.Render("~/bundles/shared/js")@Scripts.Render("~/bundles/pile/js")@RenderSection("scripts", required: false)</body></html>
4. 更新视图的 Web.config
除了站点根目录中的 Web.config 文件之外,每个 MVC 站点在其 Views 目录中还有一个 Web.config 文件。如果您的站点使用区域,则每个区域也有一个 Views 目录,以及其自己的 Web.config 文件。
Views 目录中的 Web.config 文件需要更新为
- 安装动态 Bundle 页面基类型。这使得每个视图都能注册其所需的资产,以便可以生成包含正确文件的 bundle。
- 允许 Web 服务器从 Views 目录提供 CSS、JavaScript 和图像文件。
<configuration> <system.web.webPages.razor> <pages pageBaseType="System.Web.Mvc.WebViewPageDynamicBundles.WebViewPage"> ... </pages> </system.web.webPages.razor> <system.webServer> <!-- The BlockViewHandler blocks all requests. In classic MVC sites, it is used to block all requests for files from a Views directory. With Dynamic Bundles where CSS, JavaScript and images files are co-located with the view files, we only want to block requests for the view files. --> <handlers> <remove name="BlockViewHandler"/> <!-- Replace path="*.cshtml" with path="*.vbhtml" if you use Visual Basic. --> <add name="BlockViewHandler" path="*.cshtml" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" /> </handlers> </system.webServer> </configuration>
5. 共置资产
最后,将您的资产文件(CSS、JavaScript、图像)与它们所使用的视图共置
- 将整个站点中共享的所有资产移动到 _LayoutContainer 目录中。
- 将控制器所有视图共享的所有资产移动到该控制器的目录中。
- 如果存在特定于单个视图文件的资产,请为该视图文件创建一个子目录,并将所有资产(包括视图本身)放入该子目录中。请务必将子目录命名与视图文件相同,不带扩展名。
- Dynamic Bundles 将添加子目录中的资产及其父目录中的资产,先添加不那么具体的资产。在下面的示例中,当加载 /Product/List 时,首先添加 ~/Views/Product 中的资产,然后添加 ~/Views/Product/List 中的资产。
这适用于控制器特定视图和共享(局部)视图。Dynamic Bundles 确保资产只添加到 bundle 中一次。
6. 创建显式依赖关系
您可能在一个目录中对另一个目录有依赖关系。例如,一个 JavaScript 文件假定另一个目录中的文件已加载。
您可以使用 .nuspec 文件指定这些依赖关系。这些文件与它们的 NuGet 对应物具有相同的结构(定义)。
如果 Dynamic Bundles 在目录中找到 nuspec 文件,它将查找该 nuspec 文件中指定的目录,并将这些目录中的资产添加到 bundle 中。如果这些目录本身有 nuspec 文件,Dynamic Bundles 也会处理这些 nuspec 文件,依此类推。这是一个完全递归的过程。
要从目录 X 创建对其他一些目录的依赖关系,请在目录 X 中包含一个 .nuspec 文件,其内容如下
<?xml version="1.0"?> <package > <metadata> <dependencies> <dependency id="../AccountDetailsAssets" /> <dependency id="~/Views/Shared/DetailsAssets" /> </dependencies> </metadata> </package>
请注意
- .nuspec 文件的名称无关紧要,只要它具有 .nuspec 扩展名即可。
- 您可以指定根相对路径(以 ~/ 开头)和相对于 .nuspec 文件的路径,但不能指定绝对路径(例如 C:\Dev\Views\Accounts)。