zoukankan      html  css  js  c++  java
  • JS: 模块化

    模块化

    目前比较流行的 JS 模块化方案有 CommonJS、AMD、CMD 以及 ES6 Module,还有个 UMD 方案。

    CommonJS


    CommonJS 是服务器端的模块化方案,nodeJs 就采用了这种方案。在 CommonJS 规范中,一个文件即一个模块,用module.exportsexports定义模块输出的接口,用require加载模块。

    // math.js
    function add(a, b) {
      return a+b;
    }
    
    module.exports = {
        add: add
    }
    
    // 也可以这样写
    exports.add = (a, b) => a+b;
    
    // main.js
    const math = require('./math');
    math.add(1, 2); // 3
    

    CommonJs采用同步加载方式,对于服务器和本地环境来说,同步加载是非常快的,但对于浏览器来说,就不行了,限于网络因素,异步加载才是比较好的方案。

    AMD


    为了解决浏览器模块化的问题,AMD 和 CMD 这两个异步加载方案被提出,requireJS可以说是 AMD 方案的最佳实践。

    // index.html
    // before
    <script src="..."></script>
    <script src="..."></script>
    <script src="..."></script>
        
    // AMD - requireJS
    <script src="require.js" data-main="main.js"></script>
    

    在 requireJS 中用define定义模块,require载入模块,require.config用来配置路径。

    // math.js
    // define(id?, dependencies?, factory)
    define(() => {
      return {
        add: (a, b) => a+b
      }
    });
    
    // main.js
    require.config({
        baseUrl: 'js/lib',
        paths: {
            // 左边是模块ID,右边是路径
            // 这里的路径是 js/lib/jquery.js
            jquery: 'jquery',
            // 这里的路径是 js/lib/math.js
            math: 'math'
        }
    });
    
    // require([modlue], callback)
    require(['jquery', 'math'], ($, math) => {
      // do something
    });
    

    需要注意的是,AMD 方案是依赖前置的,提前执行。

    // AMD 依赖前置,提前执行
    define(['a', 'b'], function(a, b){
        // a 和 b谁先加载完,谁就先执行
        // 并不按照代码顺序同步执行
        a.doSomething();
        b.doSomething();
    })
    

    官网:requireJS

    CMD


    与 AMD 不同,CMD 是依赖就近,延迟执行,requireJS 也支持 CMD 写法。

    // CMD 依赖就近,延迟执行
    define(function(require, exports, module){
        // 需要 a 时,才执行 a
        var a = require("a");
        a.doSomething();
        // 需要 b 时,才执行 b
        var b = require("b");
        b.doSomething()
    })
    

    阿里的 seaJS 可以说是比较出名的 CMD 实例项目,但现在都有更好的方案来替代它们了。

    需要注意的是,两者只是对依赖模块的执行时机不一样,并非加载时机不一样,模块的加载时机都是一样的,它们都是异步加载的。AMD是模块加载完就会执行模块,所有模块都加载执行完就进入require的回调函数,CMD 则是所有模块都加载完,但不执行,等到require这个模块时才执行。

    CMD标准:https://github.com/cmdjs
    AMD标准:https://github.com/amdjs

    UMD


    UMD 是 CommonJS 和 AMD 的混合,它会判断当前环境支持哪个就使用哪个,这个就不多说了。

    ES6 Module


    ES6 Module 横空出世,混乱的时代结束了。

    ES6 在语言标准层面定义模块化规范,而且简洁明了,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

    ES6 Module 主要使用export输出,import加载。

    // math.js
    let PI = 3.1415;
    let circleArea = (x) => x*PI;
    
    export {PI, circleArea};
    
    // 也可以这样写
    export let PI = 3.1415;
    export let circleArea = (x) => x*PI;
    
    // main.js
    import {PI, circleArea} from './math.js'
    circleArea(1); // 3.1415
    
    // 也可以这样写
    import * as math from './math.js'
    math.circleArea(1); // 3.1415
    

    还有一个export default命令:

    // 使用 export default 输出的模块只能有一个输出
    export default () => concole.log('hhh');
    

    需要注意的是,CommonJS 输出的是值的拷贝,对于基本数据类型是直接拷贝,即缓存模块,但复杂数据类型是浅拷贝,因此修改一个模块中的值,是会改变另一个模块的值的。 require模块的时候,就会执行整个模块,即运行时加载,然后exports输出,缓存输出值,再次require时,会直接在缓存内取值。

    而 ES6 Module 是编译时加载,import加载的是值的引用,加载进来的值是不能被修改的,即值是只读的,而且不论是基本数据类型还是复杂数据类型,修改原模块的值,import得到的值也是会改变,即值是动态的

    • CommonJS 输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    • CommonJS 是运行时加载,ES6 模块是编译时输出接口。

    至于循环加载问题(a 依赖 b,b 依赖 c,c 依赖 a):

    CommonJS 循环加载时,因为是属于加载时执行,所以一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分就不输出。而对于 ES6 Module来说,因为是动态引用,所以出现循环加载时,只要两个模块之间存在某个引用,即取值的时候能够真正的取到值,代码就能够执行。

    // node 环境
    // a.mjs
    import {bar} from './b';
    console.log('a.mjs');
    console.log(bar());
    function foo() { return 'foo' }
    export {foo};
    
    // b.mjs
    import {foo} from './a';
    console.log('b.mjs');
    console.log(foo());
    function bar() { return 'bar' }
    export {bar};
    /* 结果
    b.mjs
    foo
    a.mjs
    bar
    */
    

    执行a.mjs不会报错,因为函数声明会提升,在执行import {bar} from './b'时,函数foo就已经有定义了,所以b.mjs加载的时候不会报错。这也意味着,如果把函数foo改写成函数表达式,也会报错。

    备注


    规范真多,但我也只用过 CommonJS 和 ES6 Module,而且 ES6 还要靠 babel

    参考


    JavaScript 模块化七日谈

    阮一峰_Module 的语法

    阮一峰_Module 的加载实现

  • 相关阅读:
    [转]C#正则表达式小结
    Silverlight 参考:KeyEventArgs.Handled 属性
    一步一步搭建免费的Silverlight 2开发环境(转载)
    Silverlight2 跨域调用Web服务的方法
    BinaryFormatter 类
    从说事到流程的理解
    比尔盖茨给青少年的11条准则
    燃烧热情
    GOF模式之乱记一通
    学而不思则罔,思而不学则殆
  • 原文地址:https://www.cnblogs.com/guolao/p/10440906.html
Copyright © 2011-2022 走看看