谈谈前端JS的三种模块化规范(AMD,CMD,CommonJS)
模块化规范是什么?
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起,完成指定工作
为什么要有这个东西?
存在即合理是最好的解释,这能帮助我们更好的管理各个js文件,处理不同模块间的依赖关系
- 举个例子,用jquery要先引包,如果你在使用后才引入,那没得玩,然而新手可能并不清楚一大堆js文件之间存在先后顺序和依赖关系,很容易一个错误找半天。
- 而且,这也和浏览器解析js顺序有关,从上到下解析,a.js,b.js,c.js,依次引入,实际上c.js不需要任何依赖,就是打印一个helloworld,这种情况浏览器依旧需要先把上边两个js加载完。无效加载,浪费时间,没有主次。
- 换一种想法,用到哪个加载哪个岂不美哉?模块化规范就是这个时候诞生的。
AMD规范(以require.js为例)
AMD规范 Asynchronouse Module Defined,异步模块定义,AngularJS+RequireJS是符合AMD规范的
AMD规范使用依赖注入的模式,所有当前模块依赖的模块,都要通过异步来调用,执行语句放在回调函数里面。不依赖其他模块的语句,就不要放在回调函数里面,不干扰其他模块的运行。
官网
hello world
- 目录结构
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>require.js之hello world</title>
</head>
<body>
<h1></h1>
<!-- 这里main.js是入口文件,可以省略拓展名 -->
<script data-main="main" src="https://cdn.bootcss.com/require.js/1.0.8/require.min.js"></script>
</body>
</html>
- main.js
document.querySelector("h1").innerHTML="hello world";
- 效果图
单一模块加载
- 目录结构
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>require.js之模块加载</title>
</head>
<body>
<h1></h1>
<!-- 这里main.js是入口文件,可以省略拓展名 -->
<script data-main="js/main" src="https://cdn.bootcss.com/require.js/1.0.8/require.min.js"></script>
</body>
</html>
- main.js
//SPA我们习惯性的只留入口文件在最外边,其他的单独放文件夹里
//配置相对于index.html的js文件夹路径
requirejs.config({
baseUrl: 'js'
});
//注入依赖模块,此时必须省略.js
requirejs(["math"],function (math) {
document.querySelector("h1").innerHTML=math.add(1,2);
});
- math.js
//每一个功能模块都用的define包起来,里边的[]用来注入其他依赖模块
//模块暴露的API用return来返回
//这一点很像angular中的factory
//下面定义了一个简单的求和函数并向外暴露
define([],function(){
return {
add : function(num1,num2){
return num1+num2;
}
}
});
-
可以看到上述代码执行是有顺序的,先等依赖加载完,自己才能够运行
-
这种不需要依赖的可以裸着写,直接就执行了
多模块加载和路径配置
-
目录结构
-
这里就只粘贴主要代码了
-
main.js
//SPA我们习惯性的只留入口文件在最外边,其他的单独放文件夹里
//配置相对于index.html的js文件夹路径
//由于依赖的sub模块和main.js不在同一个文件夹,所以需要用paths配置一下相对路径
requirejs.config({
baseUrl: 'js' ,
paths : {
"sub" : "other/sub"//注意,不要加.js
}
});
requirejs.config({
baseUrl: 'js',
});
//注入依赖模块,此时必须省略.js
requirejs(["math","sub"],function (math,sub) {
document.querySelector("h1").innerHTML=math.add(1,2);
document.querySelector("h2").innerHTML=sub.do_sub(2,1);
});
- sub.js
//每一个功能模块都用的define包起来,里边的[]用来注入其他依赖模块
//模块暴露的API用return来返回
//这一点很像angular中的factory
//下面定义了一个简单的求差函数并向外暴露
define([],function(){
return {
do_sub : function(num1,num2){
return num1-num2;
}
}
});
- 最终结果一个3一个1
CMD规范(以Sea.js为例)
CMD规范 Common Modeule Defined,普通模块定义。对于依赖的模块,AMD讲究的是提前注入,管他这会用不用,先注入再说,CMD是延迟执行,什么时候用什么时候注入。
-
目录结构
-
大体和上边的amd差不多
-
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sea.js</title>
</head>
<body>
<script src="https://cdn.bootcss.com/seajs/1.3.1/sea.min.js"></script>
<script >
//与js文件夹的相对路径
seajs.config({
base: "js"
});
// 加载入口模块
seajs.use("main");
</script>
</body>
</html>
- math.js
//下面定义了一个简单的求和函数并向外暴露
define(function(require,exports,module){
//暴露
exports.add = function(num1,num2){
return num1+num2;
}
});
- main.js
//这里使用define()来包裹模块,里面有一个函数,内置require、exports、module对象:
define(function(require,exports,module){
//声明依赖,这里省略.js与否都行,用哪个require哪个
var math = require("./math");
console.log(math.add(1,2));
var sub = require("./other/sub.js");
console.log(sub.do_sub(2,1));
});
- sub.js
//下面定义了一个简单的求差函数并向外暴露
define(function(require,exports,module){
//暴露
exports.do_sub = function(num1,num2){
return num1-num2;
}
});
- 效果图
CommonJS
Node使用CommonJS模块规范,内置的require命令用于加载模块文件。这个没什么好说的,想搞懂这个,学学node好了
- 目录结构
- a.js
//直接向外暴露属性,方法
exports.name = "冷月心";
exports.say = function () {
console.log("你好");
}
- b.js
//加载
var a= require('./a.js');
//使用
console.log(a.name);
a.say();
- 效果图
node 中自定义模块注意require加载的路径问题,相对路径./不能省略,可以省略.js,直接加载模块名字的是核心模块,node内置,比如path,fs,os
构建工具webpack
webpack是CMD规范的构建工具,可以让我们裸写CMD规范的程序,帮我们自动打包成为一个js文件。这个js文件浏览器可以识别,用的时候引入就好,和引jquery方式一样。这里简单介绍webpack编译CMD规范代码的能力。
- 目录结构
- main.js
var math = require("./math");
console.log(math.add(1,2));
- math.js
exports.add = function(m,n){
return m+n;
}
-
全局安装webpack后,进入js文件夹
webpack main.js all.js -
效果如下
-
index.html引入all.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack</title>
</head>
<body>`在这里插入代码片`
<script type="text/javascript" src="js/all.js"></script>
</body>
</html>
- 效果图
- 为了方便,引入了配置文件webpack.config.js
var webpack = require('webpack')
module.exports = {
entry: './js/main.js',//入口
output: {//出口
path: __dirname,// 项目根目录
filename: './dist/all.js'//文件名
},
watch : true//热监听,实时监听代码状态,修改代码后,保存,会自动更新all.js
}
- 使用:直接
webpack
- 热监听效果图