ASP.NET MVC4 捆绑(Bundle)技术下的 JavaScript

说到 Web 应用中 JavaScript 的模块化,很容易想到 RequireJS、SeaJS 和 ECMAScript 6。ES6 要全面应用还得有段时间,RequireJS 和 SeaJS 的模块化在实际应用中又有两个分支:一是经过按需加载的方式加载并建立模块,二是经过工具打包成单一文件,一次性加载,按需建立模块。ASP.NET MVC4 的捆绑(Bundle)技术相似后者。html


MVC4 Bundle 主要用于优化 JavaScript 和 CSS 资源的加载。关于这个技术的介绍,能够参考《ASP.NET Mvc4 Bunlde 捆绑压缩技术》,或者《CSS编程:捆绑和缩小》。其特色很鲜明,主要有两点:git


  1. 在开发环境,加载原文件,便于定位和 Debug;编程

  2. 在生产环境,按配置将全部资源分类打包压缩,优化浏览器对资源加载。xcode


也正是因为它的这两个特色,若是要使用 Bundle 技术,就很难使用现有的 JavaScript 模块化工具来进行开发。翻了下百度和 Google,没找到合适的解决方案,因而决定本身写个简单的模块加载器,主要实现以下目标:浏览器


  1. 模块化开发缓存

  2. 大部分 JavaScript 文件由 MVC4 一次性加载,但模块按需建立app

  3. 部分页面的脚本,能够按页面须要单独加载,但一样是模块化的框架


分析目标,归整一下,大概有以下要点须要实现ide


  1. 因为 Bundle 以后模块不能以文件为单位,因此须要重用的模块都应该是命名模块。考虑到具体页面本身的模块不须要重用,因此这种状况下能够定义为匿名模块。因此模块定义函数要像这样:模块化

    funciton define(name, factory) {
        if (isFunction(name)) {
            factory = name
            name = undefined
        }
        // ......
    }

    模块名称惟一性由人来控制,可是应提供检查机制,因此若是出现重复定义的状况,抛出异常。由是在一个项目中,命名冲突这种状况应该不是主要矛盾。若是不幸命名冲突成为了主要矛盾,基本上也能够经过定义命名空间来解决。最简单的命名空间就是在模块名中加入命名空间部分,好比 "app.core.codec.hexcode"


  2. 按需加载,使用 require 函数

    function require(moduleName) {
        // ......
    }


  3. 执行模块的入口。虽然能够用 require 做为入口,可是 require 须要一个模块名称做为参数,不能用于匿名模块做为入口的状况。假想以下应用场景:

    define(function() {
        // ......
    }).use()

    要实现这种应用场景,就须要 define 返回一个对象,该对象拥有 use 方法,能够经过 use 方法一次性调用当前模块的 factory 函数。比较简单直接的方式就是在内部定义一个 Module 类来装载模块配置,在 define 的时候生成 Module 对象,并返回出来。

    function define(name, factory) {
        // ......
        var module = new Module(factory)
        // ......
        return module
    }


  4. 内部模块管理。经过一个 map<name, module> 来管理全部模块定义,这在实现上就是一个普通的 JavaScript 对象。匿名模块由于是当即使用,因此不须要进行管理。模块管理的核心实际上是 Module 类,须要经过它完成建立模块、缓存导出对象和提供导出对象等。并且除了 use 方法须要暴露出来以后,其它方法都应该隐藏起来。

    通过参考、推敲和实验,得出了以下的一个代码框架

    // 这是全部命名模块保存的地方
    var modules = {}
    
    function Module(name, factory) {
        // 建立模块对象,保存 factory 函数
    }
    
    Module.prototype.use = function() {
        // 执行 factory 函数
        // 处理 exports 和 isExported 等状态
        return exports
    }
    
    function define(name, factory) {
        // 定义并保存模块
        modules[name] = new Module(name, factory)
    }
    
    function require(name) {
        // 按名称找到模块,并执行之
        return modules[name].use()
    }


在最终实现的时候,还须要处理容错,以及若干细节问题。最终代码命名为 js-modular.js,在附件中能够下载。在使用的时候只须要注意一点,页面上加载脚本的时候,记得把 js-modular.js 放在全部模块定义脚本以前便可。



目前已经创建了开源项目 jNs,基于命名空间的模块管理工具,是在 js-modular.js 的基础之上发展而来的。若是有兴趣的话,请关注一下这个项目。


js-modular.js 及 Demo(VS2013 + MVC4 + NuGet)下载

相关文章
相关标签/搜索