JS模块规范(CommonJS,AMD,CMD,UMD,ES6)

#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执行流程

  1. require函数检查依赖的模块,根据配置文件,获取js文件的实际路径
  2. 根据js文件实际路径,在dom中插入script节点,并绑定onload事件来获取该模块加载完成的通知。
  3. 依赖script全部加载完成后,调用回调函数

###CMD###

sea.js就实现了CMD规范,官方网址是:http://seajs.org/docs/#docs

使用方式和AMD类似

define(function(require,exports,module){...});

SeaJS对模块的态度是懒执行, SeaJS只会在真正需要使用(依赖)模块时才执行该模块。

SeaJS执行流程

  1. 通过回调函数的Function.toString函数,使用正则表达式来捕捉内部的require字段,找到require('jquery')内部依赖的模块jquery
  2. 根据配置文件,找到jquery的js文件的实际路径
  3. 在dom中插入script标签,载入模块指定的js,绑定加载完成的事件,使得加载完成后将js文件绑定到require模块指定的id(这里就是jquery这个字符串)上
  4. 回调函数内部依赖的js全部加载(暂不调用)完后,调用回调函数
  5. 当回调函数调用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

监听文件变化

相关资料

http://www.jianshu.com/p/ff7e0de573a9

http://www.ruanyifeng.com/blog/2016/01/babel.html

###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的压缩文件。

相关资料

http://www.gruntjs.net/getting-started

###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的文件,可以运行在所有浏览器上

相关资料

http://www.cnblogs.com/zjzhome/p/4848592.html

###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 ...
});

转载于:https://my.oschina.net/tongjh/blog/836721

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值