您好,登錄后才能下訂單哦!
由于默認(rèn)的 ASP.NET MVC 模板使用了 Bundle 技術(shù),大家開始接受并喜歡上這種技術(shù)。Bundle 技術(shù)通過 Micorosoft.AspNet.Web.Optimization 包實(shí)現(xiàn),如果在 ASP.NET WebForm 項(xiàng)目中引入這個(gè)包及其依賴包,在 ASP.NET WebForm 項(xiàng)目中使用 Bundle 技術(shù)也非常容易。
關(guān)于在 WebForm 中使用 Bundle 技術(shù)的簡短說明
通過 NuGet 很容易在 WebForm 項(xiàng)目中引入
Microsoft.AspNet.Web.Optimization
包及其依賴包。不過在 MVC 項(xiàng)目的 Razor 頁面中可以使用類似下面的語句引入資源@Scripts.Render("...")而在
*.aspx
頁面中則需要通過<%= %>
來引入了:<%@ Import Namespace="System.Web.Optimization" %> // ... <%= Scripts.Render("...") %>備注 有些資料中是使用的
<%: %>
,我實(shí)在沒有發(fā)現(xiàn)它和<%= %>
有啥區(qū)別,但至少我在《ASP.NET Reference》的《Code Render Blocks》一節(jié)找到了<%= %>
,卻暫時(shí)沒在官方文檔里找到<%: %>
然后,我在一個(gè)使用了 EasyUI 的項(xiàng)目中使用了 Bundle 技術(shù)。才開始一切正常,至到第一個(gè) Release 版本測(cè)試的那一天,“血案”發(fā)生了——
由于一個(gè)腳本錯(cuò)誤,EasyUI 沒有生效。最終原因是 Bunlde 在 Release 版中將 EasyUI 的腳本壓縮了——當(dāng)然,定位到這個(gè)原因還是經(jīng)歷了一翻周折,這就不細(xì)說了。
這個(gè)解決方案理論上只需要在配置里加一句話就行:
BundleTable.EnableOptimizations = false;
但問題在于,這樣一來,為了一個(gè) EasyUI,就放棄了所有腳本的壓縮,而僅僅只是合并,效果折半,只能當(dāng)作萬不得已的備選。
先看看原本的 Bundle 配置(已簡化)
public static void Register(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/libs") .Include("~/scripts/jquery-{version}.js") .Include("~/scripts/jquery.eaysui-{versoin}.js") .Include("~/scripts/locale/easyui-lang-zh_CN.js") .IncludeDirectory("~/scripts/app", "*.js", true) ); }
這段配置先引入了 jquery,再引入了 easyui,最后引入了一些為當(dāng)前項(xiàng)目寫的公共腳本。為了實(shí)現(xiàn)解決方案二,必須要改成分三個(gè) Bundle 引入,同時(shí)還得想辦法阻止壓縮其中一個(gè) Bundle。
要分段,簡單
public static void Register(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/jquery") .Include("~/scripts/jquery-{version}.js") ); bundles.Add(new ScriptBundle("~/easyui") .Include("~/scripts/jquery.eaysui-{versoin}.js") .Include("~/scripts/locale/easyui-lang-zh_CN.js") ); bundles.Add(new ScriptBundle("~/libs") .IncludeDirectory("~/scripts/app", "*.js", true) ); }
但為了阻止壓縮,查了文檔,也搜索了不少資料都沒找到解決辦法,所以只好看源碼分析了,請(qǐng)出 JetBrains dotPeek。分析代碼之后得出結(jié)論,只需要去掉默認(rèn)的 Transform 就行
// bundles.Add(new ScriptBundle("~/easyui") // .Include("~/scripts/jquery.eaysui-{versoin}.js") // .Include("~/scripts/locale/easyui-lang-zh_CN.js") // ); Bundle easyuiBundle = new ScriptBundle("~/easyui") .Include("~/scripts/jquery.eaysui-{versoin}.js") .Include("~/scripts/locale/easyui-lang-zh_CN.js") ); easyuiBundle.Transforms.Clear(); bundles.Add(easyuiBundle);
關(guān)鍵代碼的分析說明
首先從 ScriptBunlde 入手
public class ScriptBundle: Bundle { public ScriptBundle(string virtualPath) : this(virtualPath, (string) null) {} public ScriptBundle(string virtualPath, string cdnPath) : base(virtualPath, cdnPath, (IBundleTransform) new JsMinify() ) { this.ConcatenationToken = ";" + Environment.NewLine; } }
可以看出,ScriptBunlde 的構(gòu)建最終是通過其基類 Bunlde 中帶 IBunldeTransform 參數(shù)的那一個(gè)來構(gòu)造的。再看 Bunlde 的關(guān)鍵代碼
public class Bunlde public IList<IBundleTransform> Transforms { get { return this._transforms; } } public Bundle( string virtualPath, string cdnPath, params IBundleTransform[] transforms ) { // ... foreach(IBundleTransform bundleTransform in transforms) { this._transforms.Add(bundleTransform); } } }
容易理解,ScriptBunlde 構(gòu)建的時(shí)候往 Transforms 中添加了一默認(rèn)的 Transform——JsMinify,從名字就可以看出來,這是用來壓縮腳本的。而 IBundleTransform 只有一個(gè)接口方法
public interface IBundleTransform { void Process(BundleContext context, BundleResponse response); }
看樣子它是在處理 BundleResponse。而 BundleResponse 中定義有文本類型的 Content 和 ContentType 屬性,以及一個(gè) IEnumerable<BundleFile> Files。
為什么是 Files 而不是 File 呢,我猜 Content 中包含的是一個(gè) Bundle 中所有文件的內(nèi)容,而不是某一個(gè)文件的內(nèi)容。要驗(yàn)證也很容易,自己實(shí)現(xiàn)個(gè) IBundleTransform 試下就行了
Bundle b = new ScriptBundle("~/test") .Include(...) .Include(...); b.Transforms.Clear();b.Transforms.Add(new MyTransform()) // MyTransform 可以自由發(fā)揮,我其實(shí)啥都沒寫,只是在 Process 里打了個(gè)斷點(diǎn),檢查了 response 的屬性值而已
實(shí)驗(yàn)證明在 BundleResponse 傳入 Transforms 之前,其 Content 就已經(jīng)有所有引入文件的內(nèi)容了。
方案二解決了方案一不能解決的問題,但同時(shí)也帶來了新問題。原來只需要一句話就能引入所有腳本
@Scripts.Render("~/libs")
而現(xiàn)在需要 3 句話
@Scripts.Render("~/jquery") @Scripts.Render("~/easyui") @Scripts.Render("~/libs")
鑒于方案二帶來的新問題,試想,如果有一個(gè)東西,能把 3 個(gè) Bundle 對(duì)象組合起來,變成一個(gè) Bundle 對(duì)象,豈不是就解決了?
于是,我發(fā)明了 Bundle 的 Bundle,不妨就叫 BundleBundle 吧。
public class BundleBundle : Bundle{ readonly List<Bundle> bundles = new List<Bundle>(); public BundleBundle(string virtualPath) : base(virtualPath) { } public BundleBundle Include(Bundle bundle) { bundles.Add(bundle); return this; } // 在引入 Bundle 對(duì)象時(shí)申明清空 Transforms,這幾乎就是為 EasyUI 準(zhǔn)備的 public BundleBundle Include(Bundle bundle, bool isClearTransform) { if (isClearTransform) { bundle.Transforms.Clear(); } bundles.Add(bundle); return this; } public override BundleResponse GenerateBundleResponse(BundleContext context) { List<BundleFile> allFiles = new List<BundleFile>(); StringBuilder content = new StringBuilder(); string contentType = null; foreach (Bundle b in bundles) { var r = b.GenerateBundleResponse(context); content.Append(r.Content); // 考慮到 BundleBundle 可能用于 CSS,所以這里進(jìn)行一次判斷, // 只在 ScriptBundle 后面加分號(hào)(兼容 ASI 風(fēng)格腳本) // 這里可能會(huì)出現(xiàn)在已有分號(hào)的代碼后面加分號(hào)的情況, // 考慮到只會(huì)浪費(fèi) 1 個(gè)字節(jié),忍了 if (b is ScriptBundle) { content.Append(';'); } content.AppendLine(); allFiles.AddRange(r.Files); if (contentType == null) { contentType = r.ContentType; } } var response = new BundleResponse(content.ToString(), allFiles); response.ContentType = contentType; return response; } }
使用 BundleBundle 也簡單,就像這樣
bundles.Add(new BundleBundle("~/libs") .Include(new ScriptBundle("~/bundle/jquery") .Include("~/scripts/jquery-{version}.js") ) .Include( new ScriptBundle("~/bundle/easyui") .Include("~/scripts/jquery.easyui-{version}.js") .Include("~/scripts/locale/easyui-lang-zh_CN.js") ) .Include(new ScriptBundle("~/bundle/app") .IncludeDirectory("~/scripts/app", "*.js", true) ) );
然后
@Scripts.Render("~/libs")
注意,每個(gè)子 Bundle 都有名字,但這些名字不能直接給 @Scripts.Render() 使用,因?yàn)樗鼈儾]有直接加入 BundleTable.Bundles 中。但名字是必須的,而且不能是 null,不信就試試。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。