# AMD 和 CMD 规范的区别?

  • AMD 是异步模块,提前执行依赖 - 尽早执行,requireJS 是它的实现
  • CMD 按需执行依赖 - 懒执行,seaJS 是它的实现
  • AMD 推崇依赖前置、提前执行,CMD 推崇依赖就近、延迟执行
  • Nodejs 借鉴了 Commonjs 实现了模块化 ,从 ES6 开始, JavaScript 才真正意义上有自己的模块化规范
  • CommonJS 模块是运行时加载的同步加载(同步的就意味着一个文件没有加载结束之前,后面的代码都不会执行)并执行模块文件,ES6 模块提前加载并执行模块文件

# CommonJS

  1. CommonJS 规范的本质就是值的拷贝输出,一旦输出一个值,模块内部的变化就影响不到这个值,ES6 模块化是引用输出
  2. Commonjs 是运行时加载,ES6 模块化是编译时输出接口
  3. Commonjs 是单个值导出,ES6 模块化可以多个值导出
  4. Commonjs 是动态语法可写在函数体中,ES6 模块化静态语法只能写在顶层
  5. Commonjs 的 this 是当前模块化,ES6 模块化的 this 是 undefined
  6. Commonjs 是深度遍历
  7. CommonJS 模块由 JS 运行时实现
  8. CommonJs 是单个值导出,本质上导出的就是 exports 属性
  9. CommonJS 是可以动态加载的,对每一个加载都存在缓存,可以有效的解决循环引用问题
  • commonjs 的特性:
    1. 在 Node 中每一个 js 文件都是一个单独的模块
    2. 该模块中,包含 CommonJS 规范的核心变量: exports、module.exports、require
    3. exports 和 module.exports 可以负责对模块中的内容进行导出
    4. require 函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容
  • commonjs 的优势:
    1. require 避免重复加载,首先加载之后的文件的 module 会被缓存到 Module 上,比如一个模块已经 require 引入了 a 模块,如果另外一个模块再次引用 a ,那么会直接读取缓存值 module ,所以无需再次执行模块
    2. require 动态加载

# ES6 Module

  1. ES6 Module 静态的,不能放在块级作用域内,代码发生在编译时
  2. ES6 Module 的值是动态绑定的,可以通过导出方法修改,可以直接访问修改结果
  3. ES6 Module 可以导出多个属性和方法,可以单个导入导出,混合导入导出
  4. ES6 模块提前加载并执行模块文件
  5. ES6 Module 的特性可以很容易实现 Tree Shaking 和 Code Splitting
  • Es Module 的产生有很多优势:
    1. 借助 Es Module 的静态导入导出的优势,实现了 tree shaking
    2. Es Module 还可以 import() 懒加载方式实现代码分割
  • ES6 module 特性
    1. ES6 module 的引入和导出是静态的,import 会自动提升到代码的顶层 ,import , export 不能放在块级作用域或条件语句中。
    2. ES6 module 和 Common.js 一样,对于相同的 js 文件,会保存静态属性
    3. ES6 模块在预处理阶段分析模块依赖,在执行阶段执行模块,两个阶段都采用深度优先遍历,执行顺序是子 -> 父
    4. 不能修改 import 导入的属性
    5. import() 返回一个 Promise 对象, 返回的 Promise 的 then 成功回调中,可以获取模块的加载成功信息
    6. import() 动态加载、懒加载
    7. Tree Shaking 在 Webpack 中的实现,是用来尽可能的删除没有被使用过的代码,一些被 import 了但其实没有被使用的代码

# Vite 的特点

  • 快速的冷启动: No Bundle + esbuild 预构建
  • 即时的模块热更新: 基于 ESM 的 HMR,同时利用浏览器缓存策略提升速度
  • 真正的按需加载: 利用浏览器 ESM 支持,实现真正的按需加载
  • 在生产环境下,Vite 使用 Rollup 来进行打包

# vite 对比 webpack

  • Webpack 在启动时,会先构建项目模块的依赖图,如果在项目中的某个地方改动了代码,Webpack 则会对相关的依赖重新打包,随着项目的增大,其打包速度也会下降

  • Vite 相比于 Webpack 而言,没有打包的过程,而是直接启动了一个开发服务器 devServer。

  • Webpack 是先解析依赖、打包构建再启动开发服务器,Dev Server 必须等待所有模块构建完成,当我们修改了 bundle 模块中的一个子模块, 整个 bundle 文件都会重新打包然后输出。项目应用越大,启动时间越长

  • Vite 利用浏览器对 ESM 的支持,当 import 模块时,浏览器就会下载被导入的模块。先启动开发服务器,当代码执行到模块加载时再请求对应模块的文件,本质上实现了动态加载。

# rollup 打包原理

Rollup 分为 build(构建)阶段和 output generate(输出生成)阶段。主要过程如下:

  • 获取入口文件的内容,包装成 module,生成抽象语法树
  • 对入口文件抽象语法树进行依赖解析
  • 生成最终代码
  • 写入目标文件

# vite 为什么需要预构建?

  • Vite 是基于浏览器原生支持 ESM 的能力实现的,但要求用户的代码模块必须是 ESM 模块,因此必须将 commonJs 的文件提前处理,转化成 ESM 模块并缓存入 node_modules/.vite
  • 减少模块和请求数量,Vite 将有许多内部模块的 ESM 依赖关系转换为单个模块,以提高后续页面加载性能

# Esbuild 为什么快?

  • 大多数前端打包工具都是基于 JavaScript 实现的,大家都知道 JavaScript 是解释型语言,边运行边解释。而 Esbuild 则选择使用 Go 语言编写,该语言可以编译为原生代码,在编译的时候都将语言转为机器语言,在启动的时候直接执行即可,在 CPU 密集场景下,Go 更具性能优势
  • JavaScript 本质上是一门单线程语言,直到引入 WebWorker 之后才有可能在浏览器、Node 中实现多线程操作。就我对 Webpack 的源码理解,其源码也并未使用 WebWorker 提供的多线程能力。而 GO 天生的多线程优势
Last Updated: 4/25/2025, 8:08:15 AM