https://juejin.cn/post/6943468761575849992
https://juejin.cn/post/6844904094281236487
# Webpack 面试
# Webpack 的作用是什么?
模块打包。可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。利用打包我们就可以在开发的时候根据我们自己的业务自由划分文件模块,保证项目结构的清晰和可读性。
通过 webpack 的 Loader 机制,不仅仅可以帮助我们对代码做 polyfill,还可以编译转换诸如.less, .vue, .jsx 这类在浏览器无法识别的格式文件,让我们在开发的时候可以使用新特性和新语法做开发,提高开发效率。
能力扩展。通过
webpack
的Plugin
机制,我们在实现模块化打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。
# webpack 与 grunt、gulp 的不同?
Grunt、Gulp 是基于任务运行的工具:
它们会自动执行指定的任务,就像流水线,把资源放上去然后通过不同插件进行加工,它们包含活跃的社区,丰富的插件,能方便的打造各种工作流。
Webpack 是基于模块化打包的工具:
自动化处理模块,webpack 把一切当成模块,当 webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle
# webpack 构建流程
初始化:从配置文件读取与合并参数,然后实例化插件
new Plugin()
开始编译:通过上一步获取的参数,初始化一个
Complier
对象加载插件,执行Compiler.run
开始编译确定入口:根据配置中
entry
找出所有入口文件编译模块:从
entry
出发,调用配置的loader
,对模块进行转换,同时找出模块依赖的模块,一直递归,一直到找到所有依赖的模块完成模块编译:这一步已经使用
loader
对所有模块进行了转换,得到了转换后的新内容以及依赖关系输出资源:根据入口与模块之间的依赖关系,组装成
chunk代码块
,生成文件输出列表输出成功:根据配置中的输出路径还有文件名,把文件写入系统,完成构建
# webpack 热更新流程
主要依赖 webpack
, express
, websocket
使用
express
开启本地服务,服务端和客户端使用websocket
开启长连接webpack
监听源文件的变化:每次编译完成之后都会生成: hash 值,改动模块的 json 文件,改动模块的 js 文件,然后向客户端推送当前编译的哈希值客户端拿到哈希值后,会和上一次进行比较,如果一致就拿缓存,如果不一致就通过
ajax
和jsonp
获取最新的资源使用内存文件系统替换修改内容实现更新
# 有哪些常见的 Loader?
file-loader
:把文件输出到一个文件夹中url-loader
:和file-loader
类似,但是能在文件很小的情况下以base64
的方式把文件内容注入到代码中去source-map-loader
:加载额外的Source Map 文件
,以方便断点调试image-loader
:加载并且压缩图片文件babel-loader
:把 ES6 转换成 ES5css-loader
:加载 CSS,支持模块化、压缩、文件导入等特性style-loader
:打包 css 文件的原理是先通过css-loader
打包 css 文件到指定目录,然后通过style-loader
将样式挂载到header
eslint-loader
:通过 ESLint 检查 JavaScript 代码less-loader
scss-loader
ts-loader
# 有哪些常见的 Plugin?
define-plugin
:定义环境变量html-webpack-plugin
:简化 html 文件创建clean-webpack-plugin
:帮助在打包之前删除之前打包的文件夹uglifyjs-webpack-plugin
:通过 UglifyES 压缩 ES6 代码webpack-parallel-uglify-plugin:
多核压缩,提高压缩速度webpack-bundle-analyzer:
可视化 webpack 输出文件的体积
# 分别介绍 bundle,chunk,module 是什么
bundle
是指webpack
打包出来的文件chunk
是指 webpack 进行模块依赖分析的时候,代码分隔出来的代码块module
是开发中的单个模块
# Loader 和 Plugin 的不同?
- webpack 原生只能解析 js 文件,如果想将其他文件也打包的话,就会用到 loader。 所以 Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力。
- Plugin 可以扩展 webpack 的功能,让 webpack 具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
# source map 是什么?生产环境怎么用?
source map 是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要 soucre map。
map 文件只要不打开开发者工具,浏览器是不会加载的。
编译后的map
{
"version": 3,
"file": "main.js",
"sources": [
"../src/main.js"
],
"sourcesContent": [
"throw new Error('error 1');\n"
],
"names": [
"Error"
],
"mappings": ";;AAAA,MAAM,IAAIA,KAAJ,CAAU,SAAV,CAAN"
}
线上环境一般有三种处理方案:
hidden-source-map
:借助第三方错误监控平台 Sentry 使用
hidden-source-map,就是不在 bundle 文件结尾处追加 sourceURL 指定其 sourcemap 文件的位置,但是仍然会生成 sourcemap 文件。这样,浏览器开发者工具就无法应用 sourcemap, 目的是避免把 sourcemap 文件发布到生产环境,造成源码泄露。生产环境应该用错误报告工具结合 sourcemap 文件来查找问题
nosources-source-map
:只会显示具体行数以及查看源代码的错误栈。安全性比 sourcemap 高,不带源码sourcemap
:通过 nginx 设置将 .map 文件只对白名单开放(公司内网)
# 文件指纹是什么?怎么用?
文件指纹是打包后输出的文件名的后缀。
Hash
:和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改Chunkhash
:和 Webpack 打包的 chunk 有关,不同的 entry 会生出不同的 chunkhashContenthash
:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变
- JS 文件指纹:设置 output 的 filename,用 chunkhash。
- CSS 文件指纹:设置 MiniCssExtractPlugin 的 filename,使用 contenthash。
- 图片的文件指纹位置,设置 file-loader 的 name,使用 hash。
# 说一下 webpack 中 css-loader 和 style-loader 的区别,file-loader 和 url-loader 的区别
file-loader: 将指定文件打包到制定目录
url-loader: 也有上述的效果,但是可以设置一个
limit
值,代表当文件超过这个大小,文件就会按照配置的规则进行打包输出到指定文件,如果没有超过,图片就会以 base64 格式保存在打包文件中,这样就有一个缺点,文件会越撑越大。css-loader: 打包 css 文件到制定目录
style-loader: 将样式挂在到 header
# 提升 webpack 的构建速度
使用高版本的
webpack
和Node.js
多进程、多实例构建:
thread-loader
压缩代码
- 多进程压缩代码
- webpack-paralle-uglify-plugin
- uglifyjs-webpack-plugin 开启 parallel 参数(不支持 ES6)
- terser-webpack-plugin 开启 parallel 参数
- 通过 mini-css-extract-plugin 提取 Chunk 的 CSS 代码到单独文件,通过 css-loader 的 minimize 选项压缩 css
图片压缩
- 使用基于 Node 的
imagemin
- 配置
image-webpack-loader
- 使用基于 Node 的
缩小打包作用域
exclude/include
确定 loader 规则范围resolve.modules
指明第三方模块的绝对路径
noParse
对完全不需要解析的库进行忽略- 合理使用
alias
提取公共资源
- 基础包分离
- 使用
html-webpack-externals-plugin
将基础包通过 CDN 引入,不打入 bundle
- 使用
- 使用 SplitChunksPlugin 进行(公共脚本、基础包、页面公共文件)分离
DLL
- 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间
利用缓存
- babel-loader 开启缓存
- terser-webpack-plugin 开启缓存
- cache-loader
Tree shaking
# webpack 的 proxy 是如何解决跨域的?
我们在使用 webpack 开发项目的时候,webpack 的 dev-server 模块会启动一个服务器,这个服务器不止帮我们做了自动更新,同时也可以做到反向代理。 就是我们把请求发送给 webpack-dev-server, 然后 webpack-dev-server 再去请求后端服务器,服务之间的请求是没有跨域问题的,只要后端返回了 webpack-dev-server 就能拿到,然后再返回给前端。
# Babel 基本原理
解析 Parse: 将代码解析生成抽象语法树( 即
AST
),即词法分析与语法分析的过程转换 Transform: 对于 AST 进行变换一系列的操作,babel 接受得到 AST 并通过
babel-traverse
对其进行遍历,在此过程中进行添加、更新及移除等操作生成 Generate: 将变换后的 AST 再转换为 JS 代码, 使用到的模块是
babel-generator