【前端模块化规范】简练对比总结

557 阅读4分钟

前端模块化规范对比总结

用最简洁的方式了解模块化规范的特点

为什么要模块化,它解决了什么问题?

  • 解决了命名冲突:每个模块独立,有自己的作用域
  • 提高可维护性:将代码拆分,利于维护
  • 性能优化:如异步加载
  • 模块版本管理

主流模块化标准

  • CommonJS
  • AMD
  • CMD
  • UMD
  • ES6

CommonJS

实现:NodeJS

使用module.exports暴露变量,函数

 // num.js
 var a = 1
 var b = 2
 var add = function (){
   return a + b
 }
 ​
 // 导出
 module.exports.a = a
 module.exports.b = b
 module.exports.add = add
 var num = require('./num.js')
 ​
 console.log(num.a) // 1
 console.log(num.b) // 2
 console.log(num.add(a,b)) // 3

CommonJS特点

  • 每个文件都是一个独立的模块,有独立作用域,不会污染全局作用域
  • 模块可被重复引用,但只会在第一次加载时运行一次,此后加载就直接读取缓存结果。要重新运行模块必须清除缓存
  • modules.exports输出的是值的拷贝,一旦值被除数,模块内再发生并不爱不影响已经输出的值
  • 同步加载,只有引入的模块加载完成,才会执行后面的操作。在服务端问题不大,但在浏览器中不合适

AMD规范

实现:require.js

专门为浏览器环境设计,异步加载模块

语法如下:

 define(id?: String, dependencies?: String[], factory: Function|Object)
 复制代码
  • id 即模块的名字,字符串,可选
  • dependencies 指定了所要依赖的模块列表,它是一个数组,也是可选的参数,每个依赖的模块的输出将作为参数一次传入 factory 中。如果没有指定 dependencies,那么它的默认值是 ["require", "exports", "module"]
  • factory 包裹了模块的具体实现,可为函数或对象,如果是函数,返回值就是模块的输出接口或者值

导入

 const foo = require("./xxx")

导出

 module.exports = { ... }

AMD特点

  • 异步加载,加载完成后可以执行回调

  • 动态创建script标签

  • 推崇依赖前置原则

    • 在模块代码逻辑开始前就把所有依赖前置执行,然后再执行自己的代码逻辑

CMD规范

代表:sea.js

 define( function(require, exports, module){
     ...
 })
 复制代码

三个参数分别是:

  • require:可以引用其他模块,也可以用 require.async 异步调用其他模块

  • expxort:是一个对象,定义模块的时候,需要通过参数 export 添加属性来导出 API

  • module:是一个对象,它有三个上属性

    • uri: 模块完整的 URI 路径
    • dependencies:模块的依赖
    • exports:模块需要被导出的 API

看个栗子

 define( function(require, export, module){
     const add = require("math").add
     exports.increment = function(val){
         return add(val, 1)
     }
     module.id = "increment"
 })

CMD特点

  • 同时支持同步异步加载模块

  • 懒加载

  • 推崇依赖就近原则

    • 在需要的时候才加载,如果 require 引入了依赖,但整个逻辑并未使用这个依赖或未执行到逻辑使用它的地方,依赖不会执行

UMD规范

UMD特点

  • 可以通过运行时或者编译时让同一个代码模块在使用 CommonJs、CMD 甚至是 AMD 的项目中运行
  • 在定义模块的时候会检测当前使用环境和模块的定义方式,如果匹配就使用其规范语法,全部不匹配则挂载再全局对象上

ES Module

ES6发布的,在语言标准的层面上实现了模块功能

ES Module特点

  • 在编译时就能确定模块的依赖关系,所有的加载都是引用

    • 好处是可以执行静态分析和类型检查
    • Tree-Shaking 就是在静态分析阶段移除掉JS上下文中没有引用的代码
  • 在浏览器使用ES模块化

    • 在 script 标签中添加 type="module" 属性
  • Node.js从9.0版本开始支持ES模块

ES Module 和 CommonJS对比

ES ModuleCommon JS
import 静态引入require 动态引入
是对模块的引入,输出的是值的应用,改变原来模块中的值引用的值也会改变对模块的拷贝,修改原来模块的值不会影响引用值
this指向undefinedthis指向模块本身
在编译时确定依赖关系运行时加载模块
可以单独加载某个方法加载整个模块
不能被重新赋值可以重新赋值(改变this指向)

参考

juejin.cn/post/699659…

juejin.cn/post/700794…