模块化的演进过程
Stage 1 - 文件划分方式
缺点:
- 模块直接在全局工作,大量模块成员污染全局作用域;
- 没有私有空间,所有模块内的成员都可以在模块外部被访问或者修改;
- 一旦模块增多,容易产生命名冲突;
- 无法管理模块与模块之间的依赖关系;
- 在维护的过程中也很难分辨每个成员所属的模块。
Stage 2 – 命名空间方式
我们约定每个模块只暴露一个全局对象,所有模块成员都挂载到这个全局对象中
这种命名空间的方式只是解决了命名冲突的问题,但是其它问题依旧存在。
Stage 3 – IIFE
使用立即执行函数表达式(IIFE,Immediately-Invoked Function Expression)为模块提供私有空间。具体做法是将每个模块成员都放在一个立即执行函数所形成的私有作用域中,对于需要暴露给外部的成员,通过挂到全局对象上的方式实现。
这种方式带来了私有成员的概念,私有成员只能在模块成员内通过闭包的形式访问,这就解决了前面所提到的全局作用域污染和命名冲突的问题。
Stage 4 - IIFE 依赖参数
在 IIFE 的基础之上,我们还可以利用 IIFE 参数作为依赖声明使用,这使得每一个模块之间的依赖关系变得更加明显。
更为理想的方式应该是在页面中引入一个 JS 入口文件,其余用到的模块可以通过代码控制,按需加载进来。
模块化规范的出现
两点需求
- 一个统一的模块化标准规范
- 一个可以自动加载模块的基础库
模块化的标准规范
JavaScript 的标准逐渐走向完善
前端模块化规范的最佳实践方式也基本实现了统一:
- 在 Node.js 环境中,我们遵循 CommonJS 规范来组织模块。
- 在浏览器环境中,我们遵循 ES Modules 规范。
模块打包工具的出现
随着模块化思想的引入,我们的前端应用又会产生了一些新的问题
- 首先,我们所使用的 ES Modules模块系统本身就存在环境兼容问题。尽管现如今主流浏览器的最新版本都支持这一特性,但是目前还无法保证用户的浏览器使用情况。所以我们还需要解决兼容问题。
- 其次,模块化的方式划分出来的模块文件过多,而前端应用又运行在浏览器中,每一个文件都需要单独从服务器请求回来。零散的模块文件必然会导致浏览器的频繁发送网络请求,影响应用的工作效率。
- 最后,谈一下在实现 JS 模块化的基础上的发散。随着应用日益复杂,在前端应用开发过程中不仅仅只有 JavaScript代码需要模块化,HTML 和 CSS 这些资源文件也会面临需要被模块化的问题。而且从宏观角度来看,这些文件也都应该看作前端应用中的一个模块,只不过这些模块的种类和用途跟 JavaScript 不同。
写在最后