内容简介:故事從上回的測試發現 ScriptBundle 版 DateReviver 注入失效,追查問題出在既然 ASP.NET MVC 已完全開源,查原始碼找答案才是男子漢! 爬文找到 ScriptBundle 依靠 IBundleOrderer 決定檔案順序,而預設的 IBundleOrderer 是 DefaultBundleOrderer,追進
故事從上回的 讓 JSON.parse() 內建日期解析小把戲
說起,我提到將置換 JSON.parse 動作放在載入 jquery.js 之前,jQuery.ajax 便會內建將 2012-12-21T00:00:00Z
轉成 Date 型別的能力。寫完程式雛型進行重構時,我將 CSS、JavaScript 用 ASP.NET ScriptBundle 機制
打包壓縮,依直覺會寫成:
bundles.Add( new ScriptBundle("~/bundles/site") .Include( "~/Scripts/addDateReviver.js", "~/Scripts/jquery-{version}.js" ));
測試發現 ScriptBundle 版 DateReviver 注入失效,追查問題出在 @Scripts.Render("~/bundles/site")
將 jquery.js 放在 addDateReviver.js 之前,而不是依據我 Include 的順序。
<script src="/Scripts/jquery-3.3.1.js"></script> <script src="/Scripts/addDateReviver.js"></script>
既然 ASP.NET MVC 已完全開源,查原始碼找答案才是男子漢! 爬文找到 ScriptBundle 依靠 IBundleOrderer 決定檔案順序,而預設的 IBundleOrderer 是 DefaultBundleOrderer,追進 DefaultBundleOrderer.cs 原始碼 ,找到以下影響檔案順序的邏輯:
// Goal is to return a list of files in FileSetOrderList where each registered file set is ordered if it exists List<BundleFile> result = new List<BundleFile>(); List<BundleFile> fileList = new List<BundleFile>(files); Dictionary<string, HashSet<BundleFile>> fileMap = BuildFileMap(fileList); if (fileMap.Count == 0) { return result; } HashSet<VirtualFile> foundFiles = new HashSet<VirtualFile>(VirtualFileComparer.Instance); // For each ordering if we find a file, output all files that match that name foreach (BundleFileSetOrdering ordering in context.BundleCollection.FileSetOrderList) { AddOrderingFiles(ordering, fileList, fileMap, foundFiles, result); } // Last step, add all unused files to the final list foreach (BundleFile f in fileList) { if (!foundFiles.Contains(f.VirtualFile)) { result.Add(f); foundFiles.Add(f.VirtualFile); } } return result;
由程式碼可知 DefaultBundleOrderer 會先依 BundleCollection.FileSetOrderList 巡過一輪放入檔案,剩下的再依當初放入順序排列,而 BundleCollection.FileSetOrderList 的內容由 BundleCollection.AddDefaultFileOrderings() 決定,它列舉了 jquery、modernizr、dojo、mootools、prototype、ext... 等常用程式庫,確保它們會優先載入並依照預先定義的順序排序。(以下取自 AddDefaultFileOrderings() API 文件說明)
/// The default ordering values are as follows: /// <list type="bullet"> /// <item><description>reset.css</description></item> /// <item><description>normalize.css</description></item> /// <item><description>jquery.js</description></item> /// <item><description>jquery-min.js</description></item> /// <item><description>jquery-*</description></item> /// <item><description>jquery-ui*</description></item> /// <item><description>jquery.ui*</description></item> /// <item><description>jquery.unobtrusive*</description></item> /// <item><description>jquery.validate*</description></item> /// <item><description>modernizr-*</description></item> /// <item><description>dojo.*</description></item> /// <item><description>mootools-core*</description></item> /// <item><description>mootools-*</description></item> /// <item><description>prototype.js</description></item> /// <item><description>prototype-*</description></item> /// <item><description>scriptaculous-*</description></item> /// <item><description>ext.js</description></item> /// <item><description>ext-*</description></item> /// </list>
看完 ASP.NET MVC 原始碼,真相大白。ScriptBundle 會優先載入常用程式庫,並依事先定義的順序排列。不信的話,我們來惡搞一下,故意將 jquery.ui.js 放在 jquery.js 前面,ext.js 本應排在 prototype.js 之後,我們也將它移到 jquery.js 之前:
bundles.Add( new ScriptBundle("~/bundles/site") .Include( "~/Scripts/addDateReviver.js", "~/Scripts/ext.js", "~/Scripts/jquery.ui.js", "~/Scripts/jquery-{version}.js", "~/Scripts/prototype.js" ));
執行結果顯示,不論怎麼亂搞,FileSetOrderList 都會確保上述的程式庫依序優先載入,其他 JavaScript 則排在後面:
<script src="/Scripts/jquery-3.3.1.js"></script> <script src="/Scripts/jquery.ui.js"></script> <script src="/Scripts/prototype.js"></script> <script src="/Scripts/ext.js"></script> <script src="/Scripts/addDateReviver.js"></script>
回到一開始遇到問題,要解決不難。第一種做法是把 addDateReviver.js 移出該組 ScriptBundle 提前轉入,另一種做法則是自訂依 Include() 加入順序 排序 的 IBundleOrderer,參考 Stackoverlow 討論 ,使用 ForceOrdered() 擴充方法把 DefaultBundleOrderer 換成依 Include() 順序出貨,也是不錯的解法。
internal class AsIsBundleOrderer : IBundleOrderer { public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files) { return files; } } internal static class BundleExtensions { public static Bundle ForceOrdered(this Bundle sb) { sb.Orderer = new AsIsBundleOrderer(); return sb; } } public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add( new ScriptBundle("~/bundles/site") .Include( "~/Scripts/addDateReviver.js", "~/Scripts/jquery-{version}.js" ).ForceOrdered()); //...以下省略...
Reveal the principle of ScriptBundle DefaultBundleOrderer and how to customize it.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 安全研究 | 传真机的攻击面研究报告
- IBM将建新AI研究中心,旨在开展“颠覆性研究项目”
- 清华人工智能研究院成立「知识智能研究中心」,发布四大知识平台
- 姚班天才少年鬲融凭非凸优化研究成果获得斯隆研究奖
- 大热下的 GNN 研究面临哪些“天花板”?未来的重点研究方向又在哪?
- 中国电信广州研究院与思博伦通信共同推动智能化承载网络测试技术研究
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。