#JS模块规范(CommonJS,AMD,CMD,ES6)#
##commonjs##
CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}
//sum.js
exports.sum = function(){...做加操作..};
//calculate.js
var math = require('sum');
exports.add = function(n){
return math.sum(val,n);
};
require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。
CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,因为commonjs是同步加载
建议阅读 http://www.cnblogs.com/skylar/p/4065455.html
###AMD###
AMD(异步模块定义), AMD就只有一个接口:define(id?,dependencies?,factory);
它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中,像这样:
define(['dep1','dep2'],function(dep1,dep2){...});
RequireJS对模块的态度是预执行。由于 RequireJS 是执行的 AMD 规范, 因此所有的依赖模块都是先执行.RequireJS是预先把依赖的模块执行,相当于是require被提前了
RequireJS执行流程
- require函数检查依赖的模块,根据配置文件,获取js文件的实际路径
- 根据js文件实际路径,在dom中插入script节点,并绑定onload事件来获取该模块加载完成的通知。
- 依赖script全部加载完成后,调用回调函数
###CMD###
sea.js就实现了CMD规范,官方网址是:http://seajs.org/docs/#docs
使用方式和AMD类似
define(function(require,exports,module){...});
SeaJS对模块的态度是懒执行, SeaJS只会在真正需要使用(依赖)模块时才执行该模块。
SeaJS执行流程
- 通过回调函数的Function.toString函数,使用正则表达式来捕捉内部的require字段,找到require('jquery')内部依赖的模块jquery
- 根据配置文件,找到jquery的js文件的实际路径
- 在dom中插入script标签,载入模块指定的js,绑定加载完成的事件,使得加载完成后将js文件绑定到require模块指定的id(这里就是jquery这个字符串)上
- 回调函数内部依赖的js全部加载(暂不调用)完后,调用回调函数
- 当回调函数调用require('jquery'),即执行绑定在'jquery'这个id上的js文件,即刻执行,并将返回值传给var b
相关文档:
https://github.com/seajs/seajs/issues/277
https://www.zhihu.com/question/20342350
一个例子来分析下require.js和sea.js的执行区别
####sea.js###
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="sea.js"></script>
</head>
<body>
<script type="text/javascript">
seajs.use("./main")
</script>
</body>
</html>
main.js
define(function(require,exports,module){
console.log("进入")
var a = require("a");
a.hello();
var b = require("b");
b.hello();
})
a.js
define(function(require,exports,module){
console.log("这里是a模块")
exports.hello = function(){
console.log("a模块的hello方法")
}
})
b.js
define(function(require,exports,module){
console.log("这里是b模块")
exports.hello = function(){
console.log("b模块的hello方法")
}
})
####require.js###
<!DOCTYPE html>
<html>
<head>
<title>requirejs</title>
<script src="require.js"></script>
</head>
<body>
<script type="text/javascript">
require(["main"])
</script>
</body>
</html>
main.js
define(["a","b"],function(a,b) {
console.log("进入")
a.hello();
b.hello();
})
a.js
define(function() {
console.log("这里是a模块")
return {
hello:function(){
console.log("a模块的hello方法")
}
}
})
b.js
define(function(require, exports, module) {
console.log("这里是b模块")
return {
hello:function(){
console.log("b模块的hello方法")
}
}
})
##ES6## es6是一套2016年指定的javascript新标准,目前没有浏览器能支持,所以需要插件来转换成es5标准才能在浏览器正常解析
####Babel####
babel是一个可以把es6语法转换成es5语法的工具,但是不支持es6的模块
babel安装
npm init
npm install
npm install --save-dev babel-cli
babel-cli 是babel提供的命令行转码工具
npm install --save-dev babel-preset-es2015
babel-preset-es2015 es2015转码规则
npm install --save-dev babel-plugin-transform-object-assign
适用于Object.assign()
npm install --save-dev babel-plugin-transform-object-rest-spread
适用于展开运算符
npm install --save-dev babel-preset-stage-0
npm install --save-dev babel-preset-stage-1
npm install --save-dev babel-preset-stage-2
npm install --save-dev babel-preset-stage-3
ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
配置
在根目录中新建.babelrc
文件
{
"presets":[
"es2015",
"stage-2"
],
"plugins":[
"transform-object-assign",
"transform-object-rest-spread"
]
}
对应上面安装的几个套件
使用
babel example.js -o compiled.js
转译文件到文件
babel src -d dist
转译目录到目录
babel -w src -d dist
监听文件变化
相关资料
###Browserify###
Babel 的作用是帮助我们转换 ES6 代码为 ES5, 但是它没有模块管理的功能,浏览器端默认也无法识别 CommonJs 规范,这就需要我们额外使用模块打包工具,为我们的代码做一些包裹,让它能在浏览器端使用。 比如 Browserify, Webpack。
Browserify本身不是模块管理器,只是让服务器端的CommonJS格式的模块可以运行在浏览器端。这意味着通过它,我们可以使用Node.js的npm模块管理器。所以,实际上,它等于间接为浏览器提供了npm的功能。
Browserify编译的时候,会将脚本所依赖的模块一起编译进去。这意味着,它可以将多个模块合并成一个文件。
安装
npm install -g broswerify
使用
browserify main.js > bundle.js
相关资料
http://www.cnblogs.com/zjzhome/p/4848592.html
http://www.ruanyifeng.com/blog/2014/09/package-management.html
###Grunt###
grunt是一个打包构建工具,它可以自动运行你所设定的任务
安装
npm install -g grunt-cli
安装grunt-cli并不等于安装了 Grunt!Grunt CLI的任务很简单:调用与Gruntfile在同一目录中 Grunt。这样带来的好处是,允许你在同一个系统上同时安装多个版本的 Grunt。
npm init
npm init
会在目录下创建一个package.json这是npm包管理器的配置文件,打开这个文件,增加依赖项
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-nodeunit": "~0.4.1",
"grunt-contrib-uglify": "~0.5.0"
}
添加完依赖项后,就可以安装这些依赖项了,安装的方式放 npm install
npm install
Gruntfile
项目根目录下新建Gruntfile.js文件,这是grunt的配置文件,Gruntfile由一下几部分构成
- "wrapper" 函数
- 项目与任务配置
- 加载grunt插件和任务
- 自定义任务
下面一个简单的配置列子
module.exports = function(grunt){
//项目与任务配置
grunt.initConfig({
pkg:grunt.file.readJSON('package.json'),
uglify:{
options:{
banner:'/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %>*/\n'
},
build:{
src:'src/<%= pkg.name %>.js',
dest:'build/<%= pkg.name %>.min.js'
}
}
});
//加载插件
grunt.loadNpmTasks('grunt-contrib-uglify');
//执行任务列表
grunt.registerTask('default',['uglify']);
}
加载
grunt-contrib-uglify
插件,使用默认的uglify
任务,它的配置被指定在一个同名属性中,在uglify
配置中,有一个options.banner选项(用于在编译的文件顶部生成一个注释),紧接着是一个build
选项,用于将一个js文件压缩为一个目标文件。
src/test2.js
import {sum,square,variable,MyClass} from './import';
console.log(square(5));
var cred={
name:'Ritesh Kumar',
enrollmentNo:11115078
}
var x = new MyClass(cred);
console.log(x.getName());
运行 grunt
命令会在build目录下生成一个相同文件名.min.js的压缩文件。
相关资料
###es6打包构建###
我们使用上面提到的工具来一个整合使用,打包编译我们写好的es6语法程序,让程序支持es6语法和module模块。
1、第一步生成package.json
npm init
2、第二步安装打包依赖的工具,打开package.json文件增加依赖工具
"dependencies": {
"grunt": "^0.4.5"
},
"devDependencies": {
"babelify": "^6.1.0",
"grunt-browserify": "^3.8.0",
"grunt-contrib-watch": "^0.6.1"
}
3、安装这些依赖工具
npm install
4、创建 Gruntfile.js
module.exports=function(grunt){
grunt.initConfig({
browserify:{
dist:{
options:{
transform:[['babelify',{'loose':"all"}]]
},
files: {
'./dist/module.js':['./modules/index.js']
}
}
},
watch:{
scripts:{
files:['./modules/*.js'],
tasks:['browserify']
}
}
});
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default',["watch"]);
grunt.registerTask('build',["browserify"]);
};
5、开始编程
dist/index.html
<!DOCTYPE html>
<html>
<head>
<title>dome</title>
</head>
<body>
<script src="module.js"></script>
</body>
</html>
我们新建了一个index.html文件,引入了打包编译完成后的文件module.js
modules/import.js
var sum = (a, b = 6)=>(a + b);
var square = (b)=> {
"use strict";
return b * b;
};
var variable = 8;
class MyClass{
constructor(credentials){
this.name = credentials.name;
this.enrollmentNo = credentials.enrollmentNo
}
getName(){
return this.name;
}
}
export {sum,square,variable, MyClass};
我们新建了一个模块文件,使用es6语法导出了一个对象
modules/index.js
import {sum,square,variable,MyClass} from './import';
console.log(square(5)); //25
var cred={
name:'Ritesh Kumar',
enrollmentNo:11115078
}
var x = new MyClass(cred);
console.log(x.getName());
我们新建了一个js入口文件,引用了import.js里的导出对象
6、打包编译
grunt build
编译后在dist目录下生成了module.js
文件,这样我们就可以愉快的在modules目录下使用es6语法了,编译命令共干了三件事
- 使用grunt构建工具,让Browserify把所有文件打包成一个javascript文件
- 打包后的文件通过Babelify转换成es5代码
- 生成一个名为module.js的文件,可以运行在所有浏览器上
相关资料
###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.eventUtil = factory();
}
})(this, function () {
//module ...
});