最近在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 ==========