前端模块化发展

103 阅读5分钟

前端模块化—— 文件划分,通过 HTML 中的 script 来一一引入,分散了不同模块的状态和运行逻辑,弊端有哪些?

  • 1、模块变量相当于在全局声明和定义,会有变量名冲突的问题。 比如 module-b 可能也存在 data 变量,这就会与 module-a 中的变量冲突。

  • 2、由于变量都在全局定义,我们很难知道某个变量到底属于哪些模块,因此调试非常困难。

  • 3、无法清晰地管理模块之间的依赖关系和加载顺序。 假如 module-a 依赖 module-b,那么上述 HTML 的 script 执行顺序需要手动调整先引入 module-b ,再引入 module-a ,不然可能会产生运行时错误。

命名空间 和 IIFE 优化 文件划分 实现模块化

  • 命名空间 和 IIFE,解决了全局变量所带来的命名冲突及作用域不明确的问题,也就是在文件划分方式中所总结的问题 1 和问题 2,
  • 命名空间 和 IIFE,并没有真正解决另外一个问题——模块加载。如果模块间存在依赖关系,那么 script 标签的加载顺序就需要受到严格的控制,一旦顺序不对,则很有可能产生运行时 Bug。

CommonJS 规范

  • CommonJS 是业界最早正式提出的 JavaScript 模块规范

    • 统一的模块化代码规范
    • 实现自动加载模块的加载器(也称 loader)
  • 主要用于服务端,随着 Node.js 越来越普及,这个规范也被业界广泛应用。

  • 简单应用: require 来导入一个模块,用 module.exports 来导出一个模块。

  • 产生的问题:

    • 模块加载器由 Node.js 提供,依赖了 Node.js 本身的功能实现,比如文件系统,如果 CommonJS 模块直接放到浏览器中是无法执行的。当然, 业界也产生了 browserify 这种打包工具来支持打包 CommonJS 模块,从而顺利在浏览器中执行,相当于社区实现了一个第三方的 loader。
    • CommonJS 本身约定以同步的方式进行模块加载,这种加载机制放在服务端是没问题的,一来模块都在本地,不需要进行网络 IO,二来只有服务启动时才会加载模块,而服务通常启动后会一直运行,所以对服务的性能并没有太大的影响。但如果这种加载机制放到浏览器端,会带来明显的性能问题。它会产生大量同步的模块请求,浏览器要等待响应返回后才能继续解析模块。也就是说,模块请求会造成浏览器 JS 解析过程的阻塞,导致页面加载速度缓慢。

AMD 规范

  • AMD 全称为 Asynchronous Module Definition,即异步模块定义规范。

  • 模块根据这个规范,在浏览器环境中会被异步加载,而不会像 CommonJS 规范进行同步加载,也就不会产生同步请求导致的浏览器解析过程阻塞的问题了

  • 简单应用:

  • 在 AMD 规范当中,我们可以通过 define 去定义或加载一个模块,比如上面的 main 模块和 print 模块,如果模块需要导出一些成员需要通过在定义模块的函数中 return 出去(参考 print 模块),如果当前模块依赖了一些其它的模块则可以通过 define 的第一个参数来声明依赖(参考 main 模块),这样模块的代码执行之前浏览器会先加载依赖模块。

AMD.png 当然,你也可以使用 require 关键字来加载一个模块,如: 不过 require 与 define 的区别在于前者只能加载模块,而不能定义一个模块。

AMD2.png

  • 缺点:
    • 由于没有得到浏览器的原生支持,AMD 规范需要由第三方的 loader 来实现
    • AMD 规范使用起来稍显复杂,代码阅读和书写都比较困难。

UMD

  • UMD (Universal Module Definition)规范
  • 不算一个新的规范,只是兼容 AMD 和 CommonJS 的一个模块化方案
  • 可以同时运行在浏览器和 Node.js 环境

ES6 Module

  • ES6 Module 也被称作 ES Module(或 ESM), 是由 ECMAScript 官方提出的模块化规范,作为一个官方提出的规范,ES Module 已经得到了现代浏览器的内置支持。
  • 在现代浏览器中,如果在 HTML 中加入含有type="module"属性的 script 标签,那么浏览器会按照 ES Module 规范来进行依赖加载和模块解析,这也是 Vite 在开发阶段实现 no-bundle 的原因,由于模块加载的任务交给了浏览器,即使不打包也可以顺利运行模块代码。
  • ES Module 的浏览器兼容性如今已经相当好了,覆盖了 90% 以上的浏览器份额,可在 CanIUse 上查询。
  • 一直以 CommonJS 作为模块标准的 Node.js 也紧跟 ES Module 的发展步伐,从 12.20 版本开始正式支持原生 ES Module。也就是说,如今 ES Module 能够同时在浏览器与 Node.js 环境中执行,拥有天然的跨平台能力。
  • 简单应用: es6_1.png es6_2.png 如果在 Node.js 环境中,你可以在 package.json 中声明 type: "module"属性

es6_4.png 在 Node.js 中,即使是在 CommonJS 模块里面,也可以通过 import 方法顺利加载 ES 模块,如下所示:

es6_3.png

参考资料: 掘金小测