You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
abp/docs/zh-Hans/AspNetCore/Bundling-Minification.md

355 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## ASP.NET Core MVC 捆绑 & 压缩
有许多方法可以捆绑&压缩客户端资源(JavaScript和CSS文件). 最常见的方式是:
* 使用Visual Studio[捆绑&压缩](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier)扩展或者其它的[NuGet相关包](https://www.nuget.org/packages/BuildBundlerMinifier/).
* 使用[Gulp](https://gulpjs.com/)/[Grunt](https://gruntjs.com/)及其插件.
ABP内置了简单,动态,强大,模块化的方式.
### Volo.Abp.AspNetCore.Mvc.UI.Bundling 包
> 默认情况下已在启动模板安装此软件包. 大多数情况下,你不需要手动安装它.
将`Volo.Abp.AspNetCore.Mvc.UI.Bundling` nuget包安装到你的项目中:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
然后将`AbpAspNetCoreMvcUiBundlingModule`依赖项添加到你的模块上:
````C#
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
public class MyWebModule : AbpModule
{
//...
}
}
````
### Razor Bundling Tag Helpers
创建bundle的最简单方法是使用`abp-script-bundle`或`abp-style-bundle` tag helpers. 例如:
````html
<abp-style-bundle name="MyGlobalBundle">
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
<abp-style src="/styles/my-global-style.css" />
</abp-style-bundle>
````
`abp-script-bundle`定义了一个带有**唯一名称**的样式包:`MyGlobalBundle`. 使用方法很容易理解. 让我们看看它是如何*工作的*:
* 当首次请求时,ABP从提供的文件中 **(延迟)lazy** 创建. 后续将从 **缓存** 中返回内容. 这意味着如果你有条件地将文件添加到包中,它只执行一次, 并且条件的任何更改都不会影响下一个请求的包.
* 在`development`环境中ABP会将包文件**单独**添加到页面中, 其他环境(`staging`,`production`...)会自动捆绑和压缩.
* 捆绑文件可以是**物理**文件或[**虚拟/嵌入**](../Virtual-File-System.md)的文件.
* ABP自动将 **版本查询字符串(version query string)** 添加到捆绑文件的URL中,以防止浏览器缓存. 如:?_v=67872834243042(从文件的上次更改日期生成). 即使捆绑文件单独添加到页面(在`development`环境中), 版本控制仍然有效.
#### 导入 Bundling Tag Helpers
> 默认情况下已在启动模板导入. 大多数情况下,你不需要手动安装它.
要使用`bundle tag helpers`, 你需要将其添加到`_ViewImports.cshtml`文件或页面中:
````
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
#### 未命名的 Bundles
对于razor bundle tag helpers, `name`是**可选**. 如果没有定义一个名字,它将根据使用的捆绑文件名自动**计算生成**(they are **concatenated** and **hashed**) 例:
````html
<abp-style-bundle>
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
@if (ViewBag.IncludeCustomStyles != false)
{
<abp-style src="/styles/my-global-style.css" />
}
</abp-style-bundle>
````
这将潜在地创建**两个不同的bundles**(一个包括`my-global-style.css`而另一个则不包括).
**未命名的** bundles优点:
* 可以**有条件地将项目**添加到捆绑包中. 但这可能会导致基于条件的捆绑的存在多种变化.
**命名** bundles优点:
* 其他模块可以通过其名称为捆绑包做出贡献(参见下面的部分).
#### 单个文件
如果你只需要在页面中添加一个文件, 你可以使用`abp-script`或`abp-style`而不需要包含在`abp-script-bundle`或`abp-style-bundle`中. 例:
````xml
<abp-script src="/scripts/my-script.js" />
````
对于上面的示例,包名称将是 *scripts.my-scripts*("/"替换为"."). 所有捆绑功能也可以按预期应用于单个文件.
### Bundling 选项
如果你需要在 **多个页面中使用相同的包** 或想要使用更多 **强大功能**, 你可以在[模块](../Module-Development-Basics.md)类中进行**配置**.
#### 创建一个新的捆绑包
用法示例:
````C#
[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
public class MyWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<BundlingOptions>(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"
);
});
});
}
}
````
> 你可以在脚本和样式包中使用相同的名称(*MyGlobalBundle*), 因为它们被添加到不同的集合(`ScriptBundles`和`StyleBundles`).
在定义bundle之后, 可以使用上面定义的相同tag helpers将其包括在页面中. 例如:
````html
<abp-script-bundle name="MyGlobalBundle" />
````
这次tag helper定义中没有定义文件, 因为捆绑文件是由代码定义的.
#### 配置现有的 Bundle
ABP也支持[模块化](../Module-Development-Basics.md)捆绑. 模块可以修改由依赖模块创建的捆绑包.
例如:
````C#
[DependsOn(typeof(MyWebModule))]
public class MyWebExtensionModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<BundlingOptions>(options =>
{
options
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddFiles(
"/scripts/my-extension-script.js"
);
});
});
}
}
````
> 无法通过代码配置未命名的bundle tag helpers, 因为它们的名称在开发时是未知的. 建议始终使用bundle tag helper的名称.
### Bundle 贡献者
将文件添加到现有bundle似乎很有用. 如果你需要**替换**bundle中的文件或者你想**有条件地**添加文件怎么办? 定义bundle贡献者可为此类情况提供额外的功能.
一个bundle的贡献者使用自定义版本bootstrap.css替换示例:
````C#
public class MyExtensionGlobalStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.ReplaceOne(
"/libs/bootstrap/css/bootstrap.css",
"/styles/extensions/bootstrap-customized.css"
);
}
}
````
然后你可以按照下面的代码使用这个贡献者:
````C#
services.Configure<BundlingOptions>(options =>
{
options
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor));
});
});
````
贡献者也可以在bundle tag helpers中使用.
例如:
````xml
<abp-style-bundle>
<abp-style type="@typeof(BootstrapStyleContributor)" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
</abp-style-bundle>
````
`abp-style`和`abp-script`标签可以使用`type`属性(而不是`src`属性), 如本示例所示. 添加bundle贡献者时, 其依赖关系也会自动添加到bundle中.
#### 贡献者依赖关系
bundle贡献者可以与其他贡献者具有一个或多个依赖关系.
例如:
````C#
[DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
//...
}
````
添加bundle贡献者时,其依赖关系将 **自动并递归** 添加. **依赖顺序** 通过阻止 **重复** 添加的依赖关系. 即使它们处于分离的bundle中,也会阻止重复. ABP在页面中组织所有bundle并消除重复.
创建贡献者和定义依赖关系是一种跨不同模块组织bundle创建的方法.
#### 贡献者扩展
在某些高级应用场景中, 当用到一个bundle贡献者时你可能想做一些额外的配置. 贡献者扩展可以和被扩展的贡献者无缝衔接.
下面的示例为 prism.js 脚本库添加一些样式:
````csharp
public class MyPrismjsStyleExtension : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.css");
}
}
````
然后你可以配置 `BundleContributorOptions` 去扩展已存在的 `PrismjsStyleBundleContributor`.
````csharp
Configure<BundleContributorOptions>(options =>
{
options
.Extensions<PrismjsStyleBundleContributor>()
.Add<MyPrismjsStyleExtension>();
});
````
任何时候当 `PrismjsStyleBundleContributor` 被添加到bundle中时, `MyPrismjsStyleExtension` 也会被自动添加.
#### 访问 IServiceProvider
虽然很少需要它, 但是`BundleConfigurationContext`有一个`ServiceProvider`属性, 你可以在`ConfigureBundle`方法中解析服务依赖.
#### 标准包装贡献者
将特定的NPM包资源(js,css文件)添加到包中对于该包非常简单. 例如, 你总是为bootstrap NPM包添加`bootstrap.css`文件.
所有[标准NPM包](Client-Side-Package-Management.md)都有内置的贡献者. 例如,如果你的贡献者依赖于bootstrap,你可以声明它,而不是自己添加bootstrap.css.
````C#
[DependsOn(typeof(BootstrapStyleContributor))] //Define the bootstrap style dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
//...
}
````
使用标准包的内置贡献者:
* 防止你输入**无效的资源路径**.
* 如果资源 **路径发生变化** (依赖贡献者将处理它),则防止更改你的贡献者.
* 防止多个模块添加**重复文件**.
* 以递归方式管理依赖项(如果需要,添加依赖项的依赖项).
##### Volo.Abp.AspNetCore.Mvc.UI.Packages 包
> 默认情况下已在启动模板安装此软件包. 大多数情况下,你不需要手动安装它.
标准包贡献者在`Volo.Abp.AspNetCore.Mvc.UI.Packages` NuGet包中定义.
将它安装到你的项目中:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Packages
````
然后将`AbpAspNetCoreMvcUiPackagesModule`模块依赖项添加到你的模块中;
````C#
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAspNetCoreMvcUiPackagesModule))]
public class MyWebModule : AbpModule
{
//...
}
}
````
#### Bundle 继承
在某些特定情况下, 可能需要从其他bundle创建一个 **新** bundle **继承**, 从bundle继承(递归)会继承该bundle的所有文件/贡献者. 然后派生的bundle可以添加或修改文件/贡献者**而无需修改**原始bundle.
例如:
````c#
services.Configure<BundlingOptions>(options =>
{
options
.StyleBundles
.Add("MyTheme.MyGlobalBundle", bundle => {
bundle
.AddBaseBundles("MyGlobalBundle") //Can add multiple
.AddFiles(
"/styles/mytheme-global-styles.css"
);
});
});
````
### 主题
主题使用标准包贡献者将库资源添加到页面布局. 主题还可以定义一些标准/全局包, 因此任何模块都可以为这些标准/全局包做出贡献. 有关更多信息, 请参阅[主题文档](Theming.md).
### 最佳实践 & 建议
建议为应用程序定义多个包, 每个包用于不同的目的.
* **全局包**: 应用程序中的每个页面都包含全局样式/脚本包. 主题已经定义了全局样式和脚本包. 你的模块可以为他们做出贡献.
* **布局包**: 这是针对单个布局的特定包. 仅包含在所有页面之间共享的资源使用布局. 使用bundling tag helpers创建捆绑包是一种很好的做法.
* **模块包**: 用于单个模块页面之间的共享资源.
* **页面包**: 为每个页面创建的特定包. 使用bundling tag helpers创建捆绑包作为最佳实践.
在性能,网络带宽使用和捆绑包的数量之间建立平衡.
### 参见
* [客户端包管理](Client-Side-Package-Management.md)
* [主题](Theming.md)