ASP.NET MVC Bundling and RequireJS
高手速来围观帮忙解惑~关于ASP.NET MVC Bundling and RequireJS的取舍问题,最近比较困惑,我希望有一种方式可以结合两者的优点。作为.NET程序员,难道你没有过这方面的困惑吗?
因为我感觉各自都有优缺点,RequireJS的缺点在于,在开发的时候,你不能引入压缩后的js或者css,否则无法调试和修改,而Bundling的话debug模式默认情况下是不压缩,你一发布到生产成release就自动压缩,调试起来非常方便。RequireJS的优点在于可以异步按需加载,还有就是模块化js代码,而Bundling 则是简单粗暴的全部合并成一个文件进行加载,你看不出模块化引用也实现不了按需加载, 那么在开发过程中作为.NET程序员是如何取舍的呢?能不能结合二者的优点来使用呢?
目标:在ASP.NET MVC项目中实现js和css的模块化,并支持压缩合并。
如果你跟我说你还不知道RequireJS是个神马冬冬?请移步至:http://requirejs.org/docs/api.html
项目目录结构沿用上一篇ASP.NET MVC 重写RazorViewEngine实现多主题切换
方式一 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\build\build-js.js"
if $(ConfigurationName) == Release node "$(ProjectDir)themes\default\content\build\r.js" -o "$(ProjectDir)themes\default\content\build\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;
},
})
个人点评:灵活性很好,想怎么整怎么整,而且可以很好的支持每日构建和持续集成。
有时候,总是会在某一刹那间就有新奇的想法,这就是程序员,如果你觉得我的文章对你有帮助或者启发,请推荐一下吧!如果有自己的想法也请提出来,大家一起探讨!
后记:我本来还想给每js和css的url路径后缀添加版本号来实现客户端缓存的自动更新,如?v=1.0,但是后面想了下浏览器本身就自带客户端缓存,所以就先没有添加,如果真有需要,可以随时补上。
调整后的项目源码:https://git.coding.net/zouyujie/Smp.git
ASP.NET MVC Bundling and RequireJS的更多相关文章
- NET MVC Bundling and RequireJS
NET MVC Bundling and RequireJS 高手速来围观帮忙解惑~关于ASP.NET MVC Bundling and RequireJS的取舍问题,最近比较困惑,我希望有一种方 ...
- 如何在 ASP.NET MVC 中集成 AngularJS(2)
在如何在 ASP.NET MVC 中集成 AngularJS(1)中,我们介绍了 ASP.NET MVC 捆绑和压缩.应用程序版本自动刷新和工程构建等内容. 下面介绍如何在 ASP.NET MVC 中 ...
- ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view)
在本节中,您将验证电影控制器生成的编辑方法(Edit action methods)和视图.但是首先将修改点代码,使得发布日期属性(ReleaseDate)看上去更好.打开Models \ Movie ...
- 【记录】ASP.NET MVC RegisterBundles
1. Install the package from nuget Install-Package Microsoft.AspNet.Web.Optimization 2,BundleConfig 配 ...
- 在ASP.NET MVC项目中使用React
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:最近在开发钉钉的微应用,考虑到性能和UI库的支持,遂采用了React来开发前端. 目前 ...
- 如何在 ASP.NET MVC 中集成 AngularJS(1)
介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 REST 的 Web API 服务.另外,还有最新的数据库技术.最新 ...
- ASP.NET MVC 4 Optimization的JS/CSS文件动态合并及压缩
JS/CSS文件的打包合并(Bundling)及压缩(Minification)是指将多个JS或CSS文件打包合并成一个文件,并在网站发布之后进行压缩,从而减少HTTP请求次数,提高网络加载速度和页面 ...
- ASP.NET MVC使用Bootstrap系列(1)——开始使用Bootstrap
阅读目录 Bootstrap结构介绍 在ASP.NET MVC 项目中添加Bootstrap文件 为网站创建Layout布局页 使用捆绑打包和压缩来提升网站性能 在Bootstrap项目中使用捆绑打包 ...
- ASP.NET MVC 4 RC的JS/CSS打包压缩功能 Scripts.Render和Styles.Render
打包(Bundling)及压缩(Minification)指的是将多个js文件或css文件打包成单一文件并压缩的做法,如此可减少浏览器需下载多个文件案才能完成网页显示的延迟感,同时通过移除JS/CSS ...
随机推荐
- Servlet简单总结(一)
一.Servlet简单总结 1.1. 什么是Servlet Servlet是JavaEE三大组建之一,是使用Java语言编写服务器端的程序,主要用来处理Web应用程序中的请求-响应.Servlet并没 ...
- vs项目和msql不兼容解决方案
当vs的工程项目加载了libmysql.lib 即:附加包含目录,附加库目录,附加依赖项都设置好之后,如过编译出现如下: error LNK2019: 无法解析的外部符号 _mysql_real_co ...
- MySQL ProxySQL读写分离实践
目的 在上一篇文章MySQL ProxySQL读写分离使用初探里初步介绍了ProxySQL的使用,本文继续介绍它的一些特点和DBProxy的性能差异.深入一些去了解ProxySQL,通过测试来说明Pr ...
- SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession作用域(Scope)和生命周期
可以说每个MyBatis都是以一个SqlSessionFactory实例为中心的.SqlSessionFactory实例可以通过SqlSessionFactoryBuilder来构建.一是可以通过XM ...
- Jmeter 初学(一)
Jmeter 目前属于比较流行的测试工具,即可做自动化测试也可以做性能测试,而且比较方便. 环境准备: Jmeter 运行环境需要跑在java环境,首先需要安装一下java的环境,由于我目前使用的Jm ...
- win7电脑关机时间长怎么办
最近遇到电脑关机慢的问题,其实电脑是装了ssd的,而且关掉了许多不需要的软件服务,为什么还是会出现关机慢的问题呢?开机是很快的. 所以我做了以下尝试: 1 win + r 运行 regedit 注册表 ...
- servlet的过滤器的doFilter()
doFilter中的chain.doFilter(res,req);//Filter 只是链式处理,请求依然转发到目的地址 意思就说过滤器只是一个关口,如果符合条件的请求会被过滤器拦截下来,然后进行处 ...
- 张高兴的 Windows 10 IoT 开发笔记:使用 ADS1115 读取模拟信号
考虑到 Raspberry Pi 读取模拟信号是很烦人的事情,更何况是在没人玩的 Windows 10 IoT 下,所以准备正儿八经的写点东西. 需求:使用 Raspberry Pi 读取输出模拟信号 ...
- 在 eclipse 中将 web 项目部署到 tomcat 服务器上
1.在 eclipse 中,选择 Window--->Preferences--->Server--->Runtime Environments,选择 Add 按钮 2.在弹出的对话 ...
- 018 关联映射文件中<class>标签中的lazy(懒加载)属性
Lazy(懒加载): 只有在正真使用该对象时,才会创建这个对象 Hibernate中的lazy(懒加载): 只有我们在正真使用时,它才会发出SQL语句,给我们去查询,如果不使用对象则不会发SQL语句进 ...