對於在 ASP.NET MVC 中壓縮及打包 JavaScript/CSS,我的觀念還停留在 10 年前的 ASP.NET MVC ScriptBundle,或是部落格用到的第三方程式庫 LigerShark.WebOptimizer

時間來到 2025 年,前端 CSS/JavaScript 規格跟工具已不知翻過幾輪,是該時候評估學習新工具及新做法了。

我先查了 ASP.NET Core 官方文件,看看官方是否有推出現代化武器。很不幸地,ASP.NET Core 並未內建打包壓縮(Bundling and Minification)工具,只建議了兩種做法:

  1. LigerShar.WebOptimizer 開源程式庫,文章開頭有提到我部落格在用的那個
  2. 借用 Gulp / Webpack 等前端工作流程

ASP.NET Core 在這部分支援薄弱倒也不難理解,這些年前端框架 React、Vue、Angular 都有一套成熟的編譯壓縮打包機制,像我還在寫 JavaScript 從 HTML <script src=""> 載入的輕前端玩家畢竟是少數,市場太小不值得投入資源開發工具,資源與支援自然就少,選了非主流就得自立自強,缺了什麼要自己補上。

除非萬不得已我不想扯上 Gulp/WebPack 讓專案維護複雜化,還是想堅守 WebOptimizer 的簡單輕巧解法。WebOptimizer 為執行階段動態壓縮打包,JS/CSS 是靜態檔,壓縮打包動作其實在編譯或部署時做一次就夠,沒必要每次啟動重跑,我找到 WebOptimizer 作者 Mads Kristensen 的另一個作品 - BuildBundlerMinifier,是 WebOptimizer 的靜態編譯版。

使用方法很簡單,專案 dotnet add package BuildBundlerMinifier 加入參照,在專案根目錄加入 bundleconfig.json 設定壓縮打包對象:

[
  {
    "outputFileName": "wwwroot/js/bundle.min.js",
    "inputFiles": [
      "wwwroot/lib/vue/vue.global.prod.min.js",
      "wwwroot/lib/sweetalert2/sweetalert2.all.min.js",
      "wwwroot/js/app.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

執行 dotnet build,「順利的話」便會生出壓縮打包後的 bundle.min.js。

沒錯,「順利的話」代表事情沒那麼單純。實測發現,不管 WebOptimizer 或 BuildBundlerMinifier 都存在版本偏舊不支援現代 JavaScript 語法的問題。以上的設定在 minify enabled: true 時會出錯,改為 false 只合併檔案則沒什麼問題。

舉例來說,以下這段常見的 JavaScript 寫法:

const app = Vue.createApp({
    data() {
        return {
            message: 'Hello Vue!'
        }
    },
    methods: {
        showAlert() {
            Swal.fire({
                title: 'SweetAlert Example',
                text: this.message,
                icon: 'success',
                confirmButtonText: 'OK',
                animation: false
            });
        }
    }
});
const vm = app.mount('#app');

WebOptimizer 進行 Minify 壓縮時會出現以下錯誤:

細究原因,BundlerMinifier 底層靠 NUglify 程式庫壓縮 JavaScript、CSS 及 HTML 檔案,而這個有點年紀的開源專案源自更古老的 Microsoft Ajax Minifier,其 JS 語法標準只支援到 ES2021,距今已快四年(註 ECMAScript 最新標準 ES2024 已於 2024 六月發佈),故 2022 之後的新版程式庫一旦用到 ES2022、ES2023 甚至 ES2024 的新語法,轟!

由於 JavaScript 語法規格更新飛快,IE 死後 Chrome 幾已一統江湖(Edge 也是 Chromium 核心),且主流瀏覽器都會持續自動更新到最新版,前端開發界採納新規格少了很多顧忌,引起新語法沒在客氣的,想跟上腳步只能回歸主流前端工具,於是我想到了 UglifyJS

我目前找到的 Workaround 是寫一支 PowerShell 程式 Minify.ps1,讀取 Minify.csv 設定要壓縮的 .js。專案目錄要先用 npm install uglify-js 裝好 UglifyJS,編譯過程 Minify.ps1 會讀取 Minify.csv 一一呼叫 UglifyJS 進行壓後存成 min.js,之後再交給 BundlerMinifier 打包。

PowerShell 程式如下:

$ErrorActionPreference = "Stop"
$uglifyJsPath = "$PSScriptRoot\node_modules\.bin\uglifyjs.ps1"
if (-not (Test-Path $uglifyJsPath)) {
    Write-Host "uglifyjs.ps1 not found. Please run npm install uglify-js" -ForegroundColor Red
    exit 1
}
Get-Content $PSScriptRoot\minify.csv | ForEach-Object {
    $file = $_
    $ext = [System.IO.Path]::GetExtension($file)
    if (Test-Path $file) {
        & "$uglifyJsPath" $file -o ([System.IO.Path]::ChangeExtension($file, ".min" + $ext))
    }
}

最後,在 csproj 設定每次編譯前執行 Minify.ps1:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <RootNamespace>js_bundle</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
    <PackageReference Include="LigerShark.WebOptimizer.Core" Version="3.0.433" />
  </ItemGroup>
  
  <Target Name="Minify JavaScript" BeforeTargets="BeforeCompile">
    <Exec Command="pwsh &quot;$(ProjectDir)Minify.ps1&quot;"></Exec>
  </Target>

</Project>

實測設定成 BeforeTargets="BeforeCompile" Minify.ps1 會固定在 BundleMinifier 之前執行,產生好 min.js 給 BundleMinifier 打包,bundleconfig.json 要設定成 minify enabled: false 只打包不壓縮,如此便可克服 BundleMinifier 不支援新 JavaScript 語法的問題。如果要改完 .js 立即看結果,可以刪除打包輸出的 min.js,BundleMinifier 會自動觸發連帶先跑 Minify.ps1,達成即改即看的效果。

就醬,搬開輕前端之路上的小石頭繼續前進,分享給也在這條路上的伙伴。

後記,這回有復習用 libman 下載安裝 cdnjs 上的 JavaScript 程式庫,筆記一下指令:

libman install vue --provider cdnjs --files vue.global.prod.min.js --destination wwwroot/lib/vue
libman install sweetalert2 --provider cdnjs --files sweetalert2.all.min.js --destination wwwroot/lib/sweetalert2
libman install highlight.js --provider cdnjs --files styles/vs2015.min.css --files highlight.min.js --destination wwwroot/lib/highlight.js    

In 2025, front-end development tools have evolved, and traditional ASP.NET MVC bundling methods are outdated. ASP.NET Core lacks built-in bundling/minification tools, but options include LigerShark.WebOptimizer and front-end tools like Gulp/Webpack. For modern JavaScript syntax, use UglifyJS with a PowerShell script for reliable minification, integrated into your build process.


Comments

Be the first to post a comment

Post a comment