构建知识点整理

842 阅读23分钟

构建工具复习篇

前言

我们的写的代码和浏览器上运行的代码并不是相同的代码,我们的代码还需要进行一些列的处理才可以在浏览器上,这个处理的过程叫做构建,它是一些列工具共同运作的结果。

构建工具和打包工具是什么?

构建工具(Build Tool) 是一种用于自动化构建、编译和部署项目的工具。它们能够将开发者编写的源代码以及其他资源文件(如样式表、图像等)转化为可在生产环境中运行的优化、压缩的代码。

构建工具的主要功能包括:

  1. 编译:将高级语言(如ES6TypeScriptSCSS等)转换为浏览器Node.js能够理解和执行的低级语言(如ES5CSSHTML等)。

  2. 压缩:去除空格、注释、无用的代码,并对代码进行优化,以减小文件大小和提升性能。

  3. 模块化支持:将模块化的代码拆分成不同的文件,并将它们合并、压缩为单个文件,以减少网络请求,提高加载速度。

  4. 依赖管理:根据代码中的依赖关系,自动处理和管理所需的第三方库或模块,并将它们打包到最终的输出文件中。

  5. 自动化任务:执行其他开发任务,如代码检查、测试运行、文档生成等。

当项目变得复杂时,手动完成这些工作将变得非常耗时且容易出错。构建工具的出现极大地简化了这些任务的处理,并提供了一种便捷的方式来自动化构建和管理项目。

打包工具(Bundle Tool) 是前端开发中常用的工具之一,它用于将项目中的多个模块或文件打包成一个或多个较大的文件,以减少网络请求和提高加载性能。

构建 针对任务处理,打包 针对文件、模块、资源的整合与输出

由于构建打包的概念有时会重叠,同时一些现代前端工具(如WebpackVite)已经集成了构建和打包功能,模糊了构建打包的界限,因此在实际应用中,这两个术语常常被混用。

Webpack

image.png

webpack核心概念

  • mode: 采取的打包模式
  • devtool:是否产生source-map
  • entry:打包入口
  • module:模块,源码
  • loader:文件转换器
  • chunk:构建过程中多模块集合的代码块(中间产物)
  • plugin:插件,执行特定任务
  • output:打包出口
  • bundle: 最终产物
  • optimization:压缩、剥离等优化配置

安装:npm install webpack webpack-cli -D

理解编译产物bundle

bundle的本质是一个iifewebpack通过iife实现函数私有化的作用域

image.png 如图:

  • 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内导入导出,可它本身并没有被挂到htmlheaderstyle

style-loader(将CSS模块转为DOM)

style-loader会将bundle中的css模块注入到htmlheaderstyle内,具体的注入方式是它在获取到css的相关模块后会进行DOM操作,在header内插入style标签,从而使css生效

style-loader是基于css-loadercss转换为module进行的

mini-css-extract-plugin.loader

我们可以用mini-css-extract-pluginloader代替style-loader从而将cssbundle中剥离出来,单独生成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

使用Babelwebpack编译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中进行执行?

所以,我们需要定义一个loaderwebpack用来解析.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提取到单独的文件中。它为每个包含CSSJS文件单独创建CSS文件。它支持按需加载CSSSourceMaps

ProvidePlugin

ProvidePluginwebpack的内置plugin,自动加载模块,而不必在任何地方导入或需要它们,提供全局标识符映射到指定模块,如Jquery映射为$

CopyWebpackPlugin

CopyWebpackPluginwebpack的内置plugin, 它能将已经存在的单个文件或整个目录复制到构建目录

如:图片等静态资源,我们可以直接通过该pluginsrc目录下复制到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代码,类似于实现了csstree-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能将JavaScriptCSS资源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.concatenateMoudlestrue即可开启

自定义plugin

webpack hook

  1. compiler钩子类,针对chunk生成以前
  2. 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配置

image.png

  • 开启缓存后,只要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 4cache只能存储与内存中
  • webpack 5还可以直接存储与硬盘上

热更新 VS 自动刷新

  • 自动刷新: 整个网页全部刷新,速度较慢,状态会丢失
  • 热更新:新代码生效,网页不刷新,状态不丢失

体积优化

  • 先使用webpack-bundle-analyzer进行构建体积分析
  • bundlecontentHash
  • 懒加载
  • 公共代码拆分
  • webpack.IgnorePlugin忽略不需要的第三方依赖自身引入的模块
  • 使用CDN加速
  • 使用production模式
  • scope hosting(作用域提升)

使用production的好处

  • 自动压缩代码
  • VueReact等会自动删除调试代码
  • 启动Tree-shaking

从bundle剥离css资源

bundle中,css-loaderstyle-loader在进行了css模块化以及插入DOM的操作后与原来的css相比,体会会变得非常大,因此需要将cssbundle中剥离出来,按需加载

js和css的压缩 + treeshaking

  • 不改变源码,去除无意义的空格,换行,注释,从而改变体积大小,让包变得更小
  • 线上用不到的代码,不打包仅bundle

treeshaking是有触发条件的:

  1. esm的规范
  2. 通过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分包

常用来对一些大体积,构建慢,并且稳定不常变动的依赖(如:VueReact的全家桶)进行分包,既解决构建速度优化的问题,也能解决体积优化的问题

  1. 新建webpack.dll.config.js作为单独分包的配置文件
  2. 配置分包规则,规则同optimization.splitchunks.cacheGoup类似
  3. 配置webpack.dll.config.jsDllPlugin单独对dll进行打包
  4. 配置scripts——dll:webpack --config webpack.dll.config.js
  5. 配置webpack.config.jsDllReferencePlugin,打包生产模式代码时通过dllmanifest.json使用Dll的打包结果
  6. 配置add-asset-html-webpack-plugin注入我们的dll打出来的包的本体到dist目录下,并在html中引入它

现在无论是vue-cli还是CRA都不选择使用dll

Splitchunks分包

  • 分割所有chunk
  • 根据需要配置cacheGroups(缓存分组)
  • 设置切割的引用次数界限
  • 一般配置为vendor(第三方依赖)和common(我们写的源代码)
  • vendor又可以进一步分包,分为常变依赖不常变依赖
  • 常变依赖lodashecharts等,可能因为业务开发而会发生引入变更,可能会时常发生引入变更的依赖
  • 不常变依赖vue全家桶、react

splitChunks VS Dll

  1. dll配置明显麻烦得多
  2. dll包的大小,或者是dll包里的库文件需要webpack处理的越多越大dll方案的性能提升效果越是明显,否则可能是副提升
  3. dll不支持按需加载,而splitChunks支持按需加载
  4. vue-cli,CRA都选择不使用dll
  5. 优先推荐splitChunks

详情可看:DllPlugin实践分析

webpack4 VS webpack5

  1. webpack5拥有更好的tree-shaking特性,打包体积明显小了很大,因为少了很多模块加载的代码,文件压缩策略更优
  2. webpack5cache可以缓存到本地,webpack4默认只能选择truefalse,如果要缓存到本地需要借助其它的插件——hard-source-webpack-plugin,并且webpack5的缓存构建速度优于webpack4
  3. webpack4处理资源文件需要借助url-loaderfile-loader的帮助,而在webpack5中可以直接使用内置的moduleAssets特性进行处理
  4. webpack5支持更好的URI解析,比如引入文件的时候可以通过file协议进行引入
  5. webpack5打的包能很好支持package.jsonexports特性,如果是通过ESM的方式引入的,它会以对应的lib指向的import路径为入口进行扫描,分析并打包,如果是通过CommonJS引入的,则以require路径为入口扫描打包

ModuleFederation(模块联邦)

感觉这玩意有点难描述,个人理解,类似与用vue3去进行defineExport,然后另外一个包去接收,只不过换成了用webpack通过ModuleFederationPluginexposes去进行暴露,需要引入的包用remotes去接受这个暴露的包

Vite

image.png

  • dev环境采用esbuild进行打包
  • prod环境采用rollup打包
  • vite已经内置了很多配置,几乎不需要我们关心文件如何解析
  • 不区分loaderplugin,它只有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中,我们没有配置对应的模块解析规则,那么我们将无法正常工作,比如:scssless, 而Vite可以处理SCSSLess文件,它使用内置的 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就够了 image.png

同时,它创建出来的项目,只需要简单的配置就能让我们制作组件库,避免了配置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-jsbabel-regenerator的集合
  • core-js官方的polyfill集合,regeneratorbabelgenerator的补充
  • babel/runtime: babel/polyfill的强化版,避免重复生成辅助函数,避免全局污染 image.png

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如何实现异步加载?

其实就是ESModuleimport,只要我们的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模拟
  • ProxyObject.defineProperty模拟

我们会使用webpack对css进行哪些处理?

思路:转为css => 模块化 => 添加前缀 => 生成单独文件 => tree shaking => 压缩

  1. 如果我们遇到的如果是scsslesscss的预编译语言,会先使用sass-loaderless-loader将其转换为css
  2. 使用postcss-loadercss进行添加前缀等兼容性处理
  3. 使用css-loadercss进行模块化处理,避免样式重名,使其可以在js中使用模块化规范进行导入
  4. 开发环境:
    • 使用style-loadercss注入到htmlheader中 生产环境:
    • 使用mini-css-extract-plugincss单独剥离出来方便进行按需导入
    • 使用purgecss-webpack-plugin插件进行csstree-shaking避免将我们未使用到的样式打包进生产代码中
    • 使用css-minimizer-webpack-plugincss进行压缩、删除缩进、注释等处理

为什么生产环境不使用style-loader

  • 因为style-loaderCSS<style>标签的形式插入到页面中,非常方便,但它会导致每个CSS文件都被打包为一个<style>标签,增加了HTML文件的大小。而在生产环境中,我们希望将CSS文件单独提取出来,以减少HTML文件的体积,减少页面加载时间,提高性能。

  • 在使用style-loader时,浏览器解析和执行<style>标签时会阻塞并行加载其他资源(如JS文件),因为CSS样式的渲染需要依赖于DOM树的构建。而将CSS文件提取为独立的文件后,浏览器可以并行加载CSSJS文件,提高页面的加载速度。

  • 使用mini-css-extract-pluginCSS提取为独立的文件后,可以针对该文件使用浏览器缓存,当页面再次加载时,可以直接从缓存中获取CSS文件,减少了网络请求,提高了再次加载页面的速度。

如果你使用Vite需要如何处理Scss文件?

不需要处理,vite已经进行了预配置,无论是scss还是less,无论是生产环境还是开发环境,它都会直接帮我们编译为css并且完成剥离压缩等操作

我们应该如何使用webpack对图片进行处理?

思路: base64内置/打包到dist => 压缩

  • webpack4中我们需要单独安装url-loaderfile-loader对图片进行处理
  • webpack5中我们可以通过内置的moduleAssets特性对图片进行处理
  • 使用image-webpack-loader将图片进行压缩

你知道哪些新兴工具代替babel-loader进行编译处理?

我们可以使用swc-loaderesbuild-loader代替babel-loader进行处理

这些工具相比较于babel-loaderbabel的优势和劣势在哪?

优点:它们调用的编译器的处理速度快于babel,因为它们拥有强悍的多核并行处理能力,可以达到从编译上提升构建效率的目的

缺点:esbuild-loader它仅仅会扫描,进行词法解析,语法解析,构建出AST,无法操作AST, 没有TS类型检查,因此它是没有办法将ES6代码转换为ES5代码的

如何提升webpack的构建效率?

我们可以考虑从构建速度构建体积上结合webpack的构建流程去进行优化

  1. 使用speed-measure-webpack-plugin进行构建速度分析,使用webpack-bundle-analyzer进行构建体积分析
  2. 精准化,避免对无需进行编译的模块进行模块分析,编译等过程
    • 使用webpack.IgnorePlugin跳过一些第三方模块自动引入的模块
    • 配置module.noParse跳过一些我们手动引入的模块,如:XXX.min.js
  3. 合理的分包策略,可以提升首屏速度,页面二次加载时,合理运用缓存
    • 切割出源代码、公共代码
    • 切割出不常变依赖,如vue全家桶、react全家桶
    • 切割出常变依赖,如lodash-esecharts
  4. 配置剥离、压缩、混淆代码
  5. 开启scope hosting进行作用域提升
  6. 使用CDN加速
  7. 考虑是否开启多进程压缩、多进程打包

开发环境下可以通过开启cache、或者配置DllPlugin动态链接库来提升本地的构建效率。 webpack5由于cache支持缓存在硬盘上,所以不需要配置动态链接库了,并且可以考虑使用esbuild-loaderswc-loader等代替babel-loader提升开发体验

webpack的瓶颈在哪?

Webpack主要是通过抓取-编译-构建整个应用的代码(也就是常说的打包过程),生成一份编译、优化后能良好兼容各个浏览器的的生产环境代码。在开发环境流程也基本相同,需要先将整个应用构建打包后,再把打包后的代码交给dev server

随着前端业务的复杂化,js代码量呈指数增长,打包构建时间越来越久,dev server性能遇到瓶颈: * 缓慢的服务启动: 大型项目中dev server启动时间达到几十秒甚至几分钟。 * 缓慢的HMR热更新: 即使采用了 HMR 模式,其热更新速度也会随着应用规模的增长而显著下降,已达到性能瓶颈,无多少优化空间。