详解Js中的模块化是如何实现的(3)

要知道,设置了 <script> 标签的 src 之后,代码一旦下载完成,就会立即执行,根本由不得你再封装成闭包,所以文件模块需要在定义之初就要做文章,这就是我们说熟知的 AMD/CMD 规范中的 define,开篇的 add.js 需要重新改写一下

// add.js 文件
define ('add',function () {
  function add (a, b) {
   return a + b;
  }
  return add;
})

而对于 define 的实现,最重要的就是将 callback 的执行结果注册到 context 的一个模块数组中:

  context.modules = {}
  function define(name, callback) {
    context.modules[name] = callback && callback()
  }

于是 require 就可以从 context.modules 中根据模块名载入模块了,是不是有了一种自己去写一个 “requirejs” 的冲动感

具体的 AMD 实现当然还会复杂很多,还需要控制模块载入时序、模块依赖等等,但是了解了这其中的灵魂,想必去精读 require.js 的源码也不是一件困难的事情

四、Webpack 中的模块化

Webpack 也可以配置异步模块,当配置为异步模块的时候,在浏览器环境同样的是基于动态插入 <script> 的方式载入远程模块。在大多数情况下,模块的载入方式都是类似于 Node 的本地磁盘同步载入的方式

嫑忘记,Webpack 除了有模块化的能力,还是一个在辅助完善开发工作流的工具,也就是说,Webpack 的模块化是在开发阶段的完成的,使用 Webpack 构筑的工作环境,在开发阶段虽然是独立的模块文件,但是在运行时,却是一个合并好的文件

所以 Webpack 是一种在非运行时的模块化方案(基于 CommonJs),只有在配置了异步模块的时候对异步模块的加载才是运行时的(基于 AMD)

五、模块化规范

通用的问题在解决的过程中总会形成规范,上文已经多次提到 CommonJs、AMD、CMD,有必要花点篇幅来讲一讲规范

Js 的模块化规范的萌发于将 Js 扩展到后端的想法,要使得 Js 具备类似于 Python、Ruby 和 Java 那样具备开发大型应用的基础能力,模块化规范是必不可少的。CommonJS 规范的提出,为Js 制定了一个美好愿景,希望 Js 能在任何地方运行,包括但不限于:

  • 服务器端 Js 应用
  • 命令行工具
  • 桌面应用
  • 混合应用

CommonJS 对模块的定义并不复杂,主要分为模块引用、模块定义和模块标识

  1. 模块引用:使用 require 方法来引入一个模块
  2. 模块定义:使用 exports 导出模块对象
  3. 模块标识:给 require 方法传入的参数,小驼峰命名的字符串、相对路径或者绝对路径