来源于 ES6模块和CommonJS模块有哪些差异 的提问。
- 什么是JS模块化?
- 为什么要JS模块化?
- 如何实现JS模块化?
- 现有的JS模块化方案有哪些?
什么是JS模块化
JS模块化,就是将js中的逻辑进行拆分,分别在每一个局部作用域中去做,明确好上下游的依赖关系。
举个例子,我点击一个按钮
,发起一个请求
,请求过来一堆数据,此时需要发送日志
,这个数组需要经过一定的格式化
过程,将数据应用到对应的模板
上,在页面渲染出来。
Button.js // 按钮模块,里面绑定了点击事件,并向外暴露出一个函数做后续处理
Http.js // 请求模块,所有的请求通过这个模块暴露出的方法使用
Log.js // 日志模块,可以放到Http模块里,监控http发送的状态和内容
Format.js // 格式化模块,这块和业务相关,可以做成纯函数,格式化出自己想要的数据结构
Template.js // 模板模块,传入模板和数据,返回需要渲染的HTML
为什么要JS模块化
模块的优点
- 可维护性。 因为模块是独立的,一个设计良好的模块会让外面的代码对自己的依赖越少越好,这样自己就可以独立去更新和改进。
- 命名空间。 在 JavaScript 里面,如果一个变量在最顶级的函数之外声明,它就直接变成全局可用。因此,常常不小心出现命名冲突的情况。使用模块化开发来封装变量,可以避免污染全局环境。
- 重用代码。 我们有时候会喜欢从之前写过的项目中拷贝代码到新的项目,这没有问题,但是更好的方法是,通过模块引用的方式,来避免重复的代码库
如何实现JS模块化
// 将各个功能方法封装在一个单独的对象中
var Http = function(){
var func1 = () => {};
var func2 = () => {};
return {
func1,
func2
}
}
var Log = function(){
var func1 = () => {};
var func2 = () => {};
return {
func1,
func2
}
}
// 使用方法,虽然func1都是同名,但是封装在不同的对象里,不会污染全局
Http.func1();
Log.func1();
现有的JS模块化方案有哪些
以下是现有的一些关于JS模块的解决方案
CommonJS
CommonJS是一个项目,其目标是为JavaScript在网页浏览器之外创建模块约定。创建这个项目的主要原因是当时缺乏普遍可接受形式的JavaScript脚本模块单元,模块在与运行JavaScript脚本的常规网页浏览器所提供的不同的环境下可以重复使用。 — wiki百科
Node 应用由模块组成,采用 CommonJS 模块规范。
使用方法:
// module1.js
var module1 = function(param){
console.log('module1', param)
};
var MODULE_VAR = 'module1';
module.exports = {
module1,
MODULE_VAR
}
// module2.js
var { module1, MODULE_VAR } = require('./module1.js');
module1('module2'); // 输出 'module1, module2'
CommonJS规范,通过require导入,module.exports导出,导出的内容本身是一个对象。
module 本身是node提供的一个api,其中包含以下属性
|
| - children 当前模块被用于的模块
| - exports 当前模块导出的接口,本身是个对象
| - filename 带有绝对路径的文件名
| - id 当前模块id
| - loaded 当前模块是否已经被加载
| - parent 当前模块引入的模块
| - paths 多种类型的文件路径
模块循环依赖
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';
// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';
// 执行node b
输出
a.js b1
b.js a2
模块只加载一部分
CommonJS模块的特点如下:
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
具体内容可以查看 阮一峰的博客
AMD规范
CMD规范
ES6规范
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
使用方法:
// module1.js
var moduleName = '鬼谷中妖';
var module1 = function(){
console.log('words', words);
};
export {
moduleName as nn,
module1 as n1
};
// module2.js
import { nn, n1 } = from './module1.js';
n1(nn); // words,'鬼谷中妖'
具体内容可以查看 阮一峰的博客模块
回到提问:
- 使用语法不同
- CommonJS,导入导出的是对象,是动态引用。ES6,导出的是当前使用其他模块方法的代码,属于静态编译,类似C#应用,在编译时就会提示语法错误。
https://github.com/YvetteLau/Step-By-Step/issues/43
参考: