# AMD 和 CMD 规范的区别?
- AMD 是异步模块,提前执行依赖 - 尽早执行,requireJS 是它的实现
- CMD 按需执行依赖 - 懒执行,seaJS 是它的实现
- AMD 推崇依赖前置、提前执行,CMD 推崇依赖就近、延迟执行
- Nodejs 借鉴了 Commonjs 实现了模块化 ,从 ES6 开始, JavaScript 才真正意义上有自己的模块化规范
- CommonJS 模块是运行时加载的同步加载(同步的就意味着一个文件没有加载结束之前,后面的代码都不会执行)并执行模块文件,ES6 模块提前加载并执行模块文件
# CommonJS
- CommonJS 规范的本质就是值的拷贝输出,一旦输出一个值,模块内部的变化就影响不到这个值,ES6 模块化是引用输出
- Commonjs 是运行时加载,ES6 模块化是编译时输出接口
- Commonjs 是单个值导出,ES6 模块化可以多个值导出
- Commonjs 是动态语法可写在函数体中,ES6 模块化静态语法只能写在顶层
- Commonjs 的 this 是当前模块化,ES6 模块化的 this 是 undefined
- Commonjs 是深度遍历
- CommonJS 模块由 JS 运行时实现
- CommonJs 是单个值导出,本质上导出的就是 exports 属性
- CommonJS 是可以动态加载的,对每一个加载都存在缓存,可以有效的解决循环引用问题
- commonjs 的特性:
- 在 Node 中每一个 js 文件都是一个单独的模块
- 该模块中,包含 CommonJS 规范的核心变量: exports、module.exports、require
- exports 和 module.exports 可以负责对模块中的内容进行导出
- require 函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容
- commonjs 的优势:
- require 避免重复加载,首先加载之后的文件的 module 会被缓存到 Module 上,比如一个模块已经 require 引入了 a 模块,如果另外一个模块再次引用 a ,那么会直接读取缓存值 module ,所以无需再次执行模块
- require 动态加载
# ES6 Module
- ES6 Module 静态的,不能放在块级作用域内,代码发生在编译时
- ES6 Module 的值是动态绑定的,可以通过导出方法修改,可以直接访问修改结果
- ES6 Module 可以导出多个属性和方法,可以单个导入导出,混合导入导出
- ES6 模块提前加载并执行模块文件
- ES6 Module 的特性可以很容易实现 Tree Shaking 和 Code Splitting
- Es Module 的产生有很多优势:
- 借助 Es Module 的静态导入导出的优势,实现了 tree shaking
- Es Module 还可以 import() 懒加载方式实现代码分割
- ES6 module 特性
- ES6 module 的引入和导出是静态的,import 会自动提升到代码的顶层 ,import , export 不能放在块级作用域或条件语句中。
- ES6 module 和 Common.js 一样,对于相同的 js 文件,会保存静态属性
- ES6 模块在预处理阶段分析模块依赖,在执行阶段执行模块,两个阶段都采用深度优先遍历,执行顺序是子 -> 父
- 不能修改 import 导入的属性
- import() 返回一个 Promise 对象, 返回的 Promise 的 then 成功回调中,可以获取模块的加载成功信息
- import() 动态加载、懒加载
- 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 天生的多线程优势