Bundling(打包)指将多个JavaScript文件、CSS文件合并成一个或几个文件的过程。
Bundling的主要目的:
- 减少HTTP请求:通过合并文件,可以减少浏览器需要发起的HTTP请求数量,从而加快首页加载速度。
- 依赖管理:自动处理模块之间的依赖关系,确保代码正确执行。
- 代码分割:支持将代码分割成多个块(chunks),按需加载,进一步优化性能。
Minification(压缩)指在不更改功能的情况下从代码中删除不必要的字符,例如将变量名称缩短为一个字符、删除注释和不必要的空格。
Minification的主要目的:
- 减少文件体积:通过去除不必要的字符,可以显著减少文件的大小,从而减少传输的时间和带宽消耗。
- 提高加载速度:较小的文件可以更快的加载,加快页面的加载速度。
有许多方法可以打包(bundling)和压缩(minification)客户端资源(JavaScript和CSS文件)。最常见的方法是:
- 使用Bundler & Minifier Visual Studio扩展或NuGet包。
- 使用Gulp/Grunt任务管理器及其插件。
ABP是模块化的,各个模块使用的脚本和样式需要在index.html文件中引用。ABP提供了一种内置的方式,简单、动态、模块化地执行Bundling和Minification。
ABP的App.razor文件如下:
<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir="@rtl">
<head>...<AbpStyles BundleName="@BlazorLeptonXLiteThemeBundles.Styles.Global" WebAssemblyStyleFiles="GlobalStyles" @rendermode="InteractiveAuto" />...
</head>
<body class="abp-application-layout @rtl">...<AbpScripts BundleName="@BlazorLeptonXLiteThemeBundles.Scripts.Global" WebAssemblyScriptFiles="GlobalScripts" @rendermode="InteractiveAuto" /><script src="_framework/blazor.web.js"></script></body>
</html>
@code{private List<string> GlobalStyles =>["global.css",];private List<string> GlobalScripts =>["global.js"];
}
<AbpStyles>
标签将输出为:
<link rel="stylesheet" href=“...” />
<AbpScripts>
标签将输出为:
<script src="..." />
对于Blazor Server和Blazor WebAssembly,执行Bundling和Minification的原理有所不同。
Blazor Server 中
Blazor Server中的 Bundling 和 Minification 是动态执行的,它与ABP MVC的Bundling过程很相似(使用的标签不同)。通过在模块类中ConfigureServices
方法中配置AbpBundlingOptions
选项,添加js和css bundle。
需要添加Volo.Abp.AspNetCore.Mvc.UI.Bundling
NuGet包,默认情况下,这个包已经安装在启动模板中。因此,大多数时候,您不需要手动安装它。如果没有使用启动模板,可以使用ABP CLI将其安装到项目中。在项目文件夹(包含.csproj
文件的文件夹)中执行以下命令:
abp add-package Volo.Abp.AspNetCore.Mvc.UI.Bundling
创建一个新的Bundle
Configure<AbpBundlingOptions>(options =>
{options.ScriptBundles.Add("MyGlobalBundle", bundle => {bundle.AddFiles("/libs/jquery/jquery.js","/libs/bootstrap/js/bootstrap.js","/libs/toastr/toastr.min.js","/scripts/my-global-scripts.js");});
});
配置现有的Bundle
Configure<AbpBundlingOptions>(options =>
{options.StyleBundles.Configure(BlazorLeptonXLiteThemeBundles.Styles.Global,bundle =>{bundle.AddFiles("/blazor-global-styles.css");//You can remove the following line if you don't use Blazor CSS isolation for componentsbundle.AddFiles(new BundleFile("/Avalon.Blazor.styles.css", true));bundle.AddFiles(new BundleFile("/Avalon.Blazor.Client.styles.css", true));});
});
Bundle Contributor
可以创建一个BundleContributor
的派生类,更清晰的管理js和css文件。
public class BlazorLeptonXLiteThemeScriptContributor : BundleContributor
{public override void ConfigureBundle(BundleConfigurationContext context){context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap/js/bootstrap.bundle.js");context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/jquery/jquery.min.js");context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap-datepicker/js/bootstrap-datepicker.min.js");}
}
public class BlazorLeptonXLiteThemeStyleContributor : BundleContributor
{public override void ConfigureBundle(BundleConfigurationContext context){context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/chart.js/Chart.min.css");context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap-datepicker/css/bootstrap-datepicker.min.css");context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap-icons/font/bootstrap-icons.css");var rtlPostfix = CultureHelper.IsRtl ? ".rtl" : string.Empty;context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/abp-bundle{rtlPostfix}.css");context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/blazor-bundle{rtlPostfix}.css");context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/bootstrap-dim{rtlPostfix}.css");context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/layout-bundle{rtlPostfix}.css");context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/font-bundle{rtlPostfix}.css");}
}
在模块类中ConfigureServices
方法中如下配置AbpBundlingOptions
选项:
public override void ConfigureServices(ServiceConfigurationContext context)
{Configure<AbpBundlingOptions>(options =>{options.StyleBundles.Add(BlazorLeptonXLiteThemeBundles.Styles.Global, bundle =>{bundle.AddContributors(typeof(BlazorLeptonXLiteThemeStyleContributor));});options.ScriptBundles.Add(BlazorLeptonXLiteThemeBundles.Scripts.Global, bundle =>{bundle.AddContributors(typeof(BlazorLeptonXLiteThemeScriptContributor));});});
}
Contributor依赖
一个bundle contributor可以对其他contributor有一个或多个依赖关系
[DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{//...
}
当添加一个bundle contributor时,它的依赖项会自动地递归添加。依赖根据依赖顺序添加并去重。
标准Package的Contributors
所有标准NPM包都有内置的contributor。例如,如果您的contributor依赖于bootstrap,您可以直接声明它,而不是自己添加bootstrap.css。
[DependsOn(typeof(BootstrapStyleContributor))] //Define the bootstrap style dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{//...
}
使用标准包的内置contributors的优点:
- 防止您键入无效的资源路径。
- 防止在资源路径更改时更改您的contributor(依赖的contributor将处理它)。
- 防止多个模块添加重复的文件。
- 递归地管理依赖项(必要时添加依赖项的依赖项)。
需要按照Volo.Abp.AspNetCore.Mvc.UI.Packages
NuGet包。默认情况下,这个包已经安装在启动模板中。因此,大多数时候,您不需要手动安装它。如果没有使用启动模板,可以使用ABP CLI将其安装到项目中。在项目文件夹(包含.csproj
文件的文件夹)中执行以下命令:
abp add-package Volo.Abp.AspNetCore.Mvc.UI.Packages
访问IServiceProvider
虽然很少需要,但BundleConfigurationContext
有一个ServiceProvider
属性,你可以在ConfigureBundle
方法中解析服务依赖。
Bundle继承
在某些特定情况下,可能需要创建从其他bundle继承的新bundle。从一个bundle继承(递归地)将继承该bundle的所有文件和contributors。然后,派生的bundle可以添加或修改文件和contributors,而无需修改原始bundle。例子:
services.Configure<AbpBundlingOptions>(options =>
{options.StyleBundles.Add("MyTheme.MyGlobalBundle", bundle => {bundle.AddBaseBundles("MyGlobalBundle") //Can add multiple.AddFiles("/styles/mytheme-global-styles.css");});
});
Bundling模式
在development
环境中,ABP将包文件分开添加到页面中。ABP会为其他环境(staging
、production
……)自动进行打包和最小化。大多数情况下,这是你想要的行为。但是,在某些情况下,您可能需要手动配置它。有四种模式:
-
Auto
: 根据环境自动确定模式。 -
None
: 没有捆绑或缩小。 -
Bundle
: 捆绑但不缩小。 -
BundleAndMinify
: 捆绑和缩小。
你可以在模块的ConfigureServices
中配置AbpBundlingOptions
。
示例:
Configure<AbpBundlingOptions>(options =>
{options.Mode = BundlingMode.Bundle;
});
Blazor WebAssembly中
Blazor WebAssembly中的 Bundling 和 Minification 是不是动态执行的,而是通过bundle命令执行。
创建Bundle Contributor
在Blazor WebAssembly项目中,创建一个实现IBundleContributor
接口的类。
IBundleContributor
接口包含两个方法,都以BundleContext
作为参数:
-
AddScripts(...)
-
AddStyles(...)
public class MyProjectBundleContributor : IBundleContributor
{public void AddScripts(BundleContext context){context.Add("site.js");}public void AddStyles(BundleContext context){context.Add("main.css");context.Add("custom-styles.css");}
}
默认情况下,启动模板中有一个实现
IBundleContributor
接口的<ProjectName>BundleContributor
类。所以,大多数时候,您不需要手动添加它。
配置
在Blazor WebAssembly项目的wwwroot/appsettings.json
文件的AbpCli.Bundle
节点中包含了Bundling配置。
示例如下:
{"AbpCli": {"Bundle": {"Mode": "BundleAndMinify", /* Options: None, Bundle, BundleAndMinify */"Name": "global","IsBlazorWebApp": true,"InteractiveAuto": true,"Parameters": {}}}
}
-
Mode
:捆绑和缩小模式。可用的值有:-
BundleAndMinify
:将所有文件捆绑到一个文件中,并缩小内容。 -
Bundle
:将所有文件捆绑成一个文件,但不要缩小。 -
None
:单独添加文件,不要捆绑。
-
-
Name
:包文件名。默认值为global
。 -
Parameters
:您可以在此部分中定义其他键/值对参数。abp bundle
命令自动将这些参数发送给bundle贡献者,您可以在bundle贡献者中检查这些参数,并根据这些值采取一些操作。例如想要从bundle中排除某些资源:public class MyProjectNameBundleContributor : IBundleContributor {public void AddScripts(BundleContext context){}public void AddStyles(BundleContext context){var excludeThemeFromBundle = bool.Parse(context.Parameters.GetValueOrDefault("ExcludeThemeFromBundle"));context.Add("mytheme.css", excludeFromBundle: excludeThemeFromBundle);context.Add("main.css");} }
bundle命令
在Blazor WebAssembly项目中执行bundle命令:
abp bundle
bundle命令将检测IBundleContributor
接口的实现类中包含的js和css,并读取appsettings.json
文件中的配置,根据配置打包资源,并更新index.html
文件。
例如,根据以上配置,将在wwwroot
生成global.css
和global.js
bundle包,在App.razor文件文件的<AbpStyles>
标签和<AbpScripts>
标签的WebAssemblyScriptFiles
属性中引用了这两个bundle包。