高手速來圍觀幫忙解惑~關(guān)于ASP.NET MVC Bundling and RequireJS的取舍問題,最近比較困惑,我希望有一種方式可以結(jié)合兩者的優(yōu)點。作為.NET程序員,難道你沒有過這方面的困惑嗎?

    因為我感覺各自都有優(yōu)缺點,RequireJS的缺點在于,在開發(fā)的時候,你不能引入壓縮后的js或者css,否則無法調(diào)試和修改,而Bundling的話debug模式默認情況下是不壓縮,你一發(fā)布到生產(chǎn)成release就自動壓縮,調(diào)試起來非常方便。RequireJS的優(yōu)點在于可以異步按需加載,還有就是模塊化js代碼,而Bundling 則是簡單粗暴的全部合并成一個文件進行加載,你看不出模塊化引用也實現(xiàn)不了按需加載, 那么在開發(fā)過程中作為.NET程序員是如何取舍的呢?能不能結(jié)合二者的優(yōu)點來使用呢?

    目標:在ASP.NET MVC項目中實現(xiàn)js和css的模塊化,并支持壓縮合并。

    如果你跟我說你還不知道RequireJS是個神馬冬冬?請移步至:http://requirejs.org/docs/api.html

    項目目錄結(jié)構(gòu)沿用上一篇ASP.NET MVC 重寫RazorViewEngine實現(xiàn)多主題切換

方式一 Bunding+RequireJS混用

先來看看一個老外的做法,他大體上是這樣做的:

Bundling部分

App_Start/BundleConfig.cs:

bundles.Add(new ScriptBundle("~/bundles/test").Include(                   "~/Scripts/jquery-{version}.js",                   "~/Scripts/q.js",                   "~/Scripts/globalize.js"));

RequireJS配置部分

在ASP.NET MVC項目中,我們一般是在_Layout母版頁中添加js引用

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

    <script src="~/Scripts/require.js"></script>
    @if (!HttpContext.Current.IsDebuggingEnabled)
    {        <script>
            requirejs.config({
                bundles: {                    '@Scripts.Url("~/bundles/test").ToString()': [                        'jquery',                        'globalize',                        'q']
                }
            });        </script>
    }

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

個人點評:很不優(yōu)雅的實現(xiàn)方式,說好的模塊化呢?而且并沒有提供完整的應(yīng)用程序解決方案。

老外原文地址:ASP.NET MVC Bundling and Minification with RequireJS

方式二 RequireJS.NET

但是隨后我就發(fā)現(xiàn)了一個插件RequireJS.NET

什么是RequireJS.NET?

RequireJS.NET讓每一個C#程序員可以來構(gòu)建JavaScript代碼,不需要具備高級的js編程技能就可以來理解和使用。

在ASP.NET MVC中使用RequireJS的優(yōu)勢:

  • 讓JavaScript代碼更加可復(fù)用

  • 可靠的對象和依賴關(guān)系管理

  • 適用于大型復(fù)雜的應(yīng)用

  • 異步加載JavaScript文件

個人點評:安裝這個安裝那個,而且比較死板,我完全可以自己寫代碼實現(xiàn)它的功能,而且更加靈活,想怎么改怎么改。

RequireJS.NET的使用請參考:Getting started with RequireJS for ASP.NET MVC

我的實現(xiàn)方式

    接下來,我將隆重推出我的實現(xiàn)方式我的做法是:拋棄ASP.NET MVC自帶的Bundling功能,因為它太傻瓜、太粗暴了,但是可以將RequireJS and R.js 很友好的集成在ASP.NET MVC項目中來。雖然RequireJS看上去在單頁應(yīng)用的場景下用起來非常方便,但是在應(yīng)用程序場景下也是同樣適用的,只要你愿意接受它的這種方式。

使用技術(shù): using RequireJS and R.js

目錄結(jié)構(gòu)如下:

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

由于在ASP.NET MVC項目中,有模板頁_Layout.cshtml,那么我可以把一些公用調(diào)用的東西直接放到模板頁中,這里我通過Html的擴展方法進行了封裝

css的調(diào)用:

     <link rel="stylesheet" href="@Html.StylesPath("main.css")" />

js的調(diào)用:

    <script src="@Url.Content("~/themes/default/content/js/require.js")"></script>
    <script>   @Html.ViewSpecificRequireJS()</script>
        @RenderSection("scripts", required: false)

RequireJsHelpers:

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

using System.IO;using System.Text;using System.Web;using System.Web.Mvc;namespace Secom.Emx.WebApp
{    public static class RequireJsHelpers
    {        private static MvcHtmlString RequireJs(this HtmlHelper helper, string config, string module)
        {            var require = new StringBuilder();            string jsLocation = "/themes/default/content/release-js/";#if DEBUG
            jsLocation = "/themes/default/content/js/";#endif

            if (File.Exists(helper.ViewContext.HttpContext.Server.MapPath(Path.Combine(jsLocation, module + ".js"))))
            {
                require.AppendLine("require( [ \"" + jsLocation + config + "\" ], function() {");
                require.AppendLine("    require( [ \"" + module + "\",\"domReady!\"] ); ");
                require.AppendLine("});");
            }            return new MvcHtmlString(require.ToString());
        }        public static MvcHtmlString ViewSpecificRequireJS(this HtmlHelper helper)
        {            var areas = helper.ViewContext.RouteData.DataTokens["area"];            var action = helper.ViewContext.RouteData.Values["action"];            var controller = helper.ViewContext.RouteData.Values["controller"];            string url = areas == null? string.Format("views/{0}/{1}", controller, action): string.Format("views/areas/{2}/{0}/{1}", controller, action, areas);            return helper.RequireJs("config.js", url);
        }        public static string StylesPath(this HtmlHelper helper, string pathWithoutStyles)
        {#if (DEBUG)            var stylesPath = "~/themes/default/content/css/";#else
            var stylesPath =  "~/themes/default/content/release-css/";#endif
            return VirtualPathUtility.ToAbsolute(stylesPath + pathWithoutStyles);
        }
    }
}

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

再來看下我們的js主文件config.js

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

requirejs.config({
    baseUrl: '/themes/default/content/js',
    paths: {        "jquery": "jquery.min",        "jqueryValidate": "lib/jquery.validate.min",        "jqueryValidateUnobtrusive": "lib/jquery.validate.unobtrusive.min",        "bootstrap": "lib/bootstrap.min",        "moment": "lib/moment.min",        "domReady": "lib/domReady",
    },
    shim: {        'bootstrap': {
            deps: ['jquery'],
            exports: "jQuery.fn.popover"
        },        "jqueryValidate": ["jquery"],        "jqueryValidateUnobtrusive": ["jquery", "jqueryValidate"]
    }
});

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 在開發(fā)環(huán)境,我們的css文件肯定不能壓縮合并,不然無法調(diào)試了,而生產(chǎn)環(huán)境肯定是需要壓縮和合并的,那么我想要開發(fā)的時候不合并,一發(fā)布到生產(chǎn)就自動合并

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

那么有兩種方式,一種呢是單獨寫一個批處理腳本,每次發(fā)布到生產(chǎn)的時候就運行一下,一種呢是直接在項目的生成事件中進行配置,如果是debug模式就不壓縮合并,如果是release模式則壓縮合并

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

if $(ConfigurationName) == Release node "$(ProjectDir)themes\default\content\build\r.js" -o "$(ProjectDir)themes\default\content\build\build-js.js"if $(ConfigurationName) == Release node "$(ProjectDir)themes\default\content\build\r.js" -o "$(ProjectDir)themes\default\content\build\build-css.js"

自動構(gòu)建

批處理自動合并壓縮腳本build.bat:

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

@echo off
echo start build js
node.exe r.js -o build-js.js
echo end build js
echo start build css
node.exe r.js -o build-css.js
echo end build css
echo. & pause

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

因為我的js文件是和控制器中的view視圖界面一一對應(yīng)的,那么我需要一個動態(tài)的js構(gòu)建腳本,這里我使用強大的T4模板來實現(xiàn),新建一個文本模板build-js.tt,如果你的VS沒有T4的智能提示,你需要安裝一個VS插件,打開VS——工具——擴展和更新:

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

T4模板代碼如下:

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".js" #>({
    appDir: '<#= relativeBaseUrl #>',
    baseUrl: './',
    mainConfigFile: '<#= relativeBaseUrl #>/config.js',
    dir: '../release-js',
    modules: [
    {
        name: "config",
        include: [            // These JS files will be on EVERY page in the main.js file            // So they should be the files we will almost always need everywhere
            "domReady",            "jquery",            "jqueryValidate",            "jqueryValidateUnobtrusive",            "bootstrap",            "moment"
            ]
    },    <# foreach(string path in System.IO.Directory.GetFiles(this.Host.ResolvePath(relativeBaseUrl+"/views"), "*.js", System.IO.SearchOption.AllDirectories)) { #>{
       name: '<#= GetRequireJSName(path) #>'
    },    <# } #>],
    onBuildRead: function (moduleName, path, contents) {        if (moduleName = "config") {            return contents.replace("/themes/default/content/js","/themes/default/content/release-js")
        }        return contents;
    },
})<#+ 
    public const string relativeBaseUrl = "../js";    public string GetRequireJSName(string path){    var relativePath = Path.GetFullPath(path).Replace(Path.GetFullPath(this.Host.ResolvePath("..\\js\\")), "");    return Path.Combine(Path.GetDirectoryName(relativePath), Path.GetFileNameWithoutExtension(relativePath)).Replace("\\", "/");
} #>

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

通過T4模板生產(chǎn)的構(gòu)建腳本如下:

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

({
    appDir: '../js',
    baseUrl: './',
    mainConfigFile: '../js/config.js',
    dir: '../release-js',
    modules: [
    {
        name: "config",
        include: [            // These JS files will be on EVERY page in the main.js file
            // So they should be the files we will almost always need everywhere
            "domReady",            "jquery",            "jqueryValidate",            "jqueryValidateUnobtrusive",            "bootstrap",            "moment"
            ]
    },
    {
       name: 'views/areas/admin/default/index'
    },
    {
       name: 'views/home/index'
    },
    {
       name: 'views/home/login'
    },
    ],
    onBuildRead: function (moduleName, path, contents) {        if (moduleName = "config") {            return contents.replace("/themes/default/content/js","/themes/default/content/release-js")
        }        return contents;
    },
})

電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 個人點評:靈活性很好,想怎么整怎么整,而且可以很好的支持每日構(gòu)建和持續(xù)集成。

 有時候,總是會在某一剎那間感覺自己就像天才一般,O(∩_∩)O哈哈~,請讓我先自戀一下,因為程序員總是有許多自己的一些想法,如果你覺得我的文章對你有幫助或者啟發(fā),請推薦一下吧!如果有自己的想法也請?zhí)岢鰜?,大家一起探討?/p>

 后記:我本來還想給每js和css的url路徑后綴添加版本號來實現(xiàn)客戶端緩存的自動更新,如?v=1.0,但是后面想了下瀏覽器本身就自帶客戶端緩存,所以就先沒有添加,如果真有需要,可以隨時補上。

博客地址:http://www.cnblogs.com/jiekzou/
博客版權(quán):本文以學(xué)習(xí)、研究和分享為主,歡迎轉(zhuǎn)載,但必須在文章頁面明顯位置給出原文連接。
如果文中有不妥或者錯誤的地方還望高手的你指出,以免誤人子弟。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起討論,共同進步!
再次感謝您耐心的讀完本篇文章。QQ群1:已滿 電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn) QQ群2:313744535 電腦培訓(xùn),計算機培訓(xùn),平面設(shè)計培訓(xùn),網(wǎng)頁設(shè)計培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

我的拙作《ASP.NET MVC企業(yè)級實戰(zhàn)》已經(jīng)出版,希望大家多多支持!

http://www.cnblogs.com/jiekzou/p/7089781.html