02 Web全栈 模块化

JS模块化

在web开发的早期,为了团队协作和代码维护的方案,许多开发者会选择JavaScript代码分开写在不同的文件里面,然后通过多个script标签来加载他们

<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>

虽然每个代码块处在不同的文件中,但最终所有的js变量还是会处在同一个全局作用域下,这时候就需要额外注意用于作用域变量提升所带来的的问题

<script>
	// a.js
	var num = 1
	setTimeout(() => console.log(num), 1000)
</script>
<script>
	// b.js
	var num = 2
</script>

在这个例子中,我们分别加载了两个script标签,两段js都声明了num变量,第一段脚本的本意本来是希望在1s后打印自己的num变量1,但最终运行结果缺打印了第二段中num变量2,虽然两段代码写在不同的文件中,但是因为运行时声明变量都在全局下 最终产生了冲突

同时,如果代码块之间有依赖关系的话,需要额外关注脚本的加载顺序,如果文件依赖顺序有改动,就需要在html手动变更加载标签的顺序,非常麻烦

需要解决这样的问题,我们就需要将这些脚本文件模块化

  1. 每个模块都要有自己的变量作用域,两个模块之间的内部变量不会产生冲突
  2. 不同模块之间保留相互代入和导出的方式方法, 模块间能够相互通信,模块的执行与加载遵循一定的规范能保证彼此之间的依赖关系。

CommonJs规范

Nodejs就是一个基于V8引擎,事件驱动I/O的服务端JS运行环境,在2009年刚推出时,他就实现了一套名为CommonJS的模块化规范。
在CommonJS规范里,每个JS文件就是一个模块,每个模块内部可以使用require函数和module.exports对象来对模块进行导入和导出

// index.js
require("./moduleA")
var m = require("./moduleB")
console.log(m)

// moduleA.js
var m = require("./moduleB")
setTimeout(() => console.log(m), 1000)

// moduleB.js
var m = new Date().getTime()
module.exports = m

CommonJs模块只能在Nodejs环境中才能运行,直接在其他环境中运行这样的代码就会报错,这是因为只有node才会在解析JS的过程中提供一个require方法,这样当解析器质性代码时,发现有模块调用了require函数,就会通过参数找到对应模块的物理路径,通过系统调用从硬盘读取文件内容,解析这段内容最终拿到导出结果并返回,而其他运行环境并不一定会在解析时提供这么一个require方法,也就不能直接运行这样的模块了

从它的执行过程也可以看出来CommonJS是同步加载模块的模块化规范,每当一个模块require一个子模块式时都会停止当前模块解析知道子模块读取解析并加载

AMD 模块化规范

AMD 全称Asynchronous module definition,意味异步模块定义,不同于CommonJs规范的同步加载,AMD正如其名所有模块默认都是异步加载,这也是早期为了满足web开发的需要,因为如果在web端也使用同步加载,那么页面在解析脚本文件过程中可能使页面暂停响应

// index.js
require(['moduleA', 'moduleB'], (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
})

UMD能同时被CommonJS规范和AMD规范加载

写一个UMD模块也比较简单,只需要判断下模块化规范的特征值,判断出当前究竟在哪种模块化规范的环境下,然后把模块内容检测出的模块化规范语法导出即可

// CommonJS
module.exports = function () {
	return Math.random()
}

// AMD
define(function(require) {
	return function () {
		return Math.random()
	}
})

// UMD
(function(self, factory) {
	if (typeof module === 'object' && typeof module.exports === 'object') {
		module.exports = factory()
	} else if (typeof define === 'function' && define.amd) {
		define(factory)
	} else {
		self.umdModule = factory()
	}
})(this, function () {
	return function () {
		return Math.random
	}
})

ESModule规范

CommonJS规范和AMD规范有这么几个特点:

  1. 语言上层的运行环境中实现模块化规范,模块化规范由环境自己定义
  2. 相互之间不能共用模块,例如不能在Nodejs中运行AMD模块,不能在浏览器运行CommonJS模块。

在ES6之后,JS有了语言层面的模块化导入导出关键词与语法以及与之匹配的ESModule规范,使用ESModule规范,我们可以通过import和export两个关键词来对模块进行导入与导出

// index.js
import './moduleA'
import m from './mmoduleB'
console.log(m)

// moduleA.js
import m from './moduleB'
setTimeout(() => console.log(m), 1000)

// moduleB.js
var m = new Date().getTime()
export default m

ESModule与CommonJS和AMD最大的区别在于,ESModule是由JS解释器实现,而后两者是在宿主环境中运行时实现,ESModule导入实际上是在语法层面新增了一个语句,而AMD和CommonJS加载模块实际上是调用了require函数

后模块化时代

通过前面的分析我们可以看出来,使用ESModule的模块明显更符合JS开发的历史进程,因为任何一个支持JS的环境,随着对应解释器的升级,最终一定会支持ESModule的标准,但是,WEB端受制于用户使用的浏览器版本,我们并不能随心所欲的随时使用JS的最新特性,为了能让我们的新代码也运行在用户的老浏览器中,社区涌现出了越来越多的工具,他们能静态的将高版本规范的代码编译为低版本规范的代码,最为大家所熟知的就是babel。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值