资讯专栏INFORMATION COLUMN

JS常见模块化规范(CommonJS/AMD/CMD/UMD/ES6 Module)

walterrwu / 646人阅读

摘要:常见模块化方案是由社区提出的模块化方案中的一种,遵循了这套方案。是模块化规范中的一种,遵循了这套规范。中的模块化能力由两个命令构成和,命令用于规定模块的对外接口,命令用于输入其他模块提供的功能。

为什么需要模块化

在ES6出现之前,JS语言本身并没有提供模块化能力,这为开发带来了一些问题,其中最重要的两个问题应当是全局污染依赖管理混乱

// file a.js
var name = "aaa";
var sayName = function() {
    console.log(name);
};



上面的代码中,我们两次调用a.js所提供的sayName函数输出了不同结果,很明显这是因为两个文件都对变量name进行了赋值,因此相互之间造成了影响。当然我们可以在编写代码时注意不要定义已存在的变量名,但是当一个页面引用了10几个几百行的文件时,记住所有已经定义过的变量显然不太现实。

// file a.js
var name = getName();
var sayName = function() {
    console.log(name)
};
// file b.js
var getName = function() {
    return "timo";
};






// Uncaught ReferenceError: getName is not defined

上面的代码说明,多个文件有依赖关系时,我们需要确保其引入的顺序,从而保证运行某个文件时,其依赖已经提前加载,可以想象,面对越大型的项目,我们需要处理的依赖关系也就越多,这既麻烦又容易出错。

而为了解决这些,社区中出现了许多为JS语言提供模块化能力的规范,借助这些规范,能让我们的开发更加方便安全。

常见模块化方案 CommonJS

CommonJS是由社区提出的模块化方案中的一种,Node.js遵循了这套方案。

基本写法
// file a.js
var obj = {
    sayHi: function() {
        console.log("I am timo");
    };
};

module.exports obj;
// file b.js
var Obj = require("xxx/xxx/a.js");

Obj.sayHi(); // "I am timo"

上面的代码中,文件a.js是模块的提供方,文件b.js是模块调用方。

规范

每个文件都是一个模块;

在模块内提供module对象,表示当前模块;

模块使用exports对外暴露自身的函数/对象/变量等;

模块内通过require()方法导入其他模块;

CommonJS的规范,简单来说就是上面4条,可以对照基本写法中的例子理解一下,在实际实现中,Node.js虽然遵循CommonJS规范,但是仍然对其进行了一些调整。

AMD

AMD是模块化规范中的一种,RequireJS遵循了这套规范。

基本用法
 // file a.js
 define("module", ["m", "./xxx/n.js"], function() {
    // code...
 }) 

上面的代码中,文件a.js向外导出了模块;

规范

AMD中,暴露模块使用define函数

define(moduleName, [], callback);

如上面代码,define函数共有三个参数

moduleName该参数可以省略,表示该模块的名字,一般作用不大

["name1", "name2"],第二个参数是一个数组,表示当前模块依赖的其他模块,如果没有依赖模块,该参数可以省略

callback,第三个参数是必传参数,是一个回调函数,内部是当前模块的相关代码

其他

ADM的特点是依赖前置,这是ADM规范与接下来要介绍的CMD规范最大的不同,依赖前置是指:在运行当前加载模块回调前,会首先将所有依赖包加载完毕,也是就是define函数的第二个参数中指定的依赖包。

CDM 基本写法
 define(function(require, exports, module) {   
    var a = require("./a")  
    a.doSomething();
    // code... 
    var b = require("./b") 
    // code...
})

上面代码是CMD规范导出模块的基本写法;

规范

从写法可以看出,CMD的写法和AMD非常像,其主要区别是对于依赖加载时机的不同,上面已经说过,AMD是依赖前置,而CMD规范推崇就近原则,简单说就是在模块运行前并不加载依赖,模块运行过程中,当需要某个依赖时,再去进行加载。

UMD

CommonJS、AMD、CMD并行的状态下,就需要一种方案能够兼容他们,这样我们在开发时,就不需要再去考虑依赖模块所遵循的规范了,而UMD的出现就是为了解决这个问题。

基本写法
(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;
}));

上面的代码是UMD的基本写法,从代码就可以看出,其能够同时支持CommonJS规范和AMD规范。

ES6 module

上面分别介绍了CommonJS、AMD、CMD和UMD,他们都是社区对于JS实现模块化的贡献,这个规范其产生的根本原因,都是JS语言自身没有模块化能力,而目前,在JS最新的语言规范ES6中,已经为JS增加了模块化能力,而JS自身的模块化方案,完全能够替代目前社区提出的各类规范,且能够做到浏览器端和Node端通用。

ES6中的模块化能力由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

export命令

ES6中一个文件就是一个模块,模块内部的变量/函数等外部是无法访问的,如果希望将内部的函数/变量等对外暴露,供其他模块进行使用,就需要通过export命令进行导出

// file a.js
export let a = 1;
export let b = 2;
export let c = 3;
// file b.js
let a = 1;
let b = 2;
let c = 3;

export {a, b, c}
// file c.js
export let add = (a, b) => {
    return a + b;
};

上面三个文件的代码,都是通过export命令导出模块内容的示例,其中a.js文件和b.js文件都是导出模块中的变量,作用完全一致但写法不同,一般我们更推荐b.js文件中的写法,原因是这种写法能够在文件最底部清楚地知道当前模块都导出了哪些变量。

import命令

模块通过export命令导出变量/函数等,是为了让其他模块能够导入去使用,在ES6中,文件导入其他模块是通过import命令进行的

// file d.js
import {a, b, c} from "./a.js";

上面的代码中,我们引入了a.js文件中的变量a、b、c,import在引入其他模块内的函数/变量时,必须与原模块所暴露出来的函数名/变量名一一对应。
同时,import命令引入的值是只读的,如果尝试对其进行修改,则会报错

import {a} d from "./a.js";
a = 2; // Syntax Error : "a" is read-only;
export default命令

从上面import的介绍可以看到,当需要引入其他模块时,需要知道此模块暴露出的变量名/函数名才可以,这显然有些麻烦,因此ES6还提供了一个import default命令

// file a.js

let add = (a, b) => {
    return a+b
};
export default add;
// file b.js
import Add from "./a.js";

Add(1, 2); // 3

上面的代码中,a.js通过export default命令导出了add函数,在b.js文件中引入时,可以随意指定其名称

export default命令是默认导出的意思,既然是默认导出,显然只能有一个,因此每个模块只能执行一次export default命令,其本质是导出了一个名为default的变量或函数。

总结

最后再来总结一下, 首先在之前的JS语言中没有模块化能力,而随着网站功能的复杂,开发越来越不方便,因此社区中出现了一批为JS提供模块化能力的方案,其中比较主流的就是我们介绍过的CommonJS、AMD、CMD和UMD等,而之后发布的ES6语言标准,从JS语言自身提供了模块化能力,因此,随着ES6的逐步普及,ES6 module应该会逐步取代目前的各类社区规范,但不可否认,在没有ES6的日子里,这些社区规范给前端人员提供了巨大的方便,并推动了JS的发展。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98701.html

相关文章

  • JS块化——CommonJS AMD CMD UMD ES6 Module 比较

    摘要:即尽早地执行依赖模块。阮一峰输出值的引用模块是动态关联模块中的值,输出的是值得引用。的加载实现阮一峰运行时加载静态编译模块是运行时加载,模块是编译时输出接口。 模块化开发 优点 模块化开发中,通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数,并且可以按需加载。 依赖自动加载,按需加载。 提高代码复用率,方便进行代码的管理,使得代码管理更加清晰、规范。 减少了命名冲...

    shadowbook 评论0 收藏0
  • js中的块化——commonjs,AMD,CMD,UMD,ES6

    摘要:若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。这也是目前很多插件头部的写法,就是用来兼容各种不同模块化的写法。语句输出的值是动态绑定的,绑定其所在的模块。 前言 历史上,js没有模块化的概念,不能把一个大工程分解成很多小模块。这对于多人开发大型,复杂的项目形成了巨大的障碍,明显降低了开发效率,java,Python有import,甚至连css都有@import,但是令人费...

    qpal 评论0 收藏0
  • 前端块化的意义

    摘要:而前端要使用模块化的原因或者要解决的问题就是解决命名冲突管理依赖提高代码的可复用性相关模块化规范常见的规范有规范规范是在推广过程中对模块定义的规范化产出,主要用于浏览器端。目前的各类全局变量都可以模块化。 开发中明显可以感觉到加载一个HTML需要依赖很多的JS文件依赖,比如到一定阶段的HTML页面,尾部就是这样的: 随着所需功能越来越多,我们就需引入更多的JS依...

    ThreeWords 评论0 收藏0
  • 前端块化规范

    摘要:一模块化命名空间二命名空间库名类别名方法名三规范一个文件为一个模块,通过暴露快接口,通过引入模块,同步执行文档示例四规范使用定义模块使用加载模块依赖前置,提前执行规范文档示例模块名字模块输出模块输出等于在最前面声明并初始化了要用到的模块即使 一、js模块化 命名空间 commonJS AMD/CMD/UMD ES6 module 二、命名空间 库名.类别名.方法名 var NameSp...

    xiaolinbang 评论0 收藏0
  • 前端块化规范

    摘要:一模块化命名空间二命名空间库名类别名方法名三规范一个文件为一个模块,通过暴露快接口,通过引入模块,同步执行文档示例四规范使用定义模块使用加载模块依赖前置,提前执行规范文档示例模块名字模块输出模块输出等于在最前面声明并初始化了要用到的模块即使 一、js模块化 命名空间 commonJS AMD/CMD/UMD ES6 module 二、命名空间 库名.类别名.方法名 var NameSp...

    lsxiao 评论0 收藏0

发表评论

0条评论

walterrwu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<