写在前面的:说实话,平时项目的 webpack 配置几乎我没有参与过。然后觉着还是有必要整理相关的一些题目巩固一下。首先呢,我推荐还是去官网稍微了解一下基础知识。
webpack 与 grunt、gulp 的不同?
三者都是构建工具,但是侧重点不同。
-
grunt & gulp
基于任务和流的。找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个 web 的构建流程。 -
webpack
基于入口的。webpack 会自动地递归解析入口所需要加载的所有资源文件,然后用不同的 loader 来处理不同的文件,用 plugin 来扩展 webpack 功能。
有哪些常见的 loader?他们是解决什么问题的?
- babel-loader:把 ES6 转换成 ES5
- ts-loader: 将 ts 转换成 js
- css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
- style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS
- less-loader:将 Less 编译为 CSS 的 loader
- sass-loader:加载 Sass/SCSS 文件并将他们编译为 CSS
- file-loader:将一个文件中的 import/require() 解析为 url,并且将文件发送到输出文件夹。
- eslint-loader:使用 eslint 对代码进行格式化
有哪些常见的 plugin?他们是解决什么问题的?
- HtmlWebpackPlugin:简化了 HTML 文件的创建
- MiniCssExtractPlugin:将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。
- DefinePlugin:在编译时创建配置的全局常量
- ProgressPlugin:提供编译进度
- BundleAnalyzerPlugin:可视化 Webpack 输出文件的体积
loader 和 plugin 的不同?
(1)定义
- loader 直译为"加载器"。Webpack将一切文件视为模块,但是 webpack 原生是只能解析 js 文件,如果想将其他文件也打包的话,就会用到 loader。 所以 loader 的作用是让 webpack 拥有了加载和解析非 js 文件的能力。
- plugin 直译为"插件"。plugin可以扩展 webpack 的功能,让 webpack 具有更多的灵活性。 在 webpack 运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果。
(2)用法
- loader 在 module.rules 中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个 Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)
- plugin 在 plugins 中单独配置。 类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入。
css-loader 和 style-loader 区别?
- css-loader:处理 css 文件;会对 @import 和 url() 进行处理。
- style-loader:把 CSS 插入到 DOM 中。
file-loader 和 url-loader 区别?
- file-loader:将一个文件中的 import/require() 解析为 url,并且将文件发送到输出文件夹,最终返回 url。
- url-loader:可以通过 limit(单位:byte) 属性对图片分情况处理,当图片小于 limit 时转 base64,大于等于 limit 时调用 file-loader 处理。
什么是bundle,什么是chunk,什么是module
- bundle:是由 webpack 打包出来的文件
- chunk: 是指 webpack 在进行模块的依赖分析的时候代码分割出来的代码块
- module:是开发中的单个模块
什么是entry,output?
-
entry:指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。默认为
./src/index.js
。 -
output:告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是
./dist/main.js
,其他生成文件默认放置在 ./dist 文件夹中。
什么是Tree-shaking?CSS可以Tree-shaking?
Tree-shaking 是指在打包中取出那些引入了但在代码中没有被用到的死代码。webpack中通过 uglifysPlugin 来Tree-shaking JS。CSS需要使用 purify-CSS
webpack 基本功能?
(1)代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
(2)文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等
(3)代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
(4)模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
(5)自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
(6)代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
(7)自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
webpack 构建流程?(介绍一下webpack 的整个生命周期?)
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
(1)初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
(2)开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
(3)确定入口:根据配置中的 entry 找出所有的入口文件;
(4)编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
(5)完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
(6)输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
(7)输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
简单说
(1)初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
(2)编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
(3)输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
如何提高webpack的构建速度?
使用高版本的 webpack
多线程/多实例构建
缩小打包作用域:
exclude/include
(确定 loader 规则范围)
resolve.modules
(指明第三方模块的绝对路径,减少不必要的查找)
resolve.extensions
(减少后缀尝试的可能性)
noParse
(对不需要解析的库进行忽略,虽然不必解析但依旧会被打包到 bundle 中,被忽略的文件不应该包含 import、require、define等模块化语句)
IgnorePlugin
(完全排除模块)
合理使用 alias
充分利用缓存提升二次构建速度
babel-loader 开启缓存
terser-webpack-plugin 开启缓存
cache-loader 或者 hard-source-webpack-plugin 开启缓存DLL
使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接)对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。
如何优化打包大小?
压缩代码
通过 mini-css-extract-plugin 插件提取 css 到单独文件中;等提取页面公共资源
利用 cdn 加速Tree Shaking
删除死代码,尽可能多使用 es6 模块;优化图片
对于小图可以使用 base64 的方式写入文件中,
配置 image-webpack-plugin 等;scope hoisting
将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当重命名以防止变量名冲突。动态 polyfill
只给用户返回需要的 polyfill;
了解哪些模块化标准?
(1) CommonJS
-
介绍
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。
同步的模块加载方式不适合在浏览器环境中,因为会阻塞加载,浏览器资源多是异步加载的。 -
基本语法
暴露模块:module.exports = value
或exports.xxx = value
引入模块:require(xxx)
(2) AMD
AMD规范则是非同步加载模块,允许指定回调函数。
(3) CMD
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。
(4) UMD
规范类似于兼容 CommonJS 和 AMD 的语法糖,是模块定义的跨平台解决方案。
(5) ES6模块
编译时能确定模块的依赖关系,以及输入和输出的变量。
-
基本语法
暴露模块:export {}
引入模块:import {} from XXX