为什么要模块化开发
ie6之前没有js引擎,js引擎在渲染引擎中。
ie6之后有了js引擎,这时js脚本是与HTML页面写在一起的,随着页面交互等复杂度的增加,又以页面为标准分成不同的脚本块(index.html中引用index.js;index2.html中引用index2.js脚本块)这是模块化的初现
这时出现一个问题,多个js脚本中包含同样的程序块,这时可以把这些共同的程序块提取到一个js文件中管理。这时在HTML页面中需要引用共同js文件和页面特有的js文件。
这时又出现一个问题,在HTML页面引用共同js文件时,在js文件中包含很多不是本页面需要的程序块,这时就产生了资源的浪费,所以说以页面为基础划分js文件并不是那么完美,为了解决这个问题,开始以程序功能模块划分js文件。
modelA.js
var a =[1,2,3,4,5].reverse();
modelB.js
var b=a.concat([6,7,8,9])
modelC.js
var c = b.join('-');
index.js
console.log(a);
console.log(b);
console.log(c);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/modelA.js"></script>
<script src="js/modelB.js"></script>
<script src="js/modelC.js"></script>
<script src="js/index.js"></script>
</body>
</html>
问题:
js引擎加载阻塞,必须按照程序的逻辑顺序加载引用模块
在这里
引用在同一个页面的js文件共用一个全局作用域
模块化解决的问题
加载顺序
污染全局
es5
立即执行函数
函数声明不是表达式,不能使用执行符号,而表达式可以。可以把函数声明变成表达式,使用括号包裹,变成表达式,在使用执行符号
modelA.js
var moduleA= (function () {
var a =[1,2,3,4,5].reverse();
return{
a :a
};
})();
modelB.js
var moduleB=(function(moduleA){
var b = moduleA.a.concat([6,7,8,9]);
return {
b: b
}
})(moduleA);
modelC.js
var moduleC=(function(moduleB){
var c =moduleB.b.join('-');
return {
c:c
}
})(moduleB);
index.js
(function(moduleA,moduleB,moduleC){
console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);
})(moduleA,moduleB,moduleC);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/modelA.js"></script>
<script src="js/modelB.js"></script>
<script src="js/modelC.js"></script>
<script src="js/index.js"></script>
</body>
</html>
每个模块是用了立即执行函数,借用函数的闭包,使每个模块有了独立的作用域,同时模块之间可以依赖了,但是依然没有解决模块加载顺序问题
CommonJs
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序
modelA.js
var a=(function(){
return [1,2,3,4,5].reverse();
})();
module.exports={
a
}
modelB.js
var moduleA=require('./modelA')
var b=(function(){
return moduleA.a.concat([6,7,8,9]);
})();
module.exports={
b
}
modelC.js
var moduleB=require('./modelB')
var c=(function(){
return moduleB.b.join('-');
})();
module.exports={
c
}
index.js
var moduleA=require('./modelA')
moduleB=require('./modelB')
moduleC=require('./modelC')
console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0123467809-=">
<title>Document</title>
</head>
<body>
<script src="js/index.js"></script>
</body>
</html>
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
CMD 推崇依赖就近,AMD 推崇依赖前置。
AMD(异步模块定义)
requireJS
modelA.js
define('moduleA',function(){
var a=[1,2,3,4,5];
return {
a:a.reverse()
}
})
modelB.js
define('moduleB',['moduleA'],function(moduleA){
var b=[6,7,8,9,10];
return {
b: moduleA.a.concat(b)
}
});
modelC.js
define('moduleC',['moduleB'],function(moduleB){
return{
c:moduleB.b.join('_')
}
})
index.js
require.config({
paths:{
moduleA:'js/modelA',
moduleB:'js/modelB',
moduleC:'js/modelC',
}
})
require(['moduleA','moduleB','moduleC'],function(moduleA,moduleB,moduleC){
console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);
})
//所有模块加载完毕后,才会执行回调函数,前置依赖
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0123467809-=">
<title>Document</title>
</head>
<body>
<script src="./node_modules/requirejs/require.js"></script>
<script src="js/index.js"></script>
</body>
</html>
CMD(同步模块定义)
modelA.js
define(function(require,exports,module){
var a=[1,2,3,4,5];
return {
a:a.reverse()
}
})
modelB.js
define(function(require,exports,module){
var moduleA=require('modelA')
var b=[6,7,8,9,10];
return {
b: moduleA.a.concat(b)
}
})
modelC.js
define(function(require,exports,module){
var moduleB=require('modelB')
return {
c:moduleB.b.join('_')
}
})
index.js
require.config({
paths:{
moduleA:'js/modelA',
moduleB:'js/modelB',
moduleC:'js/modelC',
}
})
seajs.use(['modelA.js','modelB.js','modelC.js'],function(moduleA,moduleB,moduleC){
console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);
})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/sea.js"></script>
<script src="js/index.js"></script>
</body>
</html>
ES6
modelA.js
export default{
a:[1,2,3,4,5]
}
modelB.js
import moduleA from './modelA.js'
export default{
b:moduleA.a.concat([6,7,8,9,10])
}
modelC.js
import moduleB from './modelB.js'
export default{
c:moduleB.b.join('_')
}
index.js
import moduleA from './modelA.js';
import moduleB from './modelB.js';
import moduleC from './modelC.js';
console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module" src="js/index.js"></script>
</body>
</html>