首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET 效能優化:JS/CSS 合併壓縮與快取 HttpHandler 實作

     編輯:ASP.NET 基礎

優化目標

  • **減少 HTTP 請求數**:將多個 JS/CSS 合併為單一請求。
  • **減少傳輸體積**:移除空白、註解、縮短變數名 (Minify)。
  • **善用瀏覽器快取**:ETag、Last-Modified、Cache-Control、Expires 標頭。

架構設計

  1. 建立 `IHttpHandler` (`CombineFiles.ashx`),接收 `type=js|css` 參數。
  2. 讀取指定目錄下所有對應副檔名檔案,內容合併。
  3. 使用 **Yahoo YUI Compressor for .NET** (`NuGet: Yahoo.Yui.Compressor`) 進行 Minify。
  4. 結果存入 `HttpRuntime.Cache` (伺服器端快取),設定過期時間。
  5. 支援條件式請求 (`If-Modified-Since` / `ETag`),返回 `304 Not Modified`。
  6. 輸出正確 `Content-Type` 並設定客戶端快取標頭。

核心 Handler 代碼

using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Caching;
using Yahoo.Yui.Compressor;

public class CombineFiles : IHttpHandler
{
    private const string CacheKeyFormat = "_CombineCache_{0}_";
    private const bool EnableCompress = true;

    public bool IsReusable => false;

    public void ProcessRequest(HttpContext context)
    {
        var request = context.Request;
        var response = context.Response;

        string type = request.QueryString["type"]; // "js" 或 "css"
        if (string.IsNullOrEmpty(type) || (type != "js" && type != "css"))
        {
            response.StatusCode = 400;
            response.End();
            return;
        }

        // 設定正確 MIME
        response.ContentType = type == "js" ? "application/javascript" : "text/css";

        string cacheKey = string.Format(CacheKeyFormat, type);
        var cacheItem = HttpRuntime.Cache[cacheKey] as CacheItem;

        if (cacheItem == null)
        {
            // 首次建立:掃描目錄、合併、壓縮
            string dirPath = context.Server.MapPath(type == "js" ? "~/Scripts" : "~/Content");
            if (!Directory.Exists(dirPath))
            {
                response.StatusCode = 404;
                response.End();
                return;
            }

            var sb = new StringBuilder();
            foreach (string file in Directory.GetFiles(dirPath, "*." + type, SearchOption.TopDirectoryOnly))
            {
                string content = File.ReadAllText(file, Encoding.UTF8);
                sb.AppendLine(content);
            }

            string combined = sb.ToString();

            // 壓縮
            if (EnableCompress)
            {
                if (type == "js")
                    combined = JavaScriptCompressor.Compress(combined);
                else
                    combined = CssCompressor.Compress(combined);
            }

            var expires = DateTime.Now.AddDays(30);
            cacheItem = new CacheItem { Content = combined, Expires = expires, Type = type };
            HttpRuntime.Cache.Insert(cacheKey, cacheItem, null, expires, Cache.NoSlidingExpiration);
        }

        // 條件式請求支援
        string ifModifiedSince = request.Headers["If-Modified-Since"];
        if (!string.IsNullOrEmpty(ifModifiedSince) &&
            DateTime.TryParse(ifModifiedSince, out DateTime clientDate) &&
            cacheItem.Expires <= clientDate)
        {
            response.StatusCode = 304;
            response.StatusDescription = "Not Modified";
            response.End();
            return;
        }

        // 輸出內容
        response.Write(cacheItem.Content);
        SetClientCaching(response, cacheItem.Expires);
    }

    private void SetClientCaching(HttpResponse response, DateTime expires)
    {
        response.Cache.SetETag(DateTime.Now.Ticks.ToString());
        response.Cache.SetLastModified(DateTime.Now);
        response.Cache.SetCacheability(HttpCacheability.Public);
        response.Cache.SetMaxAge(TimeSpan.FromDays(30));
        response.Cache.SetSlidingExpiration(true);
        response.Cache.SetExpires(expires);
    }

    private class CacheItem
    {
        public string Type { get; set; }
        public string Content { get; set; }
        public DateTime Expires { get; set; }
    }
}

Web.config 註冊 (IIS 7+ Integrated Mode)

<system.webServer>
  <handlers>
    <add name="CombineFiles" verb="GET" path="CombineFiles.axd" type="YourNamespace.CombineFiles, YourAssembly" preCondition="integratedMode" />
  </handlers>
</system.webServer>

前端引用方式

<!-- 頁面頭部 -->
<link rel="stylesheet" href="/CombineFiles.axd?type=css" />

<!-- 頁面底部 -->
<script src="/CombineFiles.axd?type=js"></script>

進階優化建議

優化點說明實作提示
檔案排序/依賴管理jQuery 必須在插件前載入維護 `manifest.json` 列出載入順序,Handler 依序讀取
版本指紋部署新版需刷新客戶端快取URL 加入 `?v=20240115` 或路徑包含 hash `/CombineFiles.axd?v=abc123`
GZIP/Br 壓縮傳輸再壓縮 70%+IIS 動態壓縮模組自動處理,或 Handler 手動 `GZipStream`
CDN 整合靜態資源卸載至 CDNHandler 僅處理專案私有資源,第三方庫走 CDN
Source Map除錯時對應原始碼YUI Compressor 可輸出 `.map`,開發環境關閉壓縮
非同步載入不阻塞頁面渲染`
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved