前端模块化发展了10余年左右,前前后后出现了commonJS,AMD,CMD等多种模块化标准方案,目前最新的是ES module。下面大概讲述一下这十年左右的发展历程~
最开始JS是没有模块化的,由于前端页面越来越复杂,项目越来越大,催生了模块化的概念。
前端模块化是一种开发管理规范,用分割模块的方式来管理优化代码。每个模块有它特定的功能,模块间相互隔离,但可以通过特定接口访问内部成员,也可以依赖其他模块。
(1)在没有模块化时:
<html>
<head>
<meta charset="utf-8">
head>
<body>
<div id="app">div>
<script>
console.log(2)
script>
body>
html>
我们所有的业务代码,都通过script
标签内嵌到HTML中,代码质量随着业务的扩大变得无法保证。
(2)最初的模块化方案是:
<html>
<head>
<meta charset="utf-8">
head>
<body>
<div id="app">div>
<script src="a.js">script>
<script src="b.js">script>
<script src="index.js">script>
body>
html>
将代码写在外部JS文件中,一个JS文件为一个模块,通过script src
引入。这样组织代码带来了一些好处:
虽然这样可以将不同的功能模块分割,但明显不是很好的解决方案,仍然存在一些弊端:
(3)为了解决作用域的问题,之后出现了闭包立即执行函数
来定义一个模块:
;(function(){
...})()
立即执行函数的作用域是独立的,这样就避免了上面存在的污染全局的问题。但模块依赖的加载顺序问题依然存在。
因此,我们需要比较标准的模块化方案来解决这些问题。
随着V8引擎的诞生,诞生了Node,Node 应用采用的是 CommonJS 模块规范。此规范特点:
require
引入,module.exports
或者exports
导出(输出的是值的拷贝)由于此规范无法在浏览器环境中使用,所以之后又催生了一些适用浏览器环境的规范,AMD CMD。
当然AMD只是一种规范,实现此规范的库是RequireJS,特点:
define(moduleName, [module], factory)
定义模块,一个文件可设置多个模块require([module], callback)
引入模块之后阿里也对模块化做了贡献,推出了符合CMD规范的SeaJS库(现已不再维护),特点:
define(function(require, exports, module){})
定义模块seajs.use([module路径],function(moduleA,moduleB,moduleC){ })
使用模块CommonJS AMD CMD 都不是ES官方给出的规范,直到2015年推出了ES6版本,从语言标准层面实现模块功能,作为前后端通用的解决方案。特点:
import
导入模块export
导出模块浏览器端使用ES6模块:
对于type="module"
的,浏览器会执行异步加载,不会阻塞浏览器。
<script type="module" src="a.js">script>
由于ES6+语法在浏览器和node(node13+支持,package.json
配置"type":"module"
)支持方面还不是很友好,通常需要babel
才能使用。