一 模块化
- 每个文件都要有自己的变量作用域,两个模块之间的内部变量不会产生冲突;
- 不同模块之间保留相互导入和导出的方法,模块间能够相互通信.模块的执行与加载遵循一定的对方,能保证彼此之间的依赖关系;
二 JS模块化产生背景
- 在web开发早期,为了团队协作和代码维护的方便,许多开发者会将JavaScript代码分开写在不同的文件里面,然后,通过过个script标签加载他们.
- 虽然每个代码块处在不同的文件中,但最终所有js变量还是会处在一个同一个全局作用域下,这个时候就需要格外注意作用域变量提升所带来的问题.
- 如果两个代码块都声明相同的变量并为其赋值,则这个变量名的最终结果,就是按照加载顺序进行覆盖替换后的最终结果.
- 如果两个代码块存在依赖关系,我们处理好模块的加载顺序
- 我了解决这一系列问题:我们需要将这些
脚本文件``模块化
.
三 JS模块化规范
CommonJS
-
node.js是基于V8引擎,事件驱动I/O的服务端JS运行环境.
-
它实现了一套CommonJS的模块化规范.
-
在CommonJS规范里,每个JS文件就是一个模块,每个模块内部可以利用require函数和module.exports地偶像来对模块进行导入和导出;
-
CommonJS的包含了模块化的那些特征?
有处理模块变量作用域的能力 有导入导出模块的方式,能够处理基本的依赖关系 保证了模块单例
AMD
- 为了满足web开发的需要,如果在web端也使用同步加载,那么页面在解析脚本文件的过程中可能使页面暂停响应.
- 所以,web更适合使用AMD规范(Asynchronous module definition),也就是异步模块定义.
// index.js
require(['moduleA','moduleB'],function(moduleA,moduleB) {
console.log(moduleB);
})
// moduleA.js
define(function(require) {
var m = require('moduleB');
setTimeout(()=>console.log(m),1000)
})
// moduleB.js
define(function(require) {
var m = new Date().getTime();
return m;
})
- AMD默认异步,在回调函数中定义模块内容,相对来说使用就会麻烦些;
- AMD模块不能直接在node端运行,require函数配合在浏览器中加载的require.js这类AMD库才能使用;
UMD
- UMD(Universal Module Definition)作为一种
同构
的模块化解决方案,能够同时兼容AMD和Commonjs语法. - 同构,也就是说我们使用同一套代码就可以运行在两个不同的环境中,不需要分别维护多套代码;
- UMD通过检测当前环境的模块化规范,将模块内容进行输出,如果没有检测树规范则将模块内容挂载到全局对象上.
/* 这是一个立即执行函数(this=window or global,factory = moduleContent) */
(function(self, factory) {
/* 判断环境暴露的module接口是否为对象,module.exports 接口是否为对象,如果是则为CommonJS规范的环境 */
if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = factory(); // 将工厂函数复制给module.exports
/* 判断环境暴露的define接口是否为函数,且define.amd是否存在,如果是,则为AMD规范环境 */
} else if (typeof define === 'function' && define.amd) {
define(factory) // 将工厂函数作为参数调用define函数
/* 如果什么都不是,直接挂在全局变量umdModule上 */
} else {
self.umdModule = factory();
}
}(this, function() {
return function() {
return Math.random();
}
}));
ESModule规范
- CommonJS规范和AMD规范都是:
- 语言上层的宿主环境中定义的模块化规范;
- 相互之间不能共用模块;例如在nodejs中不能直接使用AMD模块,在浏览器中不能世界使用CommonJS模块
-
es6之后,js有了语言层面上的模块化规范,也就是esmodule规范,我们可以通过import和export两个关键词来对模块进行导入和导出.
-
所以与CommonJS和AMD最大的区别在于:
esmodule是由js解释器实现的,而后两者是由宿主环境中运行时实现的. esmodule的导入实际上是在语法层面上新增了一个语句,而AMD和CommonJS加载模块实际上是调用了require函数.
- 宿主环境与js解析器之间的关系
node.js 浏览器(w3c规范) 小程序
global document wx
process window swan
↑ ↑ ↑
_____________________________________
JS Core(ecmascript规范)
var a = 123;
function test() { console.log(true) }
- 类似于setTimeout和console这样的API大部分并不是JSCore层面实现的,只不过所有运行环境都实现了类似的效果
- setTimeout在ES7之后才进入了JScore层面
四 JS模块化展望
-
通过目前的分析我们可以看出,使用ESModule的模块明显符合JS开发的历史进程
-
因为任何一个支持JS的环境,随着对应解释器的升级,最终一定会支持ESModule的标准
-
但是web端受限制于用户使用的浏览器版本,我们不能随心所欲的随时使用js的最新特性.
-
为了能让我们代码也能运行在用户的老浏览器中,社区涌现出越来越多的工具,它们能静态将高版本规范的代码编译为低版本规范代码, 最为大家所熟知的就是
babel
; -
babel
把JS Core中最高版本规范的语法,也能按照相同语义在静态阶段转化为低版本规范的语法,这样即使早期的浏览器,他们内置的js解析器也能看懂;