- ASP.NET Core 中的捆绑和缩小静态资产Bundle and minify static assets in ASP.NET Core
- 什么是捆绑和缩小What is bundling and minification
- 捆绑和缩小的影响Impact of bundling and minification
- 选择捆绑和缩小策略Choose a bundling and minification strategy
- 配置捆绑和缩小Configure bundling and minification
- 捆绑和缩小的生成时执行Build-time execution of bundling and minification
- 捆绑和缩小的即席执行Ad hoc execution of bundling and minification
- 向工作流添加文件Add files to workflow
- 基于环境的捆绑和缩小Environment-based bundling and minification
- 从 Gulp 使用 bundleconfig.jsonConsume bundleconfig.json from Gulp
- 其他资源Additional resources
ASP.NET Core 中的捆绑和缩小静态资产Bundle and minify static assets in ASP.NET Core
本文内容
作者:Scott Addie 和 David Pine
本文介绍应用捆绑和缩小的好处,包括如何在 ASP.NET Core Web 应用中使用这些功能。
什么是捆绑和缩小What is bundling and minification
捆绑和缩小是可以在 Web 应用中应用的两个不同的性能优化。捆绑和缩小一起使用,可减少服务器的请求数并减小请求的静态资产的大小,从而提高性能。
捆绑和缩小主要缩短第一个页面请求加载时间。请求网页后,浏览器会缓存静态资产(JavaScript、CSS 和图像)。因此,在请求相同资产的同一站点上请求相同的一个或多个页面时,捆绑和缩小不会提高性能。如果未在资产上正确设置 expires 标头,且未使用捆绑和缩小,则浏览器的新鲜度启发会在几天后将资产标记为过期。此外,浏览器还需要对每个资产进行验证请求。在这种情况下,即使在第一个页面请求后,捆绑和缩小仍能提高性能。
捆绑Bundling
捆绑将多个文件合并到单个文件中。捆绑可减少呈现 Web 资产(如网页)所需的服务器请求数。可以专门为 CSS、JavaScript 等创建任意数量的单个捆绑。文件越少,从浏览器到服务器或从提供应用程序的服务的 HTTP 请求就越少。这会提高第一页加载性能。
缩小Minification
缩小在不更改功能的情况下从代码中删除不必要的字符。因此,请求的资产(如 CSS、图像和 JavaScript 文件)的大小大幅减小。缩小的常见副作用包括将变量名称缩短为一个字符、删除注释和不必要的空格。
考虑以下 JavaScript 函数:
AddAltToImg = function (imageTagAndImageID, imageContext) {
///<signature>
///<summary> Adds an alt tab to the image
// </summary>
//<param name="imgElement" type="String">The image selector.</param>
//<param name="ContextForImage" type="String">The image context.</param>
///</signature>
var imageElement = $(imageTagAndImageID, imageContext);
imageElement.attr('alt', imageElement.attr('id').replace(/ID/, ''));
}
缩小将函数缩减为以下内容:
AddAltToImg=function(t,a){var r=$(t,a);r.attr("alt",r.attr("id").replace(/ID/,""))};
除了删除注释和不必要的空格外,还进行了以下参数和变量名称重命名:
原始 | 重命名 |
---|---|
imageTagAndImageID | t |
imageContext | a |
imageElement | r |
捆绑和缩小的影响Impact of bundling and minification
下表概述了单独加载资产与使用捆绑和缩小之间的差异:
操作 | 使用捆绑/缩小 | 不使用捆绑/缩小 | 更改 |
---|---|---|---|
文件请求 | 7 | 18 | 157% |
传输的 KB | 156 | 264.68 | 70% |
加载时间(毫秒) | 885 | 2360 | 167% |
对于 HTTP 请求标头,浏览器非常详细。捆绑时,已发送的总字节数指标明显减少。加载时间显示了显著改进,但本示例在本地运行。将捆绑和缩小与通过网络传输的资产结合使用时,可实现更高的性能提升。
选择捆绑和缩小策略Choose a bundling and minification strategy
MVC 和 Razor Pages 项目模板提供了一种现成的解决方案,可用于由 JSON 配置文件组成的捆绑和缩小。第三方工具(如 Grunt 任务运行程序)以更复杂的方式完成相同的任务。开发工作流需要捆绑和缩小之外的其他处理(如 linting 和图像优化)时,第三方工具非常适用。通过使用设计时捆绑和缩小,在应用部署之前创建缩小文件。在部署之前进行捆绑和缩小具有减少服务器负载的优点。但是,必须认识到,设计时捆绑和缩小会增加生成的复杂性,并且仅适用于静态文件。
配置捆绑和缩小Configure bundling and minification
在 ASP.NET Core 2.0 或更早版本中,MVC 和 Razor Pages 项目模板提供了一个 bundleconfig.json 配置文件,该文件定义每个捆绑的选项 :
在 ASP.NET Core 2.1 或更高版本中,将名为 bundleconfig.json 的新 JSON 文件添加到 MVC 或 Razor Pages 项目根 。在该文件中包含以下 JSON 作为起点:
[
{
"outputFileName": "wwwroot/css/site.min.css",
"inputFiles": [
"wwwroot/css/site.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]
bundleconfig.json 文件定义每个捆绑的选项 。在前面的示例中,为自定义 JavaScript (wwwroot/js/site.js) 和样式表 (wwwroot/css/site.css) 文件定义了单一捆绑配置 。
配置选项包括:
outputFileName
:要输出的捆绑文件的名称。可包含 bundleconfig.json 文件中的相对路径 。(必需)inputFiles
:要捆绑在一起的文件数组。这些是配置文件的相对路径。可以选择使用空值,*这将导致输出文件为空 。支持 glob 模式。minify
:输出类型的缩小选项。可选,默认值 -minify: { enabled: true }
- 每个输出文件类型都有配置选项。
includeInProject
:指示是否将生成的文件添加到项目文件的标记。可选,默认值 - falsesourceMap
:指示是否为捆绑的文件生成源映射的标记。可选,默认值 - falsesourceMapRootPath
:用于存储所生成的源映射文件的根路径。
捆绑和缩小的生成时执行Build-time execution of bundling and minification
BuildBundlerMinifier NuGet 包允许在生成时执行捆绑和缩小。包插入在生成和清理时间运行的 MSBuild 目标。bundleconfig.json 文件由生成过程进行分析,以便基于定义的配置生成输出文件 。
备注
BuildBundlerMinifier 属于 GitHub 上的社区驱动项目,Microsoft 不提供支持。应在此处提交问题。
将 BuildBundlerMinifier 包添加到项目 。
生成项目。“输出”窗口中会显示以下内容:
1>------ Build started: Project: BuildBundlerMinifierApp, Configuration: Debug Any CPU ------
1>
1>Bundler: Begin processing bundleconfig.json
1> Minified wwwroot/css/site.min.css
1> Minified wwwroot/js/site.min.js
1>Bundler: Done processing bundleconfig.json
1>BuildBundlerMinifierApp -> C:\BuildBundlerMinifierApp\bin\Debug\netcoreapp2.0\BuildBundlerMinifierApp.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
清理项目。“输出”窗口中会显示以下内容:
1>------ Clean started: Project: BuildBundlerMinifierApp, Configuration: Debug Any CPU ------
1>
1>Bundler: Cleaning output from bundleconfig.json
1>Bundler: Done cleaning output file from bundleconfig.json
========== Clean: 1 succeeded, 0 failed, 0 skipped ==========
将 BuildBundlerMinifier 包添加到项目 :
dotnet add package BuildBundlerMinifier
如果使用 ASP.NET Core 1.x,则还原新添加的包:
dotnet restore
生成项目:
dotnet build
将显示以下内容:
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Bundler: Begin processing bundleconfig.json
Bundler: Done processing bundleconfig.json
BuildBundlerMinifierApp -> C:\BuildBundlerMinifierApp\bin\Debug\netcoreapp2.0\BuildBundlerMinifierApp.dll
清理项目:
dotnet clean
将显示以下输出:
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Bundler: Cleaning output from bundleconfig.json
Bundler: Done cleaning output file from bundleconfig.json
捆绑和缩小的即席执行Ad hoc execution of bundling and minification
可以在不生成项目的情况下即席运行捆绑和缩小任务。将 BundlerMinifier.Core NuGet 包添加到项目:
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.6.362" />
备注
BundlerMinifier.Core 属于 GitHub 上的社区驱动项目,Microsoft 不提供支持。应在此处提交问题。
此包扩展 .NET Core CLI 以包含 dotnet-bundle 工具 。可以在包管理器控制台 (PMC) 窗口或命令行界面中执行以下命令:
dotnet bundle
重要
NuGet 包管理器将依赖项添加到 .csproj 文件作为 <PackageReference />
节点。仅当使用 <DotNetCliToolReference />
节点时,才使用 .NET Core CLI 注册 dotnet bundle
命令。请相应地修改 .csproj 文件。
向工作流添加文件Add files to workflow
假设添加了额外的 custom.css 文件,类似于以下内容 :
.about, [role=main], [role=complementary] {
margin-top: 60px;
}
footer {
margin-top: 10px;
}
若要缩小 custom.css 并将其与 site.css 捆绑到 site.min.css 文件中,请将相对路径添加到 bundleconfig.json :
[
{
"outputFileName": "wwwroot/css/site.min.css",
"inputFiles": [
"wwwroot/css/site.css",
"wwwroot/css/custom.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]
备注
或者,可以使用以下通配模式:
"inputFiles": ["wwwroot/**/!(*.min).css" ]
此通配模式匹配所有 CSS 文件,并排除缩小的文件模式。
生成应用程序。打开 site.min.css 并注意 custom.css 的内容将追加到文件末尾 。
基于环境的捆绑和缩小Environment-based bundling and minification
最佳做法是,应在生产环境中使用应用的捆绑文件和缩小文件。在开发过程中,原始文件可简化应用的调试。
使用视图中的环境标记帮助程序指定要包含在页面中的文件。环境标记帮助程序仅在特定环境中运行时呈现其内容。
以下 environment
标记将在 Development
环境中运行时呈现未处理的 CSS 文件:
<environment include="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
以下 environment
标记将在非 Development
环境中运行时呈现捆绑的和缩小的 CSS 文件。例如,在 Production
或 Staging
中运行将触发这些样式表的呈现:
<environment exclude="Development">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
从 Gulp 使用 bundleconfig.jsonConsume bundleconfig.json from Gulp
在某些情况下,应用的捆绑和缩小工作流需要额外处理。示例包括图像优化、缓存清除和 CDN 资产处理。为了满足这些要求,可以将捆绑和缩小工作流转换为使用 Gulp。
使用捆绑程序和缩小程序扩展Use the Bundler & Minifier extension
Visual Studio 捆绑程序和缩小程序扩展处理到 Gulp 的转换。
备注
捆绑程序和缩小程序扩展属于 GitHub 上的社区驱动项目,Microsoft 不提供支持。应在此处提交问题。
右键单击解决方案资源管理器中的 bundleconfig.json 文件,然后选择“捆绑程序和缩小程序” > “转换为 Gulp…” :
gulpfile.js 和 package.json 文件已添加到项目中 。已安装 package.json 文件 devDependencies
部分中列出的支持 npm 包 。
在 PMC 窗口中运行以下命令,以将 Gulp CLI 作为全局依赖项安装:
npm i -g gulp-cli
gulpfile.js 文件读取输入、输出和设置的 bundleconfig.json 文件 。
'use strict';
var gulp = require('gulp'),
concat = require('gulp-concat'),
cssmin = require('gulp-cssmin'),
htmlmin = require('gulp-htmlmin'),
uglify = require('gulp-uglify'),
merge = require('merge-stream'),
del = require('del'),
bundleconfig = require('./bundleconfig.json');
// Code omitted for brevity
手动转换Convert manually
如果 Visual Studio 和/或捆绑程序和缩小程序扩展不可用,请手动转换。
将 package.json 文件(包含以下 devDependencies
)添加到项目根 :
警告
gulp-uglify
模块不支持 ECMAScript (ES) 2015/ES6 和更高版本。安装 gulp-terser 而不是 gulp-uglify
来使用 ES2015/ES6 或更高版本。
"devDependencies": {
"del": "^3.0.0",
"gulp": "^4.0.0",
"gulp-concat": "^2.6.1",
"gulp-cssmin": "^0.2.0",
"gulp-htmlmin": "^3.0.0",
"gulp-uglify": "^3.0.0",
"merge-stream": "^1.0.1"
}
通过在与 package.json 相同的级别运行以下命令来安装依赖项 :
npm i
安装 Gulp CLI 作为全局依赖项:
npm i -g gulp-cli
将以下 gulpfile.js 文件复制到项目根 :
'use strict';
var gulp = require('gulp'),
concat = require('gulp-concat'),
cssmin = require('gulp-cssmin'),
htmlmin = require('gulp-htmlmin'),
uglify = require('gulp-uglify'),
merge = require('merge-stream'),
del = require('del'),
bundleconfig = require('./bundleconfig.json');
const regex = {
css: /\.css$/,
html: /\.(html|htm)$/,
js: /\.js$/
};
gulp.task('min:js', async function () {
merge(getBundles(regex.js).map(bundle => {
return gulp.src(bundle.inputFiles, { base: '.' })
.pipe(concat(bundle.outputFileName))
.pipe(uglify())
.pipe(gulp.dest('.'));
}))
});
gulp.task('min:css', async function () {
merge(getBundles(regex.css).map(bundle => {
return gulp.src(bundle.inputFiles, { base: '.' })
.pipe(concat(bundle.outputFileName))
.pipe(cssmin())
.pipe(gulp.dest('.'));
}))
});
gulp.task('min:html', async function () {
merge(getBundles(regex.html).map(bundle => {
return gulp.src(bundle.inputFiles, { base: '.' })
.pipe(concat(bundle.outputFileName))
.pipe(htmlmin({ collapseWhitespace: true, minifyCSS: true, minifyJS: true }))
.pipe(gulp.dest('.'));
}))
});
gulp.task('min', gulp.series(['min:js', 'min:css', 'min:html']));
gulp.task('clean', () => {
return del(bundleconfig.map(bundle => bundle.outputFileName));
});
gulp.task('watch', () => {
getBundles(regex.js).forEach(
bundle => gulp.watch(bundle.inputFiles, gulp.series(["min:js"])));
getBundles(regex.css).forEach(
bundle => gulp.watch(bundle.inputFiles, gulp.series(["min:css"])));
getBundles(regex.html).forEach(
bundle => gulp.watch(bundle.inputFiles, gulp.series(['min:html'])));
});
const getBundles = (regexPattern) => {
return bundleconfig.filter(bundle => {
return regexPattern.test(bundle.outputFileName);
});
};
gulp.task('default', gulp.series("min"));
运行 Gulp 任务Run Gulp tasks
若要在 Visual Studio 中生成项目之前触发 Gulp 缩小任务,请将以下 MSBuild 目标添加到 *.csproj 文件:
<Target Name="MyPreCompileTarget" BeforeTargets="Build">
<Exec Command="gulp min" />
</Target>
在此示例中,MyPreCompileTarget
目标内定义的所有任务在预定义的 Build
目标之前运行。Visual Studio 的输出窗口中显示类似于以下内容的输出:
1>------ Build started: Project: BuildBundlerMinifierApp, Configuration: Debug Any CPU ------
1>BuildBundlerMinifierApp -> C:\BuildBundlerMinifierApp\bin\Debug\netcoreapp2.0\BuildBundlerMinifierApp.dll
1>[14:17:49] Using gulpfile C:\BuildBundlerMinifierApp\gulpfile.js
1>[14:17:49] Starting 'min:js'...
1>[14:17:49] Starting 'min:css'...
1>[14:17:49] Starting 'min:html'...
1>[14:17:49] Finished 'min:js' after 83 ms
1>[14:17:49] Finished 'min:css' after 88 ms
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========