# webpack和babel的原理
webpack原理
- 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化
Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译; - 确定入口:根据配置中的
entry找出所有的入口文件; - 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
babel原理
babel的转译过程分为三个阶段:解析(Parsing)、转换(Transforming)和生成(Generating),以ES6转译为ES5为例,babel转译的具体过程如下:
- ES6代码输入;
- Parsing(解析):Babel解析器(babel-parser)将代码转换成抽象语法树(AST);
- Transforming(转换):Babel中的变换插件对AST进行各种操作,比如添加、删除节点或者替换节点。例如常用的babel-traverse对AST树进行遍历;
- Generating(生成):Babel生成器(babel-generator)将转换后的AST转换回代码。
# 优化Webpack的构建性能
使用最新版本的Webpack:Webpack的每个新版本都会带来性能改进和新特性,因此建议使用最新版本。
开启缓存:Webpack5内置了持久化缓存功能,能将打包结果缓存到磁盘,下次打包时,若文件无变化,可直接使用缓存结果。
优化Loader配置
- 减少Loader的使用范围:使用
include或exclude选项,限制Loader的处理范围。 - 缓存Loader的结果:使用
cache-loader或babel-loader的cacheDirectory选项,缓存Loader的结果。
- 减少Loader的使用范围:使用
使用
DllPlugin预编译依赖:使用DllPlugin将不常变动的第三方库(如React、Lodash)预编译成单独的bundle,减少构建时间。使用
HappyPack(webpack5已弃用)或Thread-loader并行处理:使用HappyPack或thread-loader将Loader的处理过程并行化,提高构建速度。使用
SplitChunksPlugin优化代码分割:将公共代码提取到单独的Chunk中,减少重复代码。optimization: { splitChunks: { chunks: 'all', }, }1
2
3
4
5使用Tree Shaking移除未使用代码
optimization: { usedExports: true, }1
2
3使用
HardSourceWebpackPlugin缓存构建结果,减少重复构建时间。使用
IgnorePlugin忽略不必要的模块,减少构建体积。使用
externals排除外部依赖(如通过CDN引入的库),减少构建体积。使用
Profile分析构建性能,使用--profile参数生成构建性能分析报告,找出性能瓶颈。使用
webpack-bundle-analyzer查看可视化分析打包结果,查看模块体积。使用
speed-measure-webpack-plugin测量每个插件和loader的耗时。
# webpack中loader和plugin的区别
什么是loader?
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中。
什么是plugin?
在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
区别
- 对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将
A.scss转换为A.css,单纯的文件转换过程。 - plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。
- 对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将
常用的plugin和loader loader:
- file-loader:用于处理文件类型资源,如jpg,png等图片。
- url-loader:它与file-loader作用相似,也是处理图片的,只不过url-loader可以设置一个根据图片大小进行不同的操作,如果该图片大小大于指定的大小,则将图片进行打包资源,否则将图片转换为base64字符串合并到js文件里。
- style-loader:通过注入
style标签将CSS插入到DOM中。 - css-loader:仅处理css的各种加载语法(
@import和url()函数等)。 - postcss-loader。
- less-loader:解析less,转换为css。
- ts-loader:打包编译Typescript。
- babel-loader:基于babel,用于解析JavaScript文件。
- markdown-loader:markdown编译器和解析器。
- raw-loader:可将文件作为字符串导入。
plugin:
- HtmlWebpackPlugin:生成html文件,为html引入外部资源如script、link。
- copy-webpack-plugin:将已经存在的单个文件或整个目录复制到构建目录。
- clean-webpack-plugin:删除webpack的output.path中的所有文件。
- mini-css-extract-plugin:将CSS提取到单独的文件中,为每个包含CSS的JS文件创建一个CSS文件。
- SplitChunksPlugin:代码分割。
- webpack.HotModuleReplacementPlugin:热更新插件。
- webpack.DefinePlugin:创建一个在编译时可以配置的全局常量。
- webpack-bundle-analyzer:看到项目各模块的大小,可以按需优化。
# webpack热更新原理
简单来说就是:hot-module-replacement-plugin包给webpack-dev-server提供了热更新的能力,它们两者是结合使用的,单独写两个包也是出于功能的解耦来考虑的。
webpack-dev-server(WDS)的功能提供bundle server的能力,就是生成的bundle.js文件可以通过localhost://xxx的方式去访问,另外WDS也提供livereload(浏览器的自动刷新)。hot-module-replacement-plugin的作用是提供HMR的runtime,并且将runtime注入到bundle.js代码里面去。一旦磁盘里面的文件修改,那么HMR server会将有修改的js module信息发送给HMR runtime,然后HMR runtime去局部更新页面的代码。因此这种方式可以不用刷新浏览器。
热更新步骤:
- 当修改了一个或多个文件;
- 文件系统接收更改并通知webpack;
- webpack重新编译构建一个或多个模块,并通知HMR服务器进行更新;
- HMR Server使用webSocket通知HMR runtime需要更新,HMR运行时通过HTTP请求更新json;
- HMR运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新。
# Vite与Webpack区别
Vite和Webpack是两种流行的前端构建工具,它们在设计理念、构建机制和性能优化上有显著的区别。Vite之所以比Webpack快,主要是因为它在开发模式下利用了现代浏览器的原生ES模块(ESM)支持,以及更高效的构建策略。以下是它们的详细对比:
构建机制
Webpack
- 基于打包的构建工具。
- 在开发模式下,Webpack会将所有模块打包成一个或多个bundle文件(如
main.js)。 - 每次代码更改时,Webpack需要重新构建整个bundle,导致开发服务器的启动和热更新较慢。
Vite
- 基于原生ES模块(ESM)的构建工具。
- 在开发模式下,Vite不会打包所有模块,而是利用浏览器的原生ESM支持,按需加载模块。
- 每次代码更改时,Vite只需要重新构建改动的模块,因此启动和热更新速度更快。
开发服务器
Webpack
- 开发服务器依赖于打包后的bundle文件。
- 启动时需要先打包所有模块,导致启动时间较长。
- 热更新(HMR)需要重新构建整个bundle或部分chunk。
Vite
- 开发服务器直接基于原生ESM,按需加载模块。
- 启动时只需要启动开发服务器,不需要预先打包,因此启动时间极短。
- 热更新(HMR)只更新改动的模块,速度更快。
生产构建
Webpack
- 在生产模式下,Webpack会将所有模块打包成优化的bundle文件。
- 支持多种优化策略(如代码分割、Tree Shaking、压缩等)。
Vite
- 在生产模式下,Vite使用Rollup进行打包。
- Rollup的打包机制更轻量,生成的代码更简洁,性能更好。
Vite为什么比Webpack快?
开发模式下的按需加载
- Vite利用浏览器的原生ESM支持,按需加载模块。
- 只有在浏览器请求某个模块时,Vite才会动态编译并返回该模块。
- 这种方式避免了Webpack在开发模式下需要预先打包所有模块的开销。
更快的启动速度
- Vite的启动时间几乎与项目规模无关,因为它不需要预先打包所有模块。
- Webpack的启动时间随着项目规模的增长而增加,因为它需要预先打包所有模块。
更快的热更新
- Vite的热更新只更新改动的模块,而不是整个bundle。
- Webpack的热更新需要重新构建整个bundle或部分chunk,速度较慢。
基于Rollup的生产构建
- Vite在生产模式下使用Rollup进行打包,生成的代码更简洁,性能更好。
- Webpack虽然功能强大,但在某些场景下生成的代码可能比Rollup更臃肿。
功能对比
| 特性 | Vite | Webpack |
|---|---|---|
| 开发模式构建机制 | 基于原生ESM,按需加载模块 | 基于打包,生成bundle文件 |
| 启动速度 | 极快,与项目规模无关 | 较慢,随项目规模增长而增加 |
| 热更新(HMR) | 只更新改动的模块,速度更快 | 需要重新构建bundle或chunk |
| 生产构建工具 | 使用Rollup | 使用Webpack自身 |
| 插件生态 | 插件生态较新,但增长迅速 | 插件生态非常成熟 |
| 配置复杂度 | 配置简单,开箱即用 | 配置复杂,功能强大但学习成本高 |
| Tree Shaking | 支持,基于Rollup | 支持,功能强大 |
| 代码分割 | 支持,基于Rollup | 支持,功能强大 |
总结
- Vite的优势在于开发模式下的极速启动和热更新,适合现代前端项目和快速迭代的开发环境。
- Webpack的优势在于功能强大、插件生态成熟,适合复杂项目和高度定制化的构建需求。
如果你的项目需要快速启动和热更新,并且对现代前端技术栈有较好的支持,Vite是一个更好的选择。如果你的项目需要高度定制化的构建流程,或者依赖Webpack的插件生态,Webpack仍然是更合适的选择。
# Webpack5有哪些更新
长期缓存与持久化缓存
- 内置持久化缓存:Webpack5引入了内置的持久化缓存机制,可将打包过程中的中间结果存储在磁盘上。下次打包时,若文件没有发生变化,Webpack可以直接使用缓存结果,显著提高打包速度。
- 改进的长期缓存:Webpack5改进了对文件哈希的生成算法,能够更好地处理文件内容变化,确保只有真正发生改变的文件其哈希值才会变化,从而提高长期缓存的命中率。
模块联邦(Module Federation)
模块联邦是Webpack5最重要的特性之一,它允许在不同的Webpack构建之间动态共享模块。
资源模块(Asset Modules)
简化资源处理:Webpack5引入了资源模块类型,用于替代之前的file-loader、url-loader和raw-loader。资源模块可以更方便地处理不同类型的资源文件,如图片、字体等。
性能优化
- 更好的Tree Shaking:Webpack5改进了Tree Shaking算法,能够更准确地识别和移除未使用的代码,减少打包后的文件体积。
- 更快的打包速度:通过优化内部算法和数据结构,Webpack5在打包大型项目时速度有了显著提升。