前端模块化详解(CommonJS、AMD、CMD、ES Module)

前端模块化详解(CommonJS、AMD、CMD、ES Module),第1张

前端模块化详解(CommonJS、AMD、CMD、ES Module)

大家好,我是一碗周,一个不想被喝(内卷)的前端。如果写的文章有幸可以得到你的青睐,万分有幸~

写在前面

随着前端项目的不断复杂,代码日益膨胀,项目的维护难度随之越来越大,此时模块化也就相继的出现了,本篇文章将会介绍如下内容:

  • 模块化的概念以及演变过程
  • 模块化规范
    • CommonJS
    • AMD
    • CMD
    • ES module
模块化的概念以及演变过程 什么是模块化

模块化就是将一个复杂的程序依据一定的规则或者说是规范,将其封装成几个单独的块(这里的块指的就是文件),在使用的时候将其组合在一起。

块内部的数据是私有的,只是向外部暴露一些接口或者说是一些方法,让其与其他模块进行通信。

模块化的演变过程

早期的前端技术标准根本没有预料到前端会有现在一个规模,所以说很多设计上遗留的问题就导致了现在去实现前端模块化的时候会遇到很多的困难。

虽然说模块化现在已经被一些标准或者工具去解决了,但是它的一个演变过程还是值得我们去思考的。

模块化演变的过程其实就是前端领域的实践过程,这个过程大致可以分为四个阶段:

文件划分方式

文件划分方式是最原始的模块系统,具体做法就是将一个功能以及它相关的一些状态单独存在不同的文件中,每一个文件就代表一个模块。使用这个模块就是将这个模块文件引入页面文件中,一个

使用这种方式的缺点很明显,如下:

  • 模块内部的成员都处在全局作用域中,任意位置都可以进行访问和修改,这样就造成了污染全局作用域。
  • 命名容易冲突。
  • 没有办法很好的管理模块间的依赖关系
命名空间方式

命名空间方式就是在第一个阶段的基础上约定每一个模块只暴露一个全局对象,所有模块的成员都挂载到这个对象的下面。

示例代码如下:

component/module_a.js

let moduleA = {
  name: '一碗周',
  handle() {
    console.log(this.name)
  },
}

component/module_b.js

let moduleB = {
  name: '一碗粥',
  handle() {
    console.log(this.name)
  },
}

index.html


  
  
  

通过这种方式减少了命名冲突的可能,但是仍然没有私有空间,且模块之间的依赖关系还是没有进行解决。

IIFE模式

所谓的IIFE模式就是使用立即执行函数去创建闭包,这种方式为模块提供了私有空间。

具体的做法就是将模块中每一个成员都放在一个函数提供的私有作用域当中,对于需要暴露给外部的成员可以通过挂载到全局对象上的方式去实现。这种方式实现了私有成员的概念,就是说模块的私有成员只能在模块内部通过闭包的方式去访问而在外部,是没有办法去使用使用的这样就确保了私有成员的安全。

示例代码如下:

component/module_a.js

;(function () {
  let name = '一碗周'
  function handle() {
    console.log(name)
  }
  window.moduleA = { handle }
})()


component/module_b.js

;(function () {
  let name = '一碗粥'
  function handle() {
    console.log(name)
  }
  window.moduleB = { handle }
})()

index.html


  
  
  


发展到这个阶段,就已经实现了私有成员的概念了,但是模块间的依赖关系还是没有解决。

IIFE依赖参数

我们通过为立即执行函数添加参数的形式可以实现模块间的依赖,示例代码如下:

component/module_a.js

;(function () {
  function printName(name) {
    console.log(name)
  }
  // 暴露一个打印的方法
  window.moduleA = { printName }
})()

component/module_b.js

;(function (m)  {
  let name = '一碗周'
  function sayName() {
    // 使用其他模块的成员
    m.printName(name)
  }
  window.moduleB = { sayName }
})(moduleA) // 实参


index.html


  
  
  


以上4个阶段就是早期开发者在没有工具和规范的情况下,对模块下进行的落地方式。

但是这种方式还是存在问题的,如下:

  • 引入多个 CMD

    CMD规范是在sea.js推广中形成的,与AMD类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。

    目前CMD已经不再用了,这里就不在介绍了。

    ES Module 概述

    在ES6之前, Javascript一直没有一个官方提供的模块化的体系,所使用的都是社区所提供的,例如CommonJS和AMD等。但是在ES6的时候,ECMA提出了ESmodule规范,即原生的模块化体系。但是原生提供的模块化体系的兼容性并不是很好,下图展示了ESModule的浏览器兼容性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HJvZjvV3-1639636102254)(image/01_ESmodule%E5%85%BC%E5%AE%B9%E6%80%A7.png)]

    从上图中我们可以看到,IE浏览器完全不支持(虽然现在IE几乎已经死掉了),随着时间的推移,想Webpack这种打包工具的流行,慢慢的,ESmodule就进入大家的视野了。

    语法特性

    如果想要在HTML中使用使用ES Module的话,需要为

保存