zoukankan      html  css  js  c++  java
  • NET MVC Bundling and RequireJS

    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)themesdefaultcontentuild
    .js" -o "$(ProjectDir)themesdefaultcontent
    elease-jsuild-js.js"
    if $(ConfigurationName) == Release node "$(ProjectDir)themesdefaultcontentuild
    .js" -o "$(ProjectDir)themesdefaultcontent
    elease-cssuild-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,但是后面想了下浏览器本身就自带客户端缓存,所以就先没有添加。

  • 相关阅读:
    使用jquery插件操作哈希表Hashtable的应用
    学用 ASP.Net 之 "字符串" (1): 基础
    敬告
    jQuery 练习[一]: 准备工作
    学用 ASP.Net 之 "字符串" (2): string.Format
    jQuery 练习[二]: 获取对象(1) 基本选择与层级
    学用 ASP.Net 之 "字符串" (3): string 类的非扩展方法
    jQuery 练习[二]: 获取对象(2) 定位子对象
    一个分割文本文件的小程序 回复 "jellyang" 的问题
    Delphi 7 类库继承关系表
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/7097252.html
Copyright © 2011-2022 走看看