模块打包之CommonJS与ES6模块比较初探
Time: 20190920
模块是具有特定功能的组成单元,不同模块负责不同的工作,然后会以某种方式联系到一起,形成完整的程序逻辑。
CommonJS
CommonJS是2009年社区提出的,包含模块、文件、IO和控制台在内的一系列标准。
Node.js采用的是CommonJS标准的一部分,并在此基础上做了一些变化。
CommonJS标准,最初是为服务端设计,而有了Browserify
后,可以将CommonJS模块打包为在浏览器下可以运行的单个文件。
导致的结果是:客户端的代码可以按照CommonJS标准来写。
Browserify
是运行在Node.js环境下的模块打包工具。
此外,借助于包管理工具,前端开发开始流行用CommonJS标准了。
模块的定义
CommonJS中,每个文件是一个模块。
通过script
标签引入和封装成CommonJS模块的区别是什么?
前者:顶层作用域是全局作用域,变量声明和函数声明时会污染全局环境。
后者:形成属于模块自身的作用域,变量和函数仅自己可访问,外部不可见。
导出
导出是模块向外暴露自身的唯一方式。
CommonJS标准中,导出模块的语法是:
module.exports = {}
比如:
module.exports = {
name: 'calculator',
add: function(a, b) {
return a + b;
}
}
这个module
关键字从哪里来呢?可以理解为,CommonJS模块内部会存一个module
对象用于存储当前模块的信息。
module.exports
用于指定模块对外暴露的内容,上面的代码导出了两个属性。
CommonJS也支持一种简单的导出语法:
exports.name = 'calculater';
exports.add = function(a, b) {
return a + b;
}
可用下面的代码来理解:
var module = {
exports: {},
}
var exports = module.exports;
因此,一个是赋值语法,一个是添加属性的方式,不可以直接对exports
赋值,这会导致失效,即和module
这个对象失去了关联。
混用情况辨析
exports.add = function(a, b) {
return a + b;
}
module.exports = {
name: "calculator"
}
上面最后只会导出一个name
属性出来,add
函数丢失。
还有一个小点需要注意的是,导出语句语法不表示代码的结尾,后面写的逻辑也会照常执行。
但一般比较好的习惯是,将导出语法写到文件末尾,这样的约定,代码比较好读。
导入
CommonJS采用的导入语法是:require
。
// calculator.js
module.exports = {
add: function(a, b) {
return a + b;
}
}
// index.js
const cal = require('./calculator.js')
const sum = cal.add(2, 3)
console.log(sum) // 5
require模块的两种情况
- 第一次加载,首先执行此模块,导出内容
- 第二次加载,此模块的代码不会在执行,导出上次的结果
就是会用模块的module
对象记录一个属性,module.loaded
。
require
可以只执行,产生作用,而不用它返回的内容。
另外,require
可以接收表达式,可动态指定模块的加载路径。
看到这里,我们应该可以想到import/export
这种语法,是的,我们下面就进入ES6模块标准~
ES6模块标准
ES6是JavaScript发展的一个转折点。因为JS诞生之初就没有模块的概念。
ES6是JS官方委员会正式采纳的模块化标准。
// calculator.js
export default {
name: 'calculator',
add: function(a, b) {
return a + b;
}
}
// index.js
import calculator from './calculator.js' // .js可以省略
const sum = calculator.add(2,3)
console.log(sum) // 5
在ES6模块化标准中,也是将每个文件当作一个模块,每个模块有自己的作用域。
ES6默认采用的是严格模式,即默认有use strict
的效果。
导出
ES6语法下,导出关键字是export
,有两种形式:
- 命名导出,可导出多个
- 默认导出,只能导出一个
命名导出
export const name = "calculator";
export const add = function(a, b) { return a + b };
上面是分别导出,还可以用下面的写法,统一导出:
const name = 'calculator';
const add = function(a, b) { return a + b };
export { name, add };
两种写法效果相同。
另外,命名导出时可以用as
重命名。
const name = 'calculator';
const add = function(a, b) { return a + b };
export { name, add as getSum };
默认导出
只能导出一个。
export default {
name: "calculator",
add: function(a, b) {
return a + b;
}
}
这种export default
表示对外导出一个default
变量,所以无需变量声明,直接导出了值。
// 导出字符串
export default 'This is String';
// 导出类
export default class {...}
// 导出匿名函数
export default function() {...}
导入
ES6语法下,用import
来导入。
导入命名模块
// calculator.js
const name = 'calculator';
const add = function(a, b) { return a + b; };
export { name, add };
// index.js
import { name, add } from './calculator.js';
add(2, 3);
导入命名模块有两个要求:
- import后面要跟一对大括号将导入的变量名包裹起来
- 导入的变量名和导出的变量名完全一致
命名导出时可用as
关键字进行重命名,导入时也可以。
import { name, add as getSum } from './calculator.js'
整体导入 :import * from xxx
将整体导入的结果赋予一个对象,从而导入的变量是该对象的属性。
导入默认模块
// calculator.js
export default {
name: "calculator",
add: function(a, b) { return a + b }
}
// index.js
import myCalculator from './calculator.js'
myCalculator.add(2, 3)
默认导出,在导入时,直接在import
后面跟一个变量名,此变量名*指定,用于指代默认导出的值。
从形式上来说,加大括号包围的导入导出是命名的,裸的,不加大括号的是默认导入导出。
在React项目中,常见:
import React, { Component } from 'react';
就是二者的混用,即React
是默认导出,而Component
是命名导出的变量。
CommonJS和ES6的比较
本质区别:动态与静态
CommonJS对模块依赖的解决方式是动态的,ES6对模块依赖的解决是静态的。
动与静: 动表示,模块的依赖关系在代码发生阶段。静表示模块的依赖关系建立在代码编译阶段。
在CommonJS中,require('./calculator.js')
会将该模块中module.exports
导出的结果加载进来,模块的路径可以动态加载,且支持传入表达式,还可以用代码逻辑判断是否加载模块等,非常灵活,是在模块被执行前判断不了的依赖关系。
所以说,在CommonJS中,模块的导入、导出在代码的运行阶段发生。
ES6模块则是声明式的写法,导入路径不能是表达式,且必须在模块的顶层作用域。
即,ES6是静态的模块写法,编译时就知道了模块的依赖的关系。
ES6模块相对于CommonJS的几个优点
- 死代码的检测与排除
- 模块变量的类型检查
- 编译器优化
关于ES6和CommonJS标准的比较,下文继续展开。
END.
上一篇: ES6 Reflect初探
下一篇: 有道搜索外包给360
推荐阅读
-
ES6与CommonJS中的模块处理的区别
-
详谈commonjs模块与es6模块的区别
-
javascript模块化之CommonJS、AMD、CMD、UMD、ES6
-
javascript模块化之CommonJS、AMD、CMD、UMD、ES6
-
模块化之CommonJs、AMD、CMD和ES6模块化
-
前端模块化之CommonJS,ES6,AMD,CMD
-
JS模块化加载之CommonJS、AMD、CMD、ES6
-
javascript模块化之CommonJS、AMD、CMD、UMD、ES6
-
前端模块化,AMD,CMD,ES6 Module,CommonJS,ES6 模块与CommonJS 模块的差异
-
js模块化之CommonJS、AMD、CMD、ES6