早期js是没有模块化的概念,即使是没有也没关系前端设计的内容并没复杂到要使用模块化。
但是node.js的问世,可以用js来编写后台那就不得不使用模块了,因为后台涉及到的功能很多,不得不将一个个功能封装成模块以便调用。
既然js要模块化,就要定义一个统一标准,不让各自都有自己的标准那就没法玩了。
js有两个模块化标准:CommonJS和AMD、CMD还有混合模式UMD
同步加载模块:
CommonJS一般运用在node.js上,在服务器上使用同步加载模块没什么问题,只要把模块都放在本地同一个硬盘上加载速度相当于局域网,速度取决于硬盘读写速度。
CommonJS代码风格感受下:
// module.js function hello() { this.name1='名字'; this.run=function(){ return '我的'+this.name1; } } module.exports=new hello(); //对外提供接口,hello对象暴露在全局中
//require方法默认读取js文件,所以可以省略js后缀 var hello = require('./module'); //模块加载方式 hello.run() //调用了module.js模块中的run方法
异步加载模块:
在浏览器或者客户端上最好使用异步的方式加载模块,即是:AMD。因为浏览器加载页面时,要获取到js插件或者库时都要从服务器上获取到,这时候的速度取决于网速有时候还会出现卡死状态。所以在浏览器中适用于AMD加载模块,哪部分代码依赖哪个模块就让其等待依赖的模块加载完毕后在回调运行即可。
(1)在requirejs里推行AMD加载方式
AMD代码风格感受下:
// module1.js define(function () { console.log('11111'); var obj={ myname:'陈陈', getname:function () { return '我的名字是'+this.myname; } } return obj; //对外暴露的对象 });
require(['module1'],function (obj) { //参数obj就是module1.js对外暴露的对象 console.log('2222'); console.log(obj.getname()); });
AMD也支持CommonJS加载方式例如:
define(function (require, exports, module) { var obj = require("./module1"); //使用require加载模块 console.log(obj.getname()); });
(2)CMD同样也是异步加载模块的规范,CMD在seajs中推行使用,与AMD不同的是对依赖加载的顺序不一样,例如:
//已知用require加载的都是依赖模块 define(function (require, exports, module) { var a =require('./modulevv'); //1 console.log('a'); //2 var b =require('./moduleuu'); //3 console.log('b'); //4 });
在AMD中加载顺序是:1,3,2,4
在CMD中加载顺序是:1,2,3,4
一句话概括:CMD推崇依赖就近,AMD推崇依赖前置
(其实AMD也支持CMD写法,但依赖前置是官方文档的默认模块定义写法,所以我还是推荐使用AMD的)
弱依赖(软依赖)
define(function (require, exports, module) { var a =require('./modulevv'); console.log('a'); if(state){ var b =require('./moduleuu'); } console.log('b'); });
在上面代码中,我们应该根据需求来选择性加载模块moduleuu,也就是可能加载,可能不加载。
在AMD中,不管state是什么状态始终加载模块moduleuu
在CMD中,会根据state状态来选择性加载
两者更多区别参考:https://github.com/seajs/seajs/issues/277
同/异步混合体,UMD
umd是AMD和CommonJS的糅合
AMD 浏览器第一的原则发展 异步加载模块。
CommonJS 模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。
这迫使人们又想出另一个更通用的模式UMD (Universal Module Definition)。希望解决跨平台的解决方案。
UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式。
在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。
(function (window, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else { window.自己取名 = factory(); } })(this, function () { //module ...我们的代码写这里 });
如果想添加对cmd的支持就修改为:
(function (window, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else if(typeof define === "function" && define.cmd){ define(factory); } else { window.自己取名 = factory(); } })(this, function () { //module ...我们的代码写这里 });
ES6 Module
ES6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和 AMD模块,都只能在运行时确定这些东西(按需加载)。
其模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
ES6的模块不是对象,import命令会被 JavaScript引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
运行时加载: commonjs代码运行到哪里就在哪里加载,会把整个对象加载进来,不管其他方法用不用,一般都用在nodejs服务器上
编译时加载:一开始编译的时候就把代码加载进来,需要的时候才去调用指定方法,而它只引入你所需要的方法那部分的代码,而不是整个对象。