最近在B站上看到楊旭老師的ASP.NET Core 3.x 入門視頻(完結(jié))的第三節(jié)的ASP.NET視頻教程,里面提到到ASP.NET Core 中的捆綁和縮小靜態(tài)資產(chǎn),可以在微軟官方文檔ASP.NET Core 中的捆綁和縮小靜態(tài)資產(chǎn),特此記錄一下,感興趣的可以直接查看官方文檔。
ASP.NET Core 中的捆綁和縮小靜態(tài)資產(chǎn)
2020/09/02
作者:Scott Addie 和 David Pine
本文介紹應(yīng)用捆綁和縮小的好處,包括如何在 ASP.NET Core Web 應(yīng)用中使用這些功能。
什么是捆綁和縮小
捆綁和縮小是可以在 Web 應(yīng)用中應(yīng)用的兩個不同的性能優(yōu)化。 捆綁和縮小一起使用,可減少服務(wù)器的請求數(shù)并減小請求的靜態(tài)資產(chǎn)的大小,從而提高性能。
捆綁和縮小主要縮短第一個頁面請求加載時間。 請求網(wǎng)頁后,瀏覽器會緩存靜態(tài)資產(chǎn)(JavaScript、CSS 和圖像)。 因此,在請求相同資產(chǎn)的同一站點上請求相同的一個或多個頁面時,捆綁和縮小不會提高性能。 如果未在資產(chǎn)上正確設(shè)置 expires 標(biāo)頭,且未使用捆綁和縮小,則瀏覽器的新鮮度啟發(fā)會在幾天后將資產(chǎn)標(biāo)記為過期。 此外,瀏覽器還需要對每個資產(chǎn)進(jìn)行驗證請求。 在這種情況下,即使在第一個頁面請求后,捆綁和縮小仍能提高性能。
捆綁
捆綁將多個文件合并到單個文件中。 捆綁可減少呈現(xiàn) Web 資產(chǎn)(如網(wǎng)頁)所需的服務(wù)器請求數(shù)。 可以專門為 CSS、JavaScript 等創(chuàng)建任意數(shù)量的單個捆綁。文件越少,從瀏覽器到服務(wù)器或從提供應(yīng)用程序的服務(wù)的 HTTP 請求就越少。 這會提高第一頁加載性能。
縮小
縮小在不更改功能的情況下從代碼中刪除不必要的字符。 因此,請求的資產(chǎn)(如 CSS、圖像和 JavaScript 文件)的大小大幅減小。 縮小的常見副作用包括將變量名稱縮短為一個字符、刪除注釋和不必要的空格。
考慮以下 JavaScript 函數(shù):
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/, ''));
}
縮小將函數(shù)縮減為以下內(nèi)容:
AddAltToImg=function(t,a){var r=$(t,a);r.attr("alt",r.attr("id").replace(/ID/,""))};
除了刪除注釋和不必要的空格外,還進(jìn)行了以下參數(shù)和變量名稱重命名:
| 原始 | 重命名 |
|---|---|
imageTagAndImageID |
t |
imageContext |
a |
imageElement |
r |
捆綁和縮小的影響
| 操作 | 使用捆綁/縮小 | 不使用捆綁/縮小 | 更改 |
|---|---|---|---|
| 文件請求 | 7 | 18 | 157% |
| 傳輸?shù)?KB | 156 | 264.68 | 70% |
| 加載時間(毫秒) | 885 | 2360 | 167% |
對于 HTTP 請求標(biāo)頭,瀏覽器非常詳細(xì)。 捆綁時,已發(fā)送的總字節(jié)數(shù)指標(biāo)明顯減少。 加載時間顯示了顯著改進(jìn),但本示例在本地運行。 將捆綁和縮小與通過網(wǎng)絡(luò)傳輸?shù)馁Y產(chǎn)結(jié)合使用時,可實現(xiàn)更高的性能提升。
選擇捆綁和縮小策略
MVC 和 Razor Pages 項目模板提供了一種用于捆綁和縮小的解決方案,它們構(gòu)成 JSON 配置文件。 第三方工具(如 Grunt 任務(wù)運行程序)以更復(fù)雜的方式完成相同的任務(wù)。 開發(fā)工作流需要捆綁和縮小之外的其他處理(如 linting 和圖像優(yōu)化)時,第三方工具非常適用。 通過使用設(shè)計時捆綁和縮小,在應(yīng)用部署之前創(chuàng)建縮小文件。 在部署之前進(jìn)行捆綁和縮小具有減少服務(wù)器負(fù)載的優(yōu)點。 但是,必須認(rèn)識到,設(shè)計時捆綁和縮小會增加生成的復(fù)雜性,并且僅適用于靜態(tài)文件。
配置捆綁和縮小
備注
需要將 BuildBundlerMinifier NuGet 包添加到項目中使其正常工作。
在 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:要捆綁在一起的文件數(shù)組。 這些是配置文件的相對路徑。 可以選擇使用空值,*這將導(dǎo)致輸出文件為空。 支持 glob 模式。 -minify:輸出類型的縮小選項。 可選,默認(rèn)值 - minify: { enabled: true }- 每個輸出文件類型都有配置選項。
- CSS 縮小程序
- JavaScript 縮減程序
-
HTML 縮小程序
-includeInProject:指示是否將生成的文件添加到項目文件的標(biāo)記。 可選,默認(rèn)值 - false
- 每個輸出文件類型都有配置選項。
-
sourceMap:指示是否為捆綁的文件生成源映射的標(biāo)記。 可選,默認(rèn)值 - false -
sourceMapRootPath:用于存儲所生成的源映射文件的根路徑。
向工作流添加文件
假設(shè)添加了額外的 custom.css 文件,類似于以下內(nèi)容:
.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 文件,并排除縮小的文件模式。
生成應(yīng)用程序。 打開 site.min.css 并注意 custom.css 的內(nèi)容將追加到文件末尾 。
基于環(huán)境的捆綁和縮小
最佳做法是,應(yīng)在生產(chǎn)環(huán)境中使用應(yīng)用的捆綁文件和縮小文件。 在開發(fā)過程中,原始文件可簡化應(yīng)用的調(diào)試。
使用視圖中的環(huán)境標(biāo)記幫助程序指定要包含在頁面中的文件。 環(huán)境標(biāo)記幫助程序僅在特定環(huán)境中運行時呈現(xiàn)其內(nèi)容。
以下 environment 標(biāo)記將在 Development環(huán)境中運行時呈現(xiàn)未處理的 CSS 文件:
<environment include="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
以下 environment標(biāo)記將在非 Development 環(huán)境中運行時呈現(xiàn)捆綁的和縮小的 CSS 文件。 例如,在 Production 或 Staging 中運行將觸發(fā)這些樣式表的呈現(xiàn):
<environment exclude="Development">
<link rel="stylesheet"
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.json
在某些情況下,應(yīng)用的捆綁和縮小工作流需要額外處理。 示例包括圖像優(yōu)化、緩存清除和 CDN 資產(chǎn)處理。 為了滿足這些要求,可以將捆綁和縮小工作流轉(zhuǎn)換為使用 Gulp。
手動轉(zhuǎn)換捆綁和縮小工作流以使用 Gulp
將 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 文件復(fù)制到項目根:
'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 任務(wù)
若要在 Visual Studio 中生成項目之前觸發(fā) Gulp 縮小任務(wù):
- 安裝 BuildBundlerMinifier NuGet 包。
- 將以下 MSBuild 目標(biāo)添加到項目文件:
<Target Name="MyPreCompileTarget" BeforeTargets="Build">
<Exec Command="gulp min" />
</Target>
在此示例中,MyPreCompileTarget 目標(biāo)內(nèi)定義的所有任務(wù)在預(yù)定義的 Build 目標(biāo)之前運行。 Visual Studio 的輸出窗口中顯示類似于以下內(nèi)容的輸出:
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 ==========