NET MVC Bundling and RequireJS

 

高手速来围观帮忙解惑~关于ASP.NET MVC Bundling and RequireJS的取舍问题,最近比较困惑,我希望有一种方式可以结合两者的优点。作为.NET程序员,难道你没有过这方面的困惑吗?

因为我感觉各自都有优缺点,RequireJS的缺点在于,在开发的时候,你不能引入压缩后的js或者css,否则无法调试和修改,而Bundling的话debug模式默认情况下是不压缩,你一发布到生产成release就自动压缩,调试起来非常方便。RequireJS的优点在于可以异步按需加载,还有就是模块化js代码,而Bundling 则是简单粗暴的全部合并成一个文件进行加载,你看不出模块化引用也实现不了按需加载, 那么在开发过程中作为.NET程序员是如何取舍的呢?能不能结合二者的优点来使用呢?

如果你跟我说你还不知道RequireJS是个神马冬冬?请移步至:http://requirejs.org/docs/api.html

方式一 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引用

    <script src="~/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的调用:

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

js的调用:

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

RequireJsHelpers:

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

再来看下我们的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) == Release node "$(ProjectDir)themes\default\content\build\r.js" -o "$(ProjectDir)themes\default\content\release-js\build-js.js"
if $(ConfigurationName) == Release node "$(ProjectDir)themes\default\content\build\r.js" -o "$(ProjectDir)themes\default\content\release-css\build-css.js"

自动构建

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

@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

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

T4模板代码如下:

<#@ 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("\\", "/");
} #>

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

({
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;
},
})

有时候,总是会在某一刹那间感觉自己就像天才一般,O(∩_∩)O哈哈~,请让我先自恋一下,因为程序员总是有许多自己的一些想法,如果你觉得我的文章对你有帮助或者启发,请推荐一下吧!

后记:我本来还想给每一个js后缀添加版本号来实现缓存,如?v=1.0,但是后面想了下浏览器本身就自带客户端缓存,所以就先没有添加。

NET MVC Bundling and RequireJS的更多相关文章

  1. ASP.NET MVC Bundling and RequireJS

    关于ASP.NET MVC Bundling and RequireJS的取舍问题,最近比较困惑,我希望有一种方式可以结合两者的优点.作为.NET程序员,难道你没有过这方面的困惑吗? 因为我感觉各自都 ...

  2. mvc Bundling 学习记录(一)

    参考博客:http://www.cnblogs.com/xwgli/p/3296809.html 这里要详细记录的是对于现有MVC项目进行Bundling功能 1  如果没有System.Web.Op ...

  3. mvc Bundling 学习记录

    因为现在的项目JS引用很多,无意中看到了MVC4的Bundling,开始的时候感觉很不错,将所有的CSS,js文件压缩成一个文件处理,画面调用也很简单 于是,花了一个下午的时候研究了一下,并且通过各种 ...

  4. 如何在 ASP.NET MVC 中集成 AngularJS(2)

    在如何在 ASP.NET MVC 中集成 AngularJS(1)中,我们介绍了 ASP.NET MVC 捆绑和压缩.应用程序版本自动刷新和工程构建等内容. 下面介绍如何在 ASP.NET MVC 中 ...

  5. bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

    也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...

  6. 前端MVC学习总结(一)——MVC概要与angular概要、模板与数据绑定

    一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.AngularJS是框架而jQuery则是库. 1.2. ...

  7. ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view)

    在本节中,您将验证电影控制器生成的编辑方法(Edit action methods)和视图.但是首先将修改点代码,使得发布日期属性(ReleaseDate)看上去更好.打开Models \ Movie ...

  8. 浅谈HTML5单页面架构(一)——requirejs + angular + angular-route

    心血来潮,打算结合实际开发的经验,浅谈一下HTML5单页面App或网页的架构. 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又要数单页面架构体验 ...

  9. ASP.NET MVC之如何看待内置配置来提高性能优化(四)

    前言 前几篇我们比较基础的讲了下MVC中的知识,这一节我们穿插点知识,讲讲MVC中我们可以提高性能的办法. Razor视图引擎优化(优化一) 我们知道默认情况下配置MVC去解析一个视图会首先约定通过查 ...

随机推荐

  1. scheme 之门

    scheme 之门 开始之前 这是一篇 Scheme 的介绍文章. Scheme 是一个 LISP 的方言, 相对于 Common LISP 或其他方言, 它更强调理论的完整和优美, 而不那么强调实用 ...

  2. Go语言?Docker?对新技术怎么看?

    对于 Go 语言和 Docker 这两种技术,在国内的技术圈中有相当大的一部分人和群体还在持观望或是不信任的态度.所以,我想写这篇文章,从两个方面来论述一下我的观点和看法. 上个月,作为 Go 语言的 ...

  3. from表单文件上传后页面跳转解决办法

    from表单上传文件,路径跳转后,又不能转发回来. 本人的一个解决办法是.返回一段html代码,浏览器解析后后退一步,回到原来的页面并刷新. return "<html>< ...

  4. 【转】如何学习android开发

    1.Java基础 很多朋友一上手就开始学习Android,似乎太着急了一些.Android应用程序开发是以Java语言为基础的,所以没有扎实的Java基础知识,只 是机械的照抄别人的代码,是没有任何意 ...

  5. NB-IoT 的“前世今生”

    作者:个推B2D研发工程师 海晏 根据<爱立信2018移动报告>(Ericsson Mobility Report,June 2018)的预测,蜂窝物联网设备连接数将在2023年达到35亿 ...

  6. P3932 浮游大陆的68号岛

    P3932 浮游大陆的68号岛 妖精仓库的储物点可以看做在一个数轴上.每一个储物点会有一些东西,同时他们之间存在距离. 每次他们会选出一个小妖精,然后剩下的人找到区间[l,r]储物点的所有东西,清点完 ...

  7. BZOJ2301:莫比乌斯反演+二维容斥解决GCD范围计数

    这个题是刚才刷的第一道反演题的拓展版,加上一个容斥就可以了 #include<cstdio> #include<algorithm> using std::min; ; int ...

  8. Spring RedisTemplate操作-发布订阅操作(8)

    @Component("sub") public class Sub implements MessageListener{ @Autowired private StringRe ...

  9. CSS规范 - 典型错误--(来自网易)

    不符合NEC规范的选择器用法 .class{} 不要以一个没有类别的样式作为主选择器,这样的选择器只能作为后代选择器使用,比如.m-xxx .class{}.        .m-xxx div{} ...

  10. Service Mesh 及其主流开源实现解析(转)

    什么是 Service mesh Service Mesh 直译过来是 服务网格,目的是解决系统架构微服务化后的服务间通信和治理问题.服务网格由 sidecar 节点组成.在介绍 service me ...