ASP.NET MVC ScriptBundle 檔案順序問題研究

栏目: ASP.NET · 发布时间: 5年前

内容简介:故事從上回的測試發現 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.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

疯长

疯长

[美]肖恩· 阿美拉蒂 / 中信出版集团 / 2018-10 / 45

实现财务回报以及扩大影响力是企业家长期关注和讨论的问题。 为什么有些公司实现了10倍的投资回报,而其他的则勉力支撑?产品类似的公司,为什么有的家喻户晓,有的默默无闻直至退出市场…… 为了了解真相,作者阿美拉蒂在这本书中精选10组对照公司,比如,同为社交平通的Facebook(脸谱网)和Friendster(交友网),同为快餐领域先驱的麦当劳和白色城堡,再比如都在开发电动汽车市场的特斯拉......一起来看看 《疯长》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具