Webpack与Vite的区别与联系
0. 当前工程化痛点
主流webpack需要把整个项目编译好后再交给dev server,因此大型项目devserver启动时间很长,HMR也需要很长时间。
因此,Vite应运而生
Webpack
当我们使用webpack启动项目时,webpack会根据我们配置文件中的入口文件,
- 分析出项目项目所有依赖关系,
- 然后打包成一个文件(bundle.js),
- 交给浏览器去加载渲染。
在Webpack中,构建过程大概分为这么几个阶段初始化Init、构建Make、生成Seal
- 初始化阶段: 修整配置参数,创建 Compiler、Compilation 等基础对象,并初始化插件及若干内置工厂、工具类,并最终根据 entry 配置,找到所有入口模块
- 构建阶段: 从entry找到入口,调用loader编译模块,遍历 AST 找出模块依赖的模块,之后递归遍历所有依赖块,构建出 模块依赖关系图 (dependency graph)
- 输出阶段: 根据output的配置,将模块拆解成不同的chunk对象,经过一系列优化,再将代码翻译成产物
描述得比较简单, 实际完成这些过程是比较复杂的
HMR
每次修改一个模块,webpack要遍历整个依赖图来找出所有依赖于该模块的其他模块。再进行局部的重新编译。
三个阶段:
- 找出过期的模块
- 从缓存中删除过期的模块
- 把新模块添加到modules中
webpack4以前是通过jsonp来拉取更新模块的,webpack4以及以后通过ws通信+ajax请求
devServer
原理是通过在内存中创建虚拟文件系统来提供开发服务器功能。它监听文件变化并通过 WebSocket 与浏览器通信,以实现HMR,提供高效的开发环境。
- 启动服务器;在本地启动http服务器
- 读入内存;把项目文件读入内存
- 编译构建;所有编译都在内存中完成,无需写入磁盘
- 请求转发;对于静态资源直接从内存中提供给客户端
- HMR;通过http升级websocket。
优化打包速度
- 禁止大图片转base64
- 利用webpack-bound-analyze分析
- CDN引入公共库,减少webpack打包量
- 开启缓存,以便在后续构建中重复使用之前的结果
cache: true
- 开启多线程,利用
thread-loader
Vite
底层
基于esbuild和rollup,利用浏览器对ESM的支持
- esbuild: 预构建。把不同模块规范统一编译为ESM
- rollup:在build时打包成生产环境代码
ESM
ESM === ES module, 大部分浏览器都支持<script type='module'>
在<script type="module">
中,浏览器遇到内部的import引用时,会自动发起http请求,去加载对应的模块。
ESM 支持编译时就能确定模块的依赖关系
开发环境
- 预构建。
- 针对node_modules中的第三方依赖,由esbuild把所有模块编译为ESM。
- 整合模块,对于具有许多内部模块的,整合成一个模块。避免发送太多http请求。
- 缓存。把预构建好的ESM存到
node_modules/.vite
下。
- 冷启动devServer。
- 按需编译。页面需要某些模块会发送http请求,此时vite再进行编译,编译后发给浏览器。
此时编译是对src源码的编译,包括对ts、less、esm、图片、字体的处理- 利用http2.0 支持并发请求。
- 利用http缓存,
- node_modules 使用强制缓存
- src下源码 使用协商缓存
注意:
按需编译,编译了:
typescript、vue、jsx、css预处理语言、静态资源等。实际上都转换为了浏览器可以解析的html/js/css,但是由于SouceMap方便开发者,我们在F12网络看到的资源依然是index.ts这样的后缀。
HMR
当修改一个文件时vite精确定位到该模块,使其失活。vite重新编译该模块后再发送给浏览器。浏览器利用动态导入import()
来替换。
原理:利用websocket连接浏览器,建立连接后监听文件变化。服务器向浏览器注入代码用于处理ws消息(重新请求模块、刷新页面)
重新请求模块依然走ajax请求,而不是ws;目的是为了保证ws只用于通信上。
生产环境
基于rollup打包,rollup轻量、打包更快。
为什么vite比webpack快
从几方面回答:
- vite基于esbuild、rollup
- esbuild基于Go,善于利用多核cpu
- rollup轻量,打包更快
- vite的预构建、冷启动、按需编译
- HMR的区别
- vite内置功能多,使用成本低