为什么要模块化



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. 创建好文件目录结构




ESM和Commonjs混用_json

 



 



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. 创建项目结构



 

ESM和Commonjs混用_json_02

 



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. 文件目录结构




ESM和Commonjs混用_加载_03

 



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严格区分