方式一 Bunding+RequireJS混用

先来看看一个老外的做法,他大体上是这样做的:

Bundling部分

App_Start/BundleConfig.cs:

bundles.Add(newScriptBundle("~/bundles/test").Include("~/Scripts/jquery-{version}.js","~/Scripts/q.js","~/Scripts/globalize.js"));RequireJS配置部分

在ASP.NET MVC项目中,我们一般是在_Layout母版页中添加js引用

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

个人点评:很不优雅的实现方式,说好的模块化呢?而且并没有提供完整的应用程序解决方案。

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

方式二 RequireJS.NET

但是随后我就发现了一个插件RequireJS.NET

什么是RequireJS.NET?

RequireJS.NET让每一个C#程序员可以来构建JavaScript代码,不需要具备高级的js编程技能就可以来理解和使用。

在ASP.NET MVC中使用RequireJS的优势:

让JavaScript代码更加可复用

可靠的对象和依赖关系管理

适用于大型复杂的应用

异步加载JavaScript文件

个人点评:安装这个安装那个,而且比较死板,我完全可以自己写代码实现它的功能,而且更加灵活,想怎么改怎么改。

RequireJS.NET的使用请参考:Getting started with RequireJS for ASP.NET MVC

我的实现方式

接下来,我将隆重推出我的实现方式。我的做法是:抛弃ASP.NET MVC自带的Bundling功能,因为它太傻瓜、太粗暴了,但是可以将RequireJS and R.js很友好的集成在ASP.NET MVC项目中来。虽然RequireJS看上去在单页应用的场景下用起来非常方便,但是在应用程序场景下也是同样适用的,只要你愿意接受它的这种方式。

使用技术: using RequireJS and R.js

目录结构如下:

由于在ASP.NET MVC项目中,有模板页_Layout.cshtml,那么我可以把一些公用调用的东西直接放到模板页中,这里我通过Html的扩展方法进行了封装

css的调用:

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

js的调用:

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

RequireJsHelpers:

usingSystem.IO;usingSystem.Text;usingSystem.Web;usingSystem.Web.Mvc;namespaceSecom.Emx.WebApp{publicstaticclassRequireJsHelpers{privatestaticMvcHtmlStringRequireJs(thisHtmlHelperhelper,stringconfig,stringmodule){varrequire=newStringBuilder();stringjsLocation="/themes/default/content/release-js/";#ifDEBUGjsLocation="/themes/default/content/js/";#endifif(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("});");}returnnewMvcHtmlString(require.ToString());}publicstaticMvcHtmlStringViewSpecificRequireJS(thisHtmlHelperhelper){varareas=helper.ViewContext.RouteData.DataTokens["area"];varaction=helper.ViewContext.RouteData.Values["action"];varcontroller=helper.ViewContext.RouteData.Values["controller"];stringurl=areas==null?string.Format("views/{0}/{1}",controller,action):string.Format("views/areas/{2}/{0}/{1}",controller,action,areas);returnhelper.RequireJs("config.js",url);}publicstaticstringStylesPath(thisHtmlHelperhelper,stringpathWithoutStyles){#if(DEBUG)varstylesPath="~/themes/default/content/css/";#elsevarstylesPath="~/themes/default/content/release-css/";#endifreturnVirtualPathUtility.ToAbsolute(stylesPath+pathWithoutStyles);}}}

再来看下我们的js主文件config.js

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"]}});

在开发环境,我们的css文件肯定不能压缩合并,不然无法调试了,而生产环境肯定是需要压缩和合并的,那么我想要开发的时候不合并,一发布到生产就自动合并

那么有两种方式,一种呢是单独写一个批处理脚本,每次发布到生产的时候就运行一下,一种呢是直接在项目的生成事件中进行配置,如果是debug模式就不压缩合并,如果是release模式则压缩合并

if$(ConfigurationName)==Releasenode"$(ProjectDir)themes\default\content\build\r.js"-o"$(ProjectDir)themes\default\content\release-js\build-js.js"if$(ConfigurationName)==Releasenode"$(ProjectDir)themes\default\content\build\r.js"-o"$(ProjectDir)themes\default\content\release-css\build-css.js"自动构建

批处理自动合并压缩脚本build.bat:

@echooffechostartbuildjsnode.exer.js-obuild-js.jsechoendbuildjsechostartbuildcssnode.exer.js-obuild-css.jsechoendbuildcssecho.&pause

因为我的js文件是和控制器中的view视图界面一一对应的,那么我需要一个动态的js构建脚本,这里我使用强大的T4模板来实现,新建一个文本模板build-js.tt,如果你的VS没有T4的智能提示,你需要安装一个VS插件,打开VS——工具——扩展和更新:

T4模板代码如下:

<#@templatedebug="false"hostspecific="true"language="C#"#><#@assemblyname="System.Core"#><#@importnamespace="System.Linq"#><#@importnamespace="System.IO"#><#@importnamespace="System.Configuration"#><#@importnamespace="System.Text"#><#@importnamespace="System.Collections.Generic"#><#@outputextension=".js"#>({appDir:'<#=relativeBaseUrl#>',baseUrl:'./',mainConfigFile:'<#=relativeBaseUrl#>/config.js',dir:'../release-js',modules:[{name:"config",include:[//TheseJSfileswillbeonEVERYpageinthemain.jsfile//Sotheyshouldbethefileswewillalmostalwaysneedeverywhere"domReady","jquery","jqueryValidate","jqueryValidateUnobtrusive","bootstrap","moment"]},<#foreach(stringpathinSystem.IO.Directory.GetFiles(this.Host.ResolvePath(relativeBaseUrl+"/views"),"*.js",System.IO.SearchOption.AllDirectories)){#>{name:'<#=GetRequireJSName(path)#>'},<#}#>],onBuildRead:function(moduleName,path,contents){if(moduleName="config"){returncontents.replace("/themes/default/content/js","/themes/default/content/release-js")}returncontents;},})<#+publicconststringrelativeBaseUrl="../js";publicstringGetRequireJSName(stringpath){varrelativePath=Path.GetFullPath(path).Replace(Path.GetFullPath(this.Host.ResolvePath("..\\js\\")),"");returnPath.Combine(Path.GetDirectoryName(relativePath),Path.GetFileNameWithoutExtension(relativePath)).Replace("\\","/");}#>

通过T4模板生产的构建脚本如下:

({appDir:'../js',baseUrl:'./',mainConfigFile:'../js/config.js',dir:'../release-js',modules:[{name:"config",include:[//TheseJSfileswillbeonEVERYpageinthemain.jsfile//Sotheyshouldbethefileswewillalmostalwaysneedeverywhere"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"){returncontents.replace("/themes/default/content/js","/themes/default/content/release-js")}returncontents;},})