JavaScript模块化与规范

一、什么是模块化?

模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得“有理可循”。

另一些对于模块化一些专业的定义为:模块化是软件系统的属性,这个系统被分解为一组高内聚,低耦合的模块。那么在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。

既然是模块化设计,那么作为一个模块化系统所必须的能力:

  • 定义封装的模块;
  • 定义新模块对其他模块的依赖;
  • 可对其他模块的引入支持;

本篇,将市面上流行的几种模块化技术进行对比,让大家清楚具体的规范:

  • CommonJS(注意:不要与CMD混淆)
  • AMD(Asyncronous Module Definition)异步模块定义
  • CMD(Common Module Definition)通用模块定义
  • UMD(Universal Module Definition)通用模块定义
  • ES6 module

二、规范

2.1、CommonJS

  • 特点:
    • 一个单独的文件就是一个模块;
    • 可以多次加载,但只会在第一次加载时运行一次,运行结果被缓存;
    • 以后再加载,就直接读取缓存结果;
    • 要想让模块再次运行,必须清除缓存;
    • 模块加载会阻塞后续代码执行,需要等到模块加载完成才能继续执行(同步加载);
  • 环境:服务器环境
  • 应用:nodejs的模块规范是参照commonJS实现的
  • 语法:
    • 导入:require('路径')
    • 导出:module.exports和exports
  • 注(module.exports和exports的区别):
    • exports只是module.exports的一个引用
    • 相当于Node为每个模块提供一个exports变量,指向module.exports
    • 同在每个模块头部,有一行var exports = module.exports;
 // a.js
 // 相当于这里还有一行:var exports = module.exports;代码
 exports.a = 'Hello world';  // 相当于:module.exports.a = 'Hello world';

 // b.js
 var moduleA = require('./a.js');
 console.log(moduleA.a); // 打印出:hello world
---------------------------------------------------------------------------------
 // 稍微复杂点的例子
var $ = require('jquery');
var _ = require('underscore');

// methods
function a(){};    //    private because it's omitted from module.exports
function b(){};    //    public because it's defined in module.exports
function c(){};    //    public because it's defined in module.exports

// exposed public methods
module.exports = {
    b: b,
    c: c
};

2.2、AMD

  • 特点:
    • 异步加载
    • 管理模块之间的依赖性(多个JS文件依赖时,依次加载最早被依赖的JS文件)
  • 环境:浏览器环境
  • 应用:requireJS是参照AMD规范实现的
  • 语法:
    • 导入:require( ['模块名称'], function (模块变量引用){ 代码 } );
    • 导出:define(function (){return '值');
 // a.js
 define(function (){
   return {
    a:'hello world'
   }
 });

 // b.js
 require(['./a.js'], function (moduleA){
     console.log(moduleA.a); // 打印出:hello world
});
---------------------------------------------------------------------------------
// 稍微复杂点的例子
define(['jquery', 'underscore'], function ($, _) {
    // methods
    function a(){};    //    private because it's not returned
    function b(){};    //    public because it's returned
    function c(){};    //    public because it's returned

    // exposed public methods
    return {
        b: b,
        c: c
    }
});

2.3、CMD

  • 特点:
    • CMD是在AMD基础上改进的一种规范;
    • 与AMD不同在于对依赖模块的执行时机处理不同:
    • CMD是就近依赖
    • AMD是前置依赖
  • 环境:浏览器环境
  • 应用:seaJS是参照CMD规范实现的;requireJS最新几个版本也参照了CMD规范;
  • 语法:
    • 导入:define(function(require, exports, module) {});
    • 导出:define(function (){return '值');
// a.js
define(function (require, exports, module){
  exports.a = 'hello world';
});

// b.js
define(function (require, exports, module){
    var moduleA = require('./a.js');
    console.log(moduleA.a); // 打印出:hello world
});

2.4、UMD

  • 特点:
    • 兼容AMD和CommonJS规范的同时,还兼容全局引用的方式;
  • 环境:浏览器环境
  • 应用:无
  • 语法:无
(function (root, factory) {
     if (typeof define === 'function' && define.amd) {
         // AMD
         define(['jquery'], factory);
     } else if (typeof exports === 'object') {
         // Node, CommonJS之类的
         module.exports = factory(require('jquery'));
     } else {
         // 浏览器全局变量(root 即 window)
        root.returnExports = factory(root.jQuery);
    }
}(this, function ($) {
    //方法
    function myFunc(){};
    //暴露公共方法
    return myFunc;
}));
---------------------------------------------------------------------------------
// 稍微复杂点的例子
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery', 'underscore'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS-like
        module.exports = factory(require('jquery'), require('underscore'));
    } else {
        // Browser globals (root is window)
        root.returnExports = factory(root.jQuery, root._);
    }
}(this, function ($, _) {
    // methods
    function a(){};    // private because it's not returned
    function b(){};    // public because it's returned
    function c(){};    // public because it's returned

    // exposed public methods
    return {
        b: b,
        c: c
    }
}));

2.5、ES6 module

  • 特点:
    • 按需加载(编译时加载)
    • import和export命令只能在模块的顶层,不能在代码块之中
  • 环境:浏览器环境
  • 应用:ES6最新语法支持规范;
  • 语法:
    • 导入:import {模块名A,模块名B...} from '模块路径';
    • 导出:export和export default
    • import('模块路径').then()方法
  • 注意:
    • export只支持对象形式导出,不支持值的导出
    • export default命令用于指定模块的默认输出,只支持值导出
    • export default只能指定一个,本质上它就是输出一个叫做default的变量或方法
/*错误的写法*/
 // 写法一
 export 1;

 // 写法二
 var m = 1;
 export m;

 // 写法三
if (x === 2) {
  import MyModual from './myModual';
}

/*正确的三种写法*/
// 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};

// 写法四
var n = 1;
export default n;

// 写法五
if (true) {
    import('./myModule.js')
    .then(({export1, export2}) => {
      // ...·
    });
}

// 写法六
Promise.all([
  import('./module1.js'),
  import('./module2.js'),
  import('./module3.js'),
])
.then(([module1, module2, module3]) => {
   ···
});
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容