构建工具复习篇
前言
我们的写的代码和浏览器上运行的代码并不是相同的代码,我们的代码还需要进行一些列的处理才可以在浏览器上,这个处理的过程叫做构建,它是一些列工具共同运作的结果。
构建工具和打包工具是什么?
构建工具(Build Tool) 是一种用于自动化构建、编译和部署项目的工具。它们能够将开发者编写的源代码以及其他资源文件(如样式表、图像等)转化为可在生产环境中运行的优化、压缩的代码。
构建工具的主要功能包括:
-
编译:将高级语言(如
ES6、TypeScript、SCSS等)转换为浏览器或Node.js能够理解和执行的低级语言(如ES5、CSS、HTML等)。 -
压缩:去除空格、注释、无用的代码,并对代码进行优化,以减小文件大小和提升性能。
-
模块化支持:将模块化的代码拆分成不同的文件,并将它们合并、压缩为单个文件,以减少网络请求,提高加载速度。
-
依赖管理:根据代码中的依赖关系,自动处理和管理所需的第三方库或模块,并将它们打包到最终的输出文件中。
-
自动化任务:执行其他开发任务,如代码检查、测试运行、文档生成等。
当项目变得复杂时,手动完成这些工作将变得非常耗时且容易出错。构建工具的出现极大地简化了这些任务的处理,并提供了一种便捷的方式来自动化构建和管理项目。
打包工具(Bundle Tool) 是前端开发中常用的工具之一,它用于将项目中的多个模块或文件打包成一个或多个较大的文件,以减少网络请求和提高加载性能。
构建 针对任务处理,打包 针对文件、模块、资源的整合与输出
由于构建和打包的概念有时会重叠,同时一些现代前端工具(如Webpack、Vite)已经集成了构建和打包功能,模糊了构建和打包的界限,因此在实际应用中,这两个术语常常被混用。
Webpack
webpack核心概念
- mode: 采取的打包模式
- devtool:是否产生
source-map - entry:打包入口
- module:模块,源码
- loader:文件转换器
- chunk:构建过程中多模块集合的代码块(中间产物)
- plugin:插件,执行特定任务
- output:打包出口
- bundle: 最终产物
- optimization:压缩、剥离等优化配置
安装:npm install webpack webpack-cli -D
理解编译产物bundle
bundle的本质是一个iife,webpack通过iife实现函数私有化的作用域
如图:
bundle是一个iife,这个iife执行后会在全局挂载变量__webpack_modules__bundle内通过__webpack_exports__进行模块导出bundle内通过__webpack_module_cache__对已经加载过的模块进行缓存bundle内通过__webpack_require__(<moduleID>)对模块进行加载bundle内通过__webpack_modules__[<moduleID>]()的方式执行模块bundle内通过___CSS_LOADER_EXPORT___进行css的模块输出
loader
loader用来进行文件类型转换,它的执行顺序是从右到左(或从下到上)执行
处理css资源
css-loader(模块化)
css-loader用来处理.css文件,它会把.css文件注入到bundle中,并作为一个模块并通过___CSS_LOADER_EXPORT___输出
为什么使用了css-loader进行转换之后,引入css不能生效?还需要使用style-loader?
如上所述,css-loader仅仅是注入了bundle并且使其能够在js内导入导出,可它本身并没有被挂到html的header的style内
style-loader(将CSS模块转为DOM)
style-loader会将bundle中的css模块注入到html的header的style内,具体的注入方式是它在获取到css的相关模块后会进行DOM操作,在header内插入style标签,从而使css生效
style-loader是基于css-loader将css转换为module进行的
mini-css-extract-plugin.loader
我们可以用mini-css-extract-plugin的loader代替style-loader从而将css从bundle中剥离出来,单独生成css文件
postcss-loader
对css进行兼容性处理,先在项目根目录配置postcss.config.js,当webpack调用postcss-loader进行处理时,会调用postcss.config.js的配置
处理图片等静态资源
静态资源打包逻辑:
webpack4及之前通过loader处理对应的资源,更换资源名称,同时用新的资源名称替换掉资源引用路径webpack5.12.0+通过内置的moduleAssets特性完成对静态资源的打包
url-loader(webpack4)
css中通过background-image:url(../img/sprite_nav_3.png)类似的方式引入图片,我们需要针对这个url进行打包,方便找到该url对应的资源
file-loader(webpack4)
file-loader会将文件上的import/require()解析为url,并将该文件注入到输出目录中,我们的icon就可以借助它单独到包到一个文件夹内,url-loader中会自动调用它,仅安装即可
moduleAssets特性(webpack5.12+)
- 通过
type:'assets'启用moduleAssets特性 - 通过
parser配置转换配置- 通过
dataUrlCondition配置图片是转为base64注入到bundle中还是分离到dist中
- 通过
- 通过
generator配置生成的文件位置及文件名称
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024,
},
},
generator: {
filename: 'images/[name].[hash:6][ext]',
},
image-webpack-loader
image-webpack-loader用来缩小.png, .jpeg, .gif, .svg和.webp图像,它有一个同级依赖imagemin-gifsicle需要一块安装
处理ES
babel-loader
使用Babel和webpack编译JavaScript文件为指定版本的JS。
别的格式处理
ejs-loader
ejs-loader用于webpack的.ejs解析。使用lodash模板函数编译模板。
多进程打包
thread-loader
webpack默认采用单进程的方式进行构建,thread-loader可以开启多进程进行构建
使用时,需将此thread-loader放置在其他loader之前。放置在thread-loader之后的loader会在一个独立的worker池中运行。
在worker池中运行的loader是受到限制的。例如:
- 这些
loader不能生成新的文件。 - 这些
loader不能使用自定义的loader API(也就是说,不能通过插件来自定义)。 - 这些
loader无法获取webpack的配置。
每个worker都是一个独立的node.js进程,其开销大约为 600ms 左右。同时会限制跨进程的数据交换。
请仅在耗时的操作中使用此thread-loader!
自定义loader
假设现在有一种文件格式为.jx那么如何把该文件通过webpack注入到bundle中进行执行?
所以,我们需要定义一个loader让webpack用来解析.jx文件,让它转为js并且注入bundle中
loader本身就是一个module,它输出一个函数,遇到.jx文件时执行该函数完成对.jx文件的解析-
该函数行参为
source,source为.jx文件的源码 -
函数内通过正则截取出所有的
js代码并进行返回,就比如vue中的script标签内全是js代码 -
如果是别的语言转
js,抱歉,咱还不会
-
plugin
plugin用来解决webpack构建生命周期功能定制的问题,plugin的本质是在特定的时期执行对应的任务,所以会借助很多的hook,要能很好的开发plugin就得对构建的生命周期了若指掌,能够见缝插针地安排任务执行
html-webpack-plugin
html-webpack-plugin会为我们生成一个html5文件,并且使用script标签自动引入所有webpack生成的bundle,
- 当
hash改变的时候,它显得特别有用! - 支持传入模板
- 打包成
MPA项目时,配置多个html-webpack-plugin,每个生成一个对应的html,通过chunks指定该html对应的入口文件
mini-css-extract-plugin
mini-cssextract-plugin这个插件将CSS提取到单独的文件中。它为每个包含CSS的JS文件单独创建CSS文件。它支持按需加载CSS和SourceMaps
ProvidePlugin
ProvidePlugin是webpack的内置plugin,自动加载模块,而不必在任何地方导入或需要它们,提供全局标识符映射到指定模块,如Jquery映射为$
CopyWebpackPlugin
CopyWebpackPlugin是webpack的内置plugin, 它能将已经存在的单个文件或整个目录复制到构建目录
如:图片等静态资源,我们可以直接通过该plugin由src目录下复制到dist目录下
happyPack(webpack 4)
HappyPack可以通过多进程并行转换文件使初始webpack构建更快。
webpack-parallel-uglify-plugin (webpack4)
webpack-parallel-uglify-plugin可以开启多进程压缩。
uglifyjs-webpack-plugin(webpack4)
uglifyjs-webpack-plugin用于webpack4版本压缩JS代码
terser-webpack-plugin (webpack5)
terser-webpack-plugin用于webpack5版本压缩JS代码
optimize-css-assets-webpack-plugin(webpack4)
压缩css文件
css-minimizer-webpack-plugin(webpack5)
css-minimizer-webpack-plugin使用cssnano来优化和最小化CSS。
cssnano采用格式良好的CSS,并通过许多重点优化来运行它,以确保最终结果对于生产环境来说尽可能小。
CleanWebpackPlugin(webpack4)
打包时清空dist目录,webpack 5.2+可以通过配置ouput.clean清空
speed-measure-webpack-plugin
speed-measure-webpack-plugin用于测量webpack构建速度并输出- 打包的耗时有一半都是来源于,源码分析,构建依赖图,该插件能按顺序告知我们,代码经过了哪些
loader处理, 每一个loader耗时多久
webpack-bundle-analyzer
webpack-bundle-analyzer用于bundle的构建体积分析
purgecss-webpack-plugin
purgecss-webpack-plugin用来移除没有用到的css代码,类似于实现了css的tree-shaking
DllPlugin
webpack的内置plugin,用于分包,生成manifest.json已经独立打出来的dll包
DllReferencePlugin
webpack的内置plugin,用于为主包配置分包,通过分包的manifest.json跳过相关内容的分析扫描转译等过程,进而加快打包速度
webpack.IgnorePlugin
webpack.IgnorePlugin可以用来忽略一些被第三方模块附带引入,但我们明显不想打包的模块,比如moment.js的语言包
add-asset-html-webpack-plugin
add-asset-html-webpack-plugin能将JavaScript或CSS资源copy到dist目录,并添加到html-webpack-plugin生成的html中。我们可以通过它引入dll打包生成的资源
hard-source-webpack-plugin(webpack4)
hard-source-webpack-plugin用于为模块提供中间缓存步骤,它可以缓存到本地。第一次构建将花费正常的时间。第二次构建将明显快得多。webpack 5可以配置cache替代该插件
webpack.HotModuleReplacementPlugin
webpack.HotModuleReplacementPlugin可以帮我们做到页面不刷新的情况下更新代码,保留原有状态
ModuleConcatenationPlugin(webpack 4)
使用ModuleConcatenationPlugin可以开启Scope Hosting,使bundle体积更小,创建函数作用域更少,代码可读性更好
webpack 5通过配置optimization.concatenateMoudles为true即可开启
自定义plugin
webpack hook
compiler钩子类,针对chunk生成以前compilation钩子类,针对chunk生成以后
devServer
devServer可以帮我们实现自动刷新,将打包的代码放入内存,更改代码后,快速更新bundle
安装:webpack-dev-server
webpack性能优化
构建性能优化方法
- 查找并诊断性能瓶颈
- 构建速度分析:影响构建性能和开发效率
- 构建体积分析:影响页面访问性能
- 构建性能优化常用方法
- 通过多进程加快构建速度
- 通过分包减小构建目标容量
- 减少构建目录加快构建速度
速度优化
- 先使用
speed-measure-webpack-plugin进行构建速度分析 - 优化
babel-loader配置,精准扫描、分析、编译我们的源代码 - 使用
ignorePlugin忽略我们不想打入bundle的模块 - 配置
module.noParse避免重复打包 - 多进程打包
happyPack(webpack 4)、thread-loader(webpack 5) - 使用
webpack-parallel-uglify-plugin开启多进程压缩 仅开发环境下配置: - 利用
cache提升二次构建速度 - 使用
DllPlugin拆分不常变依赖进行预打包,生成动态链接库 - 自动刷新、热更新
是否需要开启多进程?项目小,打包快,开启多进程会降低打包速度(进程开销)
优化babel-loader配置
- 开启缓存后,只要
ES6代码没有改动,就不会重新编译,二次编译时直接调用缓存 - 精准化:只对
src下的代码进行babel处理,因为编译需要转为AST,修改AST,再根据AST转为源码,非常耗时
使用ignorePlugin避免引入无用模块
忽略被第三方模块自动引入但我们明显用不到的模块,减少打包时间还有包体积
配置module.noParse忽略直接引入的模块
有的模块我们不是通过包管理器直接安装的,而是直接从网络上下载到本地而后放入项目中的,比如XXX.min.xxx,它已经是打包好了的,已经是一个bundle了,我们没必要再让它去经历分析,构建依赖关系,编译的打包过程,而是调用add-asset-html-webpack-plugin直接拷贝到dist中,从而节省时间
考虑是否应该开启多进程打包
如果我们的构建非常耗时,考虑使用thread-loader或者happyPack开启多进程打包
考虑是否应该开启多进程压缩
如果我们的构建非常耗时,考虑使用webpack-parallel-uglify-plugin开启多进程压缩
利用缓存提升二次构建速度
配置好cache可以在webpack开始工作的时候,让webpack优先去查找缓存的内容是否存在,而后开始打包,从而减少打包的内容
webpack 4的cache只能存储与内存中webpack 5还可以直接存储与硬盘上
热更新 VS 自动刷新
- 自动刷新: 整个网页全部刷新,速度较慢,状态会丢失
- 热更新:新代码生效,网页不刷新,状态不丢失
体积优化
- 先使用
webpack-bundle-analyzer进行构建体积分析 bundle加contentHash- 懒加载
- 公共代码拆分
webpack.IgnorePlugin忽略不需要的第三方依赖自身引入的模块- 使用
CDN加速 - 使用
production模式 scope hosting(作用域提升)
使用production的好处
- 自动压缩代码
Vue、React等会自动删除调试代码- 启动
Tree-shaking
从bundle剥离css资源
bundle中,css-loader与style-loader在进行了css模块化以及插入DOM的操作后与原来的css相比,体会会变得非常大,因此需要将css从bundle中剥离出来,按需加载
js和css的压缩 + treeshaking
- 不改变源码,去除无意义的空格,换行,注释,从而改变体积大小,让包变得更小
- 线上用不到的代码,不打包仅
bundle
treeshaking是有触发条件的:
esm的规范- 通过
export导出而不是export default导出
为什么cjs无法实现tree-shaking?
ESM是静态引入,编译时引入CJS是动态引入,执行时引入- 只有
ESM才能静态分析,实现Tree-shaking
删除无用的css代码
使用purgecss-webpack-plugin去除没有用到的css代码
代码切割
- 通过配置
optimization.splitchunks进行代码切割 - 将我们自己写的代码与
node_modules引入的代码进行切割,打包后,所有的node_modules打包为vendor - 通过
optimization.splitchunks.miniSize配置切割条件,如大小超过200KB进行切割 - 通过
optimization.splitchunks.cacheGoup配置需要单独打包的规则,如将vue,vue-loader等全家桶单独切割为一个包,其他零碎的依赖,单独切割为一个包 cacheGroup的优先级低于miniSize,因此,同时配置时,得先满足miniSize,cacheGroup切割的规则才能生效
图片压缩
小图片采用base64格式产出,大图片使用image-webpack-loader进行图片体积压缩
分包策略
Dll分包
常用来对一些大体积,构建慢,并且稳定不常变动的依赖(如:Vue、React的全家桶)进行分包,既解决构建速度优化的问题,也能解决体积优化的问题
- 新建
webpack.dll.config.js作为单独分包的配置文件 - 配置分包规则,规则同
optimization.splitchunks.cacheGoup类似 - 配置
webpack.dll.config.js的DllPlugin单独对dll进行打包 - 配置
scripts——dll:webpack --config webpack.dll.config.js - 配置
webpack.config.js的DllReferencePlugin,打包生产模式代码时通过dll的manifest.json使用Dll的打包结果 - 配置
add-asset-html-webpack-plugin注入我们的dll打出来的包的本体到dist目录下,并在html中引入它
现在无论是
vue-cli还是CRA都不选择使用dll了
Splitchunks分包
- 分割所有
chunk - 根据需要配置
cacheGroups(缓存分组) - 设置切割的引用次数界限
- 一般配置为
vendor(第三方依赖)和common(我们写的源代码) vendor又可以进一步分包,分为常变依赖及不常变依赖- 常变依赖指
lodash、echarts等,可能因为业务开发而会发生引入变更,可能会时常发生引入变更的依赖 - 不常变依赖为
vue全家桶、react
splitChunks VS Dll
dll配置明显麻烦得多dll包的大小,或者是dll包里的库文件需要webpack去处理的越多越大,dll方案的性能提升效果越是明显,否则可能是副提升dll不支持按需加载,而splitChunks支持按需加载vue-cli,CRA都选择不使用dll了- 优先推荐
splitChunks
详情可看:DllPlugin实践分析
webpack4 VS webpack5
webpack5拥有更好的tree-shaking特性,打包体积明显小了很大,因为少了很多模块加载的代码,文件压缩策略更优webpack5的cache可以缓存到本地,webpack4默认只能选择true或false,如果要缓存到本地需要借助其它的插件——hard-source-webpack-plugin,并且webpack5的缓存构建速度优于webpack4webpack4处理资源文件需要借助url-loader,file-loader的帮助,而在webpack5中可以直接使用内置的moduleAssets特性进行处理webpack5支持更好的URI解析,比如引入文件的时候可以通过file协议进行引入webpack5打的包能很好支持package.json的exports特性,如果是通过ESM的方式引入的,它会以对应的lib指向的import路径为入口进行扫描,分析并打包,如果是通过CommonJS引入的,则以require路径为入口扫描打包
ModuleFederation(模块联邦)
感觉这玩意有点难描述,个人理解,类似与用vue3去进行defineExport,然后另外一个包去接收,只不过换成了用webpack通过ModuleFederationPlugin的exposes去进行暴露,需要引入的包用remotes去接受这个暴露的包
Vite
dev环境采用esbuild进行打包prod环境采用rollup打包vite已经内置了很多配置,几乎不需要我们关心文件如何解析- 不区分
loader和plugin,它只有plugin
Vite启动为什么这么快?
-
webpack先打包,再启动开发服务器,请求服务器时直接给予打包后的结果; -
vite直接启动开发服务器,请求哪个模块再对哪个模块进行实时编译,从而跳过了打包的过程(开发 0 配置); -
热更新方式:
webpack的热更新是基于dev-servr每次热更新都要重新打包。当我们开始构建越来越大型的应用时,需要处理的JavaScript代码量也呈指数级增长。vite热更新是会起2个服务:客户端服务,websocket服务,监听本地文件变更,区分变更的类型,然后做相应的处理,通过ws通知客户端服务,客户端服务在去加载新的资源或刷新网页,热更新时,不同的文件有不同的处理方式- 当你只修改了
script里的内容时:不会刷新, Vue组件重新加载(会重新走生命周期),- 当你只修改了
template里的内容时: 不会刷新, Vue组件重新渲染(不会重新走生命周期)- 样式更新,样式移除时:不会刷新,直接更新样式,覆盖原来的
js文件更新时:网页重刷新
- 当你只修改了
-
由于现代浏览器本身就支持
ESM(<script type=module />),会主动发起请求去获取所需文件。vite充分利用这点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像webpack先打包,交给浏览器执行的文件是打包后的; -
由于
vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。当浏览器请求需要的模块时,再对模块进行编译,这种按需动态编译的模式,极大缩短了编译时间,当项目越大,文件越多时,vite的开发时优势越明显; -
在
HRM方面,当某个模块内容改变时,让浏览器去重新请求该模块即可,而不是像webpack重新将该模块的所有依赖重新编译; -
当需要打包到生产环境时,
vite使用传统的rollup进行打包,所以,vite的优势是体现在开发阶段,另外,由于vite使用的是ESM,所以代码中不可以使用CJS;
vite其实并不需要我们关心文件如何解析
如果在webpack中,我们没有配置对应的模块解析规则,那么我们将无法正常工作,比如:scss、less,
而Vite可以处理SCSS和Less文件,它使用内置的 sass 和 less 插件来处理这两种样式预处理器。
手动分包
vite默认会将第三方依赖和我们的源码打包进同一个模块,我们可以通过配置build.rollupOptions.output.manualChunks进行手动分包,单独将第三方依赖拆分出来
function manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
}
}
id会返回当前处理的模块路径,我们可以根据路径决定如何分包
vite还是一个脚手架,可以快速制作组件库
以往,创建一个vue项目,我们需要使用vue-cli,创建一个react项目我们需要CRA,现在使用Vite就够了
同时,它创建出来的项目,只需要简单的配置就能让我们制作组件库,避免了配置rollup,支持ts,解析文件等一系列冗长的过程,可以快速地进行组件开发
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "node:path";
export default defineConfig({
plugins: [vue()],
build: {
outDir: "ljx-ui", //输出文件名称
//库编译模式配置
lib: {
entry: path.resolve(__dirname, "./src/index.ts"), //指定组件编译入口文件
name: "ljx-ui",
fileName: "ljx-ui",
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ["vue"],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: "Vue",
},
},
},
},
});
plugin
rollup-plugin-visualizer
rollup-plugin-visualizer可以可视化并分析Rollup包,以查看哪些模块占用了空间
vite-plugin-image-optimizer
图片压缩
@vitejs/plugin-legacy
vite打出来的包是基于ES6的,如果需要打包为ES5的话,需要使用该插件
babel
Babel是一个JavaScript编译器和转换工具,用于将新版本的JavaScript代码转换成向后兼容的旧版本。
babel/preset-env基于目标环境的配置,可以自动确定所需的Babel插件和polyfill。它会根据目标环境的配置信息,比如目标浏览器版本、Node.js版本等,来确定需要转换和填充的功能和语法。babel/polyfill是用于填充缺失ES6+代码功能的库。它会根据浏览器或环境的支持情况,自动添加所需的polyfill,以使代码能在不支持相关功能的环境中正常运行,但是会污染全局环境(已废弃),因为babel-polyfill本身就是core-js和babel-regenerator的集合core-js官方的polyfill集合,regenerator是babel对generator的补充babel/runtime:babel/polyfill的强化版,避免重复生成辅助函数,避免全局污染
babel处理语法,webpack处理模块化,相互配合
考点
前端代码为何要进行构建和打包?
- 体积更小,加载更快
- 编译高级语言或语法
- 兼容性和错误检查
- 统一、高效的开发环境
- 统一的构建流程和产出标准
- 集成公司构建规范(提测、上线)
webpack如何打包MPA?
配置多个entry与多个html,指定html注入哪些chunk
const path = require('path')
const config = {
entry: path.resolve(__dirname, '../src/main.js')
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, '../dist')
}
plugins:[
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../public/index.html'),
})
]
...
}
webpack如何实现异步加载?
其实就是ESModule的import,只要我们的js里面使用了ES的异步加载,webpack就会将这个文件单独打包
webpack如何处理JSX?
安装@babel/preset-react并在babel.config.js配置,webpack就知道该如何解析.jsx文件了,并且现在写react时,已经可以在.js文件中写了
babel/polyfill和babel/runtime的区别
babel/polyfill会污染全局babel/runtime不会污染全局- 产出第三方
lib要用babel/runtime
Proxy为何不能被Polyfill
Class可以用function模拟Promise可以用callback模拟- 但
Proxy用Object.defineProperty模拟
我们会使用webpack对css进行哪些处理?
思路:转为css => 模块化 => 添加前缀 => 生成单独文件 => tree shaking => 压缩
- 如果我们遇到的如果是
scss、less等css的预编译语言,会先使用sass-loader、less-loader将其转换为css - 使用
postcss-loader对css进行添加前缀等兼容性处理 - 使用
css-loader对css进行模块化处理,避免样式重名,使其可以在js中使用模块化规范进行导入 - 开发环境:
- 使用
style-loader将css注入到html的header中 生产环境: - 使用
mini-css-extract-plugin将css单独剥离出来方便进行按需导入 - 使用
purgecss-webpack-plugin插件进行css的tree-shaking避免将我们未使用到的样式打包进生产代码中 - 使用
css-minimizer-webpack-plugin对css进行压缩、删除缩进、注释等处理
- 使用
为什么生产环境不使用style-loader?
-
因为
style-loader将CSS以<style>标签的形式插入到页面中,非常方便,但它会导致每个CSS文件都被打包为一个<style>标签,增加了HTML文件的大小。而在生产环境中,我们希望将CSS文件单独提取出来,以减少HTML文件的体积,减少页面加载时间,提高性能。 -
在使用
style-loader时,浏览器解析和执行<style>标签时会阻塞并行加载其他资源(如JS文件),因为CSS样式的渲染需要依赖于DOM树的构建。而将CSS文件提取为独立的文件后,浏览器可以并行加载CSS和JS文件,提高页面的加载速度。 -
使用
mini-css-extract-plugin将CSS提取为独立的文件后,可以针对该文件使用浏览器缓存,当页面再次加载时,可以直接从缓存中获取CSS文件,减少了网络请求,提高了再次加载页面的速度。
如果你使用Vite需要如何处理Scss文件?
不需要处理,vite已经进行了预配置,无论是scss还是less,无论是生产环境还是开发环境,它都会直接帮我们编译为css并且完成剥离压缩等操作
我们应该如何使用webpack对图片进行处理?
思路: base64内置/打包到dist => 压缩
- 在
webpack4中我们需要单独安装url-loader和file-loader对图片进行处理 - 在
webpack5中我们可以通过内置的moduleAssets特性对图片进行处理 - 使用
image-webpack-loader将图片进行压缩
你知道哪些新兴工具代替babel-loader进行编译处理?
我们可以使用swc-loader、esbuild-loader代替babel-loader进行处理
这些工具相比较于babel-loader和babel的优势和劣势在哪?
优点:它们调用的编译器的处理速度快于babel,因为它们拥有强悍的多核并行处理能力,可以达到从编译上提升构建效率的目的
缺点:esbuild-loader它仅仅会扫描,进行词法解析,语法解析,构建出AST,无法操作AST, 没有TS类型检查,因此它是没有办法将ES6代码转换为ES5代码的
如何提升webpack的构建效率?
我们可以考虑从构建速度、构建体积上结合webpack的构建流程去进行优化
- 使用
speed-measure-webpack-plugin进行构建速度分析,使用webpack-bundle-analyzer进行构建体积分析 - 精准化,避免对无需进行编译的模块进行模块分析,编译等过程
- 使用
webpack.IgnorePlugin跳过一些第三方模块自动引入的模块 - 配置
module.noParse跳过一些我们手动引入的模块,如:XXX.min.js
- 使用
- 合理的分包策略,可以提升首屏速度,页面二次加载时,合理运用缓存
- 切割出源代码、公共代码
- 切割出不常变依赖,如
vue全家桶、react全家桶 - 切割出常变依赖,如
lodash-es、echarts等
- 配置剥离、压缩、混淆代码
- 开启
scope hosting进行作用域提升 - 使用
CDN加速 - 考虑是否开启多进程压缩、多进程打包
开发环境下可以通过开启cache、或者配置DllPlugin动态链接库来提升本地的构建效率。
webpack5由于cache支持缓存在硬盘上,所以不需要配置动态链接库了,并且可以考虑使用esbuild-loader、swc-loader等代替babel-loader提升开发体验
webpack的瓶颈在哪?
Webpack主要是通过抓取-编译-构建整个应用的代码(也就是常说的打包过程),生成一份编译、优化后能良好兼容各个浏览器的的生产环境代码。在开发环境流程也基本相同,需要先将整个应用构建打包后,再把打包后的代码交给dev server。
随着前端业务的复杂化,js代码量呈指数增长,打包构建时间越来越久,dev server性能遇到瓶颈:
* 缓慢的服务启动: 大型项目中dev server启动时间达到几十秒甚至几分钟。
* 缓慢的HMR热更新: 即使采用了 HMR 模式,其热更新速度也会随着应用规模的增长而显著下降,已达到性能瓶颈,无多少优化空间。