Commonjs,AMD,CMD和ES6module的实现及差异
为什么要模块化
1. 降低复杂度,降低耦合度
2. 部署方便,功能点明确
模块化的好处
1. 避免命名冲突
2. 更好的分离,按需加载
3. 提高复用性
4. 高可维护性
CommonJS
规范:
1. 每个文件都可以当做一个模块
2. 在服务器端:模块的加载是运行时同步加载的
3. 在浏览器端:模块需要提前编译打包处理
基本语法:
1. 暴露模块,暴露的本质是exports这个对象
module.exports = value
exports.xxx =value
2. 引入模块
require()
实现
1. 服务器端实现 Node.js
//module1.js module.exports = { msg : 'module1', foo(){ console.log(this.msg); } }
//module2.js module.exports = function(){ console.log('module2'); } //相当于给exports对象赋值
//module3.js exports.foo = function(){ console.log('foo() module3'); } exports.boo = function(){ console.log('boo() module3'); } exports.arr = [1,2,3,4,4,6,6,5,2,1] //相当于给exports对象属性赋值
let uniq = require('uniq') //数组去重排序第三方包,是一个函数 let module1 = require('./modules/modules1') let module2 = require('./modules/modules2') let module3 = require('./modules/modules3') module1.foo() //module1 module2() //module2 module3.foo() //foo() module3 module3.boo() //boo() module3 console.log(uniq(module3.arr))
2. 浏览器端实现 Browserify
1. 创建好文件目录结构
![](https://img2018.cnblogs.com/blog/1624596/201903/1624596-20190320214011481-320049294.png)
2. 下载browserify
全局安装: npm install browserify -g
局部安装: npm install browserify --save-dev
3. 模块实现
//module1.js module.exports = { msg : 'module1', foo(){ console.log(this.msg); } } //module2.js module.exports = function(){ console.log('module2'); } //相当于给exports对象赋值 //module3.js exports.foo = function(){ console.log('foo() module3'); } exports.boo = function(){ console.log('boo() module3'); } //app.js let module1 = require('./module1') let module2 = require('./module2') let module3 = require('./module3') module1.foo() module2() module3.boo() module3.foo()
4. 此时运行app.js在浏览器会报错,需要打包处理js
browserify js/src/app.js -o js/dist/bundle.js
5. 页面使用引入
<script src="./js/dist/build.js"></script>
AMD(异步模块定义)
专门用于浏览器端,模块的加载是异步的
基本语法:
1. 定义暴露模块
//1.定义没有依赖的模块 define(function(){ return 模块 }) //2.定义有依赖的模块 define(['module1','module2'],function(m1,m2){ return 模块 })
2. 引入使用模块
require(['module1','module2'],function(m1,m2){ 使用m1 m2 })
使用教程:
1. 下载require.js并且引入
2. 创建项目结构
![](https://img2018.cnblogs.com/blog/1624596/201903/1624596-20190320214337842-1998377409.png)
3. 定义模块
//定义没有依赖的模块 define(function(){ let name = 'module1.js', function getName(){ return name } //暴露模块 return {getName} }) //定义有依赖的模块 define(['module1','jquery'],function(module1,$){ let msg = 'module2.js', function show(){ console.log(msg,module1.getName()) } $('body').css('backgroundColor','red') //暴露模块 return {show} }) //引入模块 (function(){ requirejs.config({ //baseUrl: 'js/lib', //基本的路径,和paths里面的路径要拼接 paths:{ //配置路径 module1:'./modules/module1', module2:'./modules/module2', jquery:'./libs/jquery-1.10.1' } }) requirejs(['module2'],function(module2){ module2.show() }) })() //html <script data-main="js/main.js" src="js/lib/require.js"></script>
CMD
专门用于浏览器端,模块的加载是异步的,使用时才会加载执行
基本语法
1. 定义暴露模块
//定义没有依赖的模块 define(function(reqiuire,exports,module){ exports.xxx = value module.exports = value }) //定义有依赖的模块 define(function(require,exports,module){ //同步引入依赖模块 let module2 = require('./module2') //异步引入依赖模块 require.async('./module3',function(module3){ }) //暴露模块 exports.xxx = value })
2. 引入使用模块
define(function(require){ let m1 = require('./module1') let m4 = require('./module4') m1.show() m4.show() })
使用教程:
1. 下载安装
2. 创建项目结构
js
lib
sea.js
modules
module1.js
module2.js
module3.js
module4.js
main.js
index.html
3. 定义模块
define(function(require,exports,module){ let msg = 'module1' function foo(){ return msg } //暴露模块 module.exports = {foo} }) define(function(require,exports,module){ let msg = 'module2' function boo(){ console.log(msg) } module.exports = boo }) define(function(require,exports,module){ let data = 'module3' function fun(){ console.log(data) } exports.module3 = {fun} }) define(function(require,exports,module){ let msg = 'module4' //同步引入 let module2 = require('./module2') module2() //module2 //异步引入 require.async('./module3',function(module3){ module3.module3.fun() //module3 }) function fun2(require,exports,module){ console.log(msg) } exports.fun2 = fun2 }) define(function(require){ let module1 = require('./module1') module1.foo() //module1 let module4 = require('./module4') module4.fun2() //module4 }) //html <script src="js/lib/sea.js"></script> <script> seajs.use('./js/modules/main.js') </script>
模块3是异步引入的,回调函数先放到事件队列,加载其他主线程的模块,主线程执行完后,才把队列中的勾出来使用,于是输出的顺序是1243
因为CMD现在几乎不用了,官网也打不开,因此不去实现
ES6模块化规范
依赖模块需要编译打包处理
语法:
1. 导出模块 export
2. 引入模块 import
实现(浏览器端)
1. 使用Babel将ES6编译为ES5代码
2. 使用Browserify编译打包js
使用教程:
1. 文件目录结构
![](https://img2018.cnblogs.com/blog/1624596/201903/1624596-20190320215045165-637241392.png)
2. 创建package.json文件
3. 安装babel-cli,babel-preset-es2015和browserify(cli:command line interface 命令行接口)
1. npm insatll babel-cli browserify -g
2. npm install babel-preset-es2015 --save-dev
preset 预设(将es6转换成es5的所有插件打包)
3. 定义.babelrc文件 (rc:run control 运行时控制文件)
{
"presets" : ["es2015"]
}
4. 编码
//module1.js //暴露模块 分别暴露 export function foo() { console.log('foo() module1') } export function boo() { console.log('boo() module1') } export let arr = [1,2,3,4,5] //module2.js //统一暴露 function fun(){ console.log('fun() module2'); } function fun2(){ console.log('fun2() module2'); } //module3.js //默认暴露 可以暴露任意数据类型 暴露什么数据就是接受什么数据 export default ()=>{ console.log('默认暴露的箭头函数') } //默认暴露只能暴露一次,否则会报错,暴露多个数据可以放到一个对象中暴露 export {fun,fun2} //main.js //引入其他的模块 //import xxx from '路径' import $ from 'jquery' import {foo,boo,arr} from './module1' import {fun,fun2} from './module2' import module3 from './module3' $('body').css('backgroundColor','red') console.log(foo,boo,arr) console.log(fun,fun2) module3() //规定在使用分别暴露和统一暴露的时候,必须要用解构赋值的方法接受 //html <script src="js/dist/bundle.js"></script>
5. 编译
1. 使用Babel将es6编译成es5代码,包含CommonJS语法 babel js/src -d js/lib
2. 使用Browserify编译js browserify js/lib/main.js -o js/dist/bundle.js
CommonJS模块和ES6模块的差异
1. Commonjs是值的复制,原来模块的值改变了不会影响已经加载的该值。ES6是值是动态的,原来模块的值改变会影响import加载的值
2. Commonjs是对模块的浅拷贝,可以对值进行修改,对复杂数据类型进行修改会影响应原来模块。ES6是对模块的引用,改变值会报错
3. Commonjs重复使用require加载同一个模块,都只会在第一次加载的时候运行一次,以后的就返回第一次加载的结果
4. Commonjs的this指向当前模块,ES6的this指向undefined
5. Commonjs加载的是整个模块,所有接口全部加载进来,ES6可以单独加载其中某个接口
AMD和CMD的差异
1. AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
2. 对于依赖的模块,AMD是提前执行,CMD是延迟执行,也称为延迟加载,即需要的时候才加载
3. AMD推崇依赖前置,CMD推崇依赖就近。AMD一开始依赖就必须写好,CMD可以在使用的时候才引入依赖
4. AMD的API默认一个当多个用,CMD的API严格区分