【面试】Webpack常见面试题
面试官灵魂三连问
- 你用过webpack么,它的原理是什么?
- 你还知道些构建工具,各有什么优缺点?
- 你在项目中有配置过么,在哪些场景配置过?
Webpack介绍
什么是webpack
webpack 是一个模块打包器。webpack 的主要目标是将 JavaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换(transform)、打包(bundle)。
webpack原理是什么
工作原理
webpack可以看做是模块打包机:
它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
在3.0出现后,Webpack还肩负起了优化项目的责任。
打包原理
把一切都视为模块:不管是 css、JS、Image 还是 html 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。
按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载
核心概念
- Entry:入口,webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
- output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
- Module:模块,在 webpack 里一切皆模块,一个模块对应着一个文件。webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
- Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
- Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
- Plugin:扩展插件,在 webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
构建流程
Webpack 的运⾏流程是⼀个串⾏的过程,从启动到结束会依次执⾏以下流程:
- 初始化参数:解析webpack配置参数,合并shell传入和webpack.config.js文件配置的参数,形成最后的配置结果。
- 开始编译:上一步得到的参数初始化compiler对象,注册所有配置的插件,插件监听webpack构建生命周期的事件节点,做出相应的反应,执行对象的 run 方法开始执行编译。
- 确定入口:从配置的entry入口,开始解析文件构建AST语法树,找出依赖,递归下去。
- 编译模块:递归中根据文件类型和loader配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
- 完成模块编译:在经过第4步使⽤ Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据⼊⼝和模块之间的依赖关系,组装成⼀个个包含多个模块的 Chunk,再把每个 Chunk 转换成⼀个单独的⽂件加⼊到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和⽂件名,把⽂件内容写⼊到⽂件系统。
注意:
在以上过程中,Webpack 会在特定的时间点,⼴播出特定的事件,插件在监听到感兴趣的事件后,会执⾏特定的逻辑。并且插件可以调⽤ Webpack 提供的 API ,改变 Webpack 的运⾏结果。比如UglifyPlugin,会在loader转换递归完,对结果使用UglifyJs压缩,覆盖之前的结果。
基本功能
- 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
- 文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
- 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
- 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
- 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
webpack基本配置
安装
全局安装
1 | npm install -g webpack |
项目依赖
1 | npm install --save-dev webpack |
基本配置-入口:
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
基本配置-出口
output 属性告诉 webpack 在哪里输出它所创建的 bundles
基本配置-loaders
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
基本配置-plugins
webpack插件(自动打开浏览器、热更新等)
- loader是使webpack拥有加载和解析非js文件的能力
- plugin 可以扩展webpack的功能,使得webpack更加灵活。可以在构建的过程中通过webpack的api改变输出的结果
基本配置-模式
package.json中设置:
1 | ”scripts": { |
webpack.config.js中设置:
1 | module.exports = { |
development:有内置的调试功能;打包后的代码可阅读,没被压缩
production:内置生产阶段的很多优化功能;代码被压缩
基本配置-启动本地服务
方式一:
在package.json里的script属性里配置,完了执行npm run xxx
例如vue项目中
package.json
1 | "scripts": { |
命令行:
1 | npm run dev |
方式二:
通过npx
例如vue项目中
命令行:
1 | npx cli-service serve |
模块化
webpack 默认使用common.js规范。
如果只是使用import 导入模后,浏览器是不能直接识别的,必须导入webpack才行。
其他基本常用配置
webpack跨域解决方案
proxy
ES6转ES5
babel
less转css
less-loader
注意:配置时执行顺序默认是从右到左,从下到上。
处理图片
方式一:url-loader
小于limit限制的转base64
说明:url-loader处理的图片一般比较小,会把图片转换成base64代码,直接添加页面。提供了一个方便的选择 limit,该选项用于控制当文件大小小于 limit 时才使用 url-loader。通常会设置5-8kb。
扩展:哈希的作用是命中缓存,使返回更快些。
方式二:file-loader
把图片转换成路径。
拆分配置和merge
背景:‘‘基本配置-模式’’,里面介绍过模式之间的区别,为了解决手动更改mode带来的不便,所以,要将这些打包的配置,拆分到不同的文件里面来。
- webpack.dev.config.js 开发环境的配置;
- webpack.prod.config.js生产环境的配置;
- webpack.base.config.js是开发环境和生产环境都会使用的配置;
- 通过webpack-merge模块将不同配置的代码合并;
webpack.dev.config.js
1 | const commonConfig = require('./webpack.base.config') |
webpack.prod.config.js
1 | const commonConfig = require('./webpack.base.config') |
业务场景
-
单页应用
-
一个项目管理多个单页面
-
代码分隔优化
一个好的代码分割对浏览器首屏效果提升很大。
-
构建服务端渲染
-
热更新
-
优化开发编译、生产环境打包速度
-
优化首屏速度
扩展:还知道哪些构建工具?有什么优缺点?
大致了解下就好 ,现在主流的是webpack、rollup、vite
构建工具有哪些?
webpack 与 grunt、gulp、FIS3、Vite等
他们有什么不同?
- 都是前端构建工具,grunt和gulp在早期比较流行,FIS3停止维护了,现在webpack相对来说比较主流,不过一些轻量化的任务,还是会用gulp来处理,比如单独打包CSS文件等。
- grunt和gulp是基于任务和流**(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
- webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
- webpack优点是专注于处理模块化的项目,尤其适合单页应用。Webpack的缺点是只能用于采用模块化开发的项目。
- FIS3并没有入口(entry)的概念,在FIS3中,构建流程不单单根据js来,而是分析每一个文件的依赖关系,生成一个资源表‘sourceMap’,你可以根据资源表定义任何的产出规则
- Fis3的优点是集成了各种Web开发所需的构建功能,配置简单、开箱即用。其缺点是目前官方已经不再更新和维护,不支持最新版本的Node.js。Fis3是一种专注于Web开发的完整解决方案,如果将Grunt、Gulp比作汽车的发动机,则可以将Fis3比作一辆完整的汽车。
总结:
从构建思路来说:gulp和grunt需要开发者将整个前端构建过程拆分成多个Task,并合理控制所有Task的调用关系; webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工;FIS3任何文件都可以作为入口
对于知识背景:gulp更像后端开发者的思路,需要对于整个流程了如指掌; webpack更倾向于前端开发者的思路。
参考文章:前端工具简介 fis3,webpack
补充:
- Webpack是一个模块打包器,他可以递归的打包项目中的所有模块,最终生成几个打包后的文件。
- 他和其他的工具最大的不同在于他支持code-splitting、模块化(AMD,ESM,CommonJs)、全局分析。
有类似webpack的工具么?
同样是基于入口的打包工具还有以下几个:webpack,rollup,parcel,vite,snowpack
从应用场景上来看:
webpack
:适合大型复杂的前端站点构建,尤其是模块化的,单页应用。rollup
:专门针对类库进行打包,它的优点是小巧而专注。因此现在很多我们熟知的库都都使用它进行打包,比如:Vue、React和three.js等。parcel
:零配置,傻瓜式。适用于简单的实验室项目,打包出错很难调试。不支持Tree Shaking。更多优点:传送门。vite
:灵活、复杂度适中,未来趋势。开发期间无需打包,越大型体验感越好。snowpack
与vite
类似。
rollup、vite、snowpack,都是基于ESM,开发期间无需构建,浏览器直用。
参考文章:
从Npm Script到Webpack,6种常见的前端构建工具对比
作为官方推荐的vite,和现在应用最广的webpack的区别?
需要理解,当下最火,被问到概率大
技巧提示:
加粗字体连起来,就是浓缩重点~
Webpack 是一个打包模块化 JavaScript 的工具,在 Webpack 里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。
一切文件:JavaScript、CSS、SCSS、图片、模板,在 Webpack 眼中都是一个个模块,这样的好处是能清晰的描述出各个模块之间的依赖关系,以方便 Webpack 对模块进行组合和打包。 经过 Webpack 的处理,最终会输出浏览器能使用的静态资源。因此每次编译都需要重新构建。
vite主要遵循的是使用ESM(Es modules模块)的规范来执行代码,由于现代浏览器基本上都支持了ESM规范,所以在开发阶段并不需要将代码打包编译成es5模块,即可直接在浏览器上运行。我们只需要从入口文件出发, 在遇到对应的 import
语句时,将对应的模块加载到浏览器中就可以了。当项目越大,文件越多时,vite的开发时优势越明显。vite热更新比webpack,vite在HRM方面,当某个模块内容改变时,让浏览器直接重新请求该模块即可,而不是像webpack重新将该模块的所有依赖重新编译。
Vite的使用简单,只需执行初始化命令,就可以得到一个预设好的开发环境,开箱即获得一堆功能,包括:CSS预处理、html预处理、异步加载、分包、压缩、HMR等。使用复杂度介于Parcel和Webpack的中间,只是暴露了极少数的配置项和plugin接口,既不会像Parcel一样配置不灵活,又不会像Webpack一样需要了解庞大的loader、plugin生态,灵活适中、复杂度适中。
总体来说,Vite在前端构建工具领域上开辟了一条和webpack完全不同的道路,很好地解决了前端开发阶段构建速度慢的问题。
参考文章:Vite介绍和原理解析
扩展:常见Webpack面试真题
前端为何要进行打包和构建?
代码层面:
- 体积更小(Tree-Shaking、压缩、合并),加载更快
- 编译高级语言或语法(TS、ES6+,模块化,SCSS)
- 兼容性和错误检查(Polyfill、postcss、eslint)
研发流程层面:
- 统一、高效的开发环境
- 统一的构建流程和产出标准
- 集成公司构建规范(提测、上线等)
module\chunk\bundle区别
- module:各个源码文件,webpack中一切皆模块。
- chunk:在内存中还没有产出的代码块。
- bundle:每个chunk打包后,都可以产生个bundle文件。
loader和plugin区别
- loader模块转换器,如less->css,举2-3个例子
- plugin扩展插件,如HtmlWebpackPlugin,举2-3个例子
Webpack的loader的加载顺序为什么是从右向左:
Webpack选择了compose方式(即 函数式编程),而不是pipe的方式。
对于loader来说呢,有一个最大的需求——任意组合。而使用compose函数就很好的能完成这个需求,我们只需要在loader中的顺序就可以完成这个函数嵌套的需求,让编程更精练、算法更清晰。
常见的loader和plugin
挑几个记一下。
全面完整版:
-
loader:www.webpackjs.com/loaders/
-
plugin:www.webpackjs.com/plugins/
babel和webpack的区别
Babel.js是新语法编译工具,不关心模块化。
webpack是打包构建工具,是多个loader、plugin的集合。你配置什么loader plugin,它就有什么能力。
bable-polyfill和able-runtime的区别
- babel-polyfill 会污染全局
- babel-runtime不会污染全局
- 产出第三方lib要用babel-runtime
webpack如何实现懒加载
- import()
- 结合Vue React异步组件
- 结合Vue-router React-router异步加载路由
为何Proxy不能被Polyfill?
因为没有任何东西,可以模拟它。
- 如Class可以用funciton模拟
- 如Promse可以用callback来模拟
- 但Proxy的功能用Object.defineProperty模拟
webpack如何优化构建速度?
可用于生产环境
-
优化babel-loader
-
IgnorePlugin
-
noParse
多进程:
-
happyPack
-
ParallelUglifyPlugin
不可用于生产环境
- 自动刷新
- 热更新
- DIIPlugin
如何利用webpack来优化前端性能
-
提取公共代码。
-
压缩代码。(development和production)
-
使用loader的时候,使用exclude排除node_modules中的文件
-
配置extractTextWebpackPlugin插件
-
使用TreeShaking插件:Tree-shaking 概念最早由Rollup.js 提出,后来在webpack2中被引入进来,但是这个这一特性能够被支持得益于ES6 modules的静态特性。ES6的模块声明相比于传统CommonJS的同步require有着本质区别。这种modules设计保证了依赖关系是提前确定的,使得静态分析成为了可能,与运行时无关。(除那些引用的但却没有使用的代码)
webpack-dev-server和http服务器如nginx有什么区别?
- webpack-dev-server使用内存来存储webpack开发环境下的打包文件
- 并且可以使用模块热更新
- 他比传统的http服务对开发更加简单高效。
什么是长缓存?在webpack中如何做到长缓存优化?
-
浏览器在用户访问页面的时候,为了加快加载速度,会对用户访问的静态资源进行存储,但是每一次代码升级或是更新,都需要浏览器去下载新的代码,最方便和简单的更新方式就是引入新的文件名称。
-
在webpack中可以在output纵输出的文件指定chunkhash,并且分离经常更新的代码和框架代码。
-
通过NameModulesPlugin或是HashedModuleIdsPlugin使再次打包文件名不变。
什么是Tree-shaking?CSS可以Tree-shaking吗?
- Tree-shaking是指在打包中去除那些引入了,但是在代码中没有被用到的那些死代码。
- 在webpack中Tree-shaking是通过uglifySPlugin来Tree-shaking JS
- css需要使用Purify-CSS。
Webpack5和Webpack4的区别有哪些?
1. Tree Shaking(强大)
如果我们的项目中引入了loader 包,但是我只用了其中的一个方法。其他没有用到的方法是不是冗余的?此时 tree-shaking 就可以把没有用的那些东西剔除掉,来大大减少最终的bundle体积。
2. 压缩代码
内部本身就自带 js 压缩功能,他内置了 terser-webpack-plugin 插件,我们不用再下载安装。
而且在 mode=“production” 的时候会自动开启 js 压缩功能。
3. 缓存配置
webpack5 内部内置了 cache 缓存机制。直接配置即可。
cache 会在开发模式下被设置成 type: memory
,而且会在生产模式把cache 给禁用掉。
4. 启动服务的差别
- webpack4 启动服务: 通过 webpack-dev-server 启动服务
- webpack5 启动服务: 内置使用 webpack serve 启动,但是他的日志不是很好,所以一般都加都喜欢用 webpack-dev-server 优化。
5. 打包
- webpack4打包: 即使后续没有使用到num1的函数,依然会将代码打包进去
- webpack5打包: 后续没有使用到num1的函数,不会将代码打包进去
6. 输出代码
- webpack4只能输出es5的代码
- webpack5新增属性output.ecmaVersion,可以生成ES5和ES6的代码
7. splitChunk
webpack4 将超过30kb的文件单独提为一个chunk
webpack5可以区分是js还是css,可以精确划分