.net 項目靜態(tài)文件自動壓縮打包
打包工具選型
在 ASP.NET MVC 時代,我們常使用 BundleCollection 設(shè)置需要打包壓縮的 js 和 css 文件,運行時框架會自動處理打包壓縮過程并將最終結(jié)果傳入響應(yīng)。
ASP.NET Core 開始,不再提供內(nèi)置的打包壓縮組件,官方推薦 WebOptimizer 替代使用。
上述兩者都是在運行時實時處理,應(yīng)該至少在系統(tǒng)初始化時會占用一定的資源。
時間來到 .NET 大一統(tǒng)時代,截止目前(.NET7),我們?nèi)匀恢荒軐で笸獠拷M件的幫助。有一個編譯期生成預(yù)期文件的組件 BuildBundlerMinifier,nuget 安裝后進(jìn)行簡單的 JSON 配置即可使用,但遺憾的是,它不支持壓縮包含 ES6 語法的 js,會報未將對象引用設(shè)置到對象的實例錯誤。
于是我們開始考慮獨立于 .NET 平臺的,較為成熟的方案,比如 grunt 和 gulp。
grunt: 使用 JSON 描述構(gòu)建步驟gulp:基于流傳遞(管道機(jī)制)
這兩者都擁有豐富的插件,以應(yīng)對各種需求。下面以更年輕更簡單的 gulp 為例,說明如何使之與 Visual Studio 配合使用。
示例
假設(shè)有如下 .NET 項目,其中按約定靜態(tài)文件存放在 wwwroot 目錄下

安裝 gulp
# 全局安裝 gulp 或 gulp-cli,提供可在命令行執(zhí)行的 gulp 指令
npm install -g gulp-cli
需要注意的是,我們還需要在待處理的目錄下安裝 gulp,否則后續(xù)運行命令時會提示 Local gulp not found. 錯誤。其實這是Gulp故意設(shè)計的,原因是為了版本和依賴的控制,也就是當(dāng)別人 fork 你的代碼,或者你過段時間拷貝到別的電腦上再 gulp 的時候,能控制該項目中 gulp 的版本和其他插件的版本始終保持不變。(這里的 gulp 其實也可看作插件,只是它是官方提供的。博主不懷疑 gulp 團(tuán)隊前向兼容的能力,推測是考慮第三方插件前向兼容的能力參差不齊的緣故。)
# 進(jìn)入待處理的目錄,此是 .net 項目下的一個子目錄
cd wwwroot/
# 將此子目錄初始化為 npm 項目
npm init -y
# 安裝 gulp 插件,提供各種 gulp.xxx()
npm install gulp -D
此時在該目錄下查看 gulp 版本
gulp --version
# 輸出:
CLI version: 2.3.0 # 全局 cli 版本
Local version: 4.0.2 # 當(dāng)前項目下 gulp 插件版本
安裝需要的第三方插件
# 以下各插件功能請查閱相關(guān)資料
npm i -D gulp-cssmin gulp-autoprefixer gulp-uglify
# es6 語法轉(zhuǎn) es5,竊以為目前瀏覽器對 es6 的支持程度,大可不必
npm i -D gulp-babel @babel/core @babel/preset-env
# 提供合并若干文件為單一文件的功能
npm i -D gulp-concat
編寫 gulpfile.js
在項目根目錄下(wwwroot/)新建 gulpfile.js,編寫任務(wù)腳本
// 加載 項目gulp 依賴包
const gulp = require('gulp');
// 加載 css相關(guān)依賴包
const autoprefixer = require('gulp-autoprefixer');
const cssmin = require('gulp-cssmin');
// 加載 js相關(guān)依賴包
const uglify = require('gulp-uglify');
const babel = require('gulp-babel');
// 加載文件合并處理包
const concat = require('gulp-concat');
// 打包 css
const cssHandler = function () {
return gulp.src('./css/main.css')
.pipe(autoprefixer())
.pipe(cssmin())
.pipe(concat('main.min.css'))
.pipe(gulp.dest('./css'))
}
// 打包 js
const jsHandler = function () {
return gulp.src(['./js/instruction/*.js', './js/repos/*.js', './js/app/*.js'])
.pipe(babel({ presets: ['@babel/env'] }))
.pipe(uglify())
.pipe(concat('main.min.js'))
.pipe(gulp.dest('./js'))
}
// 3-3,定義默認(rèn)執(zhí)行程序
module.exports.default = gulp.parallel(cssHandler, jsHandler)
注意,gulp.dest() 參數(shù)表示的是目標(biāo)目錄而非目標(biāo)文件名(輸出文件名默認(rèn)同源文件名),如果要自定義輸出文件名可使用 gulp-rename 插件;或如上使用 gulp-concat,它的目的是合并若干文件為單一文件,自然能指定生成的文件名咯。
完成上述步驟后,我們就可以在命令行執(zhí)行 gulp 檢查輸出結(jié)果。
編譯 .net 項目時自動打包
在 .net 項目根目錄下(注意非 wwwroot/)新建一個批處理文件 package-statics.bat:
cd wwwroot
gulp
然后編輯項目文件(.csproj),在
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="call package-statics.bat" />
</Target>
意思很簡單:在編譯前先執(zhí)行批處理文件,也就是先執(zhí)行之前我們設(shè)置好的 gulp 流程。
如果你的項目采用了 CI/CD 自動構(gòu)建,可能服務(wù)器上并沒有安裝 gulp,那么可以在本地先執(zhí)行打包,將生成后的文件提交,然后在服務(wù)器上編譯時跳過打包步驟,直接使用提交的文件。實現(xiàn)這個場景也很簡單,只要在上述 <Target ... Condition="'$(Configuration)'=='DEBUG'">。參看演練:使用 MSBuild
ps:在頁面中,我們希望開發(fā)時依然引用的是原文件,上線后才引用打包后文件,那么可以使用 <environment> 標(biāo)記,通過判斷環(huán)境變量達(dá)到這一目的:
<environment include="Development">
<link ... href="~/css/main.css" />
</environment>
<environment include="Production">
<link ... href="~/css/main.min.css" />
</environment>

浙公網(wǎng)安備 33010602011771號