前端webpack相关面试题(含答案)
webpack 作为前端最流行使用最广泛的构建工具,掌握 webpack 的基本知识也是非常重要。本文整理了前端面试常见的 webpack 相关的面试题,以及 webpack 面试题目相关的答案,希望对你有所帮助。
webpack 打包流程
- 初始化参数:从 shell 或者 配置文件中读取参数。
- 编译:创建 Compiler 对象,加载所有的插件,并且调用 Compiler.run() 方法开始执行。
- 确定入口:通过 entry 找到入口文件
- 编译模块:递归所有依赖的模块。形成关系树。通过 loader 进行编译转换最终的 JS 文件
- 输出文件:根据依赖关系树,将模块依次输出到文件中。
在整个打包过程,webpack 内部通过 Tapable 发布订阅者模式,让相关的 webpack 插件可以监听到打包过程中的事件,并且可以做一些额外的处理。
更多 Tapable 的使用方法,可以参考 Webpack 核心库 Tapable 的使用教程
webpack 构建过程中,compiler 和 compilation 对象的作用是什么
compiler
是全局对象,用于管理 webpack 的整个构建过程。
compilation
是每次构建的上下文对象,包含每次构建的所有信息,比如每次更新文件重新构建,compiler
都会创建一个新的 compilation
对象。
webpack 构建产物解析
webpack 构建最终产物是一个 IIFE 执行函数
(() => { // webpackBootstrap
var __webpack_modules__ = ({
'file-A-path': ((modules) => { // ... })
'index-file-path': ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { // ... })
})
// The module cache
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = __webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {}
};
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// Return the exports of the module
return module.exports;
}
// startup
// Load entry module and return exports
// This entry module can't be inlined because the eval devtool is used.
var __webpack_exports__ = __webpack_require__("./src/index.js");
})
查看上面的代码可以看出,__webpack_modules__
存放了编译后的各个文件模块的 JS 内容,__webpack_module_cache__
用来做模块缓存,__webpack_require__
是 Webpack
内部实现的一套依赖引入函数。最后一句则是代码运行的起点,从入口文件开始,启动整个项目。
为什么要实现 __webpack_require__
函数,我们在模块化开发的时候,通常会使用 import
或者 require
引入依赖模块,webpack
打包编译的时候,会统一替换成自己的 __webpack_require__
来实现模块的引入和导出,方便实现模块缓存机制,以及抹平模块规范之间的一些差异性。
sourceMap 是什么?
sourceMap
是一项将编译、打包、压缩后的代码映射源代码的技术,由于打包压缩后的代码并没有阅读性可言,一旦在开发中报错或者遇到问题,直接在混淆代码中 debug
问题会带来非常糟糕的体验,sourceMap
可以帮助我们快速定位到源代码的位置,方便我们定位问题。sourceMap
并不是 Webpack
特有的功能,而是 Webpack
支持 sourceMap
,像 JQuery
也支持 souceMap
。
既然是一种源码的映射,那必然就需要有一份映射的文件,来标记混淆代码里对应的源码的位置,通常这份映射文件以.map
结尾,里边的数据结构大概长这样:
{
"version" : 3, // Source Map版本
"file": "out.js", // 输出文件(可选)
"sourceRoot": "", // 源文件根目录(可选)
"sources": ["foo.js", "bar.js"], // 源文件列表
"sourcesContent": [null, null], // 源内容列表(可选,和源文件列表顺序一致)
"names": ["src", "maps", "are", "fun"], // mappings使用的符号名称列表
"mappings": "A,AAAB;;ABCDE;" // 带有编码映射数据的字符串
}
其中mappings
数据有如下规则:
- 生成文件中的一行的每个组用“;”分隔;
- 每一段用“,”分隔;
- 每个段由 1、4 或 5 个可变长度字段组成;
有了这份映射文件,我们只需要在我们的压缩代码的最末端加上这句注释,即可让 sourceMap 生效:
//# sourceURL=/path/to/file.js.map
有了这段注释后,浏览器就会通过 sourceURL
去获取这份映射文件,通过解释器解析后,实现源码和混淆代码之间的映射。
如果我们仔细查看 webpack 打包出来的 bundle 文件,就可以发现在默认的 development
开发模式下,每个 _webpack_modules__
文件模块的代码最末端,都会加上 //# sourceURL=webpack://file-path?
,从而实现对 sourceMap 的支持。
TreeShaking 的原理
TreeShaking 就是在打包构建的时候,把项目中无用的代码移除掉,用来减少构建后的文件大小。
原理:基于 ES6 的模块特性对代码进行静态分析,确定无用代码并移除掉。通常把 import & export 标记为 3 类:
- 所有 import 标记为 / harmony import /
- 被使用过的 export 标记为 /harmony export([type])/,其中 [type] 和 webpack 内部有关,可能是 binding,immutable 等;
- 没有被使用的 export 标记为 / unused harmony export [FuncName] /,其中 [FuncName] 为 export 的方法名,之后使用 Uglifyjs(或者其他类似的工具)进行代码精简,把没用的都删除。
为何 TreeShaking 依赖于 es6 的模块特性
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是 immutable 的
TreeShaking 的条件
- 必须遵循 ES6 的模块规范 (import,export 语法) 来书写代码,如果是 CommonJS 规范 (require) 则无法使用。
- 编写的模块代码不能有副作用,比如在代码内部改变了外部的变量则不会被移除。