乐闻世界logo
搜索文章和话题
 Webpack 面试题汇总

Webpack 面试题汇总

乐闻的头像
乐闻

2022年06月18日 12:22· 阅读 379

什么是前端模块化?AMD、CMD、CommonJS、ES6模块化之间的区别是什么?

模块化 是指将一个复杂的系统分解为多个模块以方便编码。

模块化规范 的实现是为了达成浏览器端模块化的目的。

AMD 是一种Javascript模块化规范,采用异步的方式去加载依赖的模块。不用转换代码的情况下直接运行在浏览器环境。 依赖前置,提前执行。代表库 requirejs

CMD 是一种Javascript模块化规范,依赖就近,延迟执行。代表库seajs

CommonJS 通过 require 方法同步加载依赖的其他模块,通过module.exports导出需要暴露的接口。 优点:代码可以在Nodejs环境复用。缺点:代码不能直接运行在浏览器环境,必须通过工具转换成标准的ES5。

ES6是语言层面的模块化,浏览器和Node都原生支持,逐渐取代 CommonJSAMD。目前无法直接运行在大部分 JavaScript 环境,都需要通过工具转换标准的ES5代码。

webpack工作流程及原理

原理:Webpack 启动后会从 Entry 里配置的 Module 开始,递归解析 Entry 依赖的所有的 Module。没找到一个 Module ,就会根据配置的 Loader 去找出对应的转换规则,对 Module 进行转换后,再解析出当前 Module 依赖的 Module。这些模块会以 Entry 为单位进行分组,一个 Entry 及其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后,webpack 会将所有的 C hunk 转换成文件输出。在整个流程中,Webpack 会在恰当的时机执行Plugin里定义的逻辑。

工作流程:(加载 -> 编译 -> 输出)

  1. 读取配置文件,按命令初始化配置参数,创建 Compiler 对象
  2. 调用插件的 apply 方法挂载插件监听,然后从入口文件开始执行编译
  3. 按文件类型,调用相应的 Loader 对模块进行编译,并在合适的时机点出发对应的事件,调用 plugin 执行,最后再根据模块依赖查找到所依赖的模块,递归执行直到整个项目解析完毕。
  4. 将编译后的所有的代码包装成一个个代码块(chunk),并按依赖和配置确定输出内容。这个步骤仍然可以通过 plugin 进行文件的修改。
  5. 最后根据output配置把最终编译文件输出到指定的文件夹。

webpack核心配置

entry

string 入口模块的文件路径,只生成一个chunk

array 入口模块的文件路径,只有数组里的最后一个入口文件的模块会被导出

object 配置多个入口,每个入口生成一个chunk

output

filename 产物chunk文件名称

path 输出产物文件存在位置

publicPath CDN配置路径

libary 导出库的名称

libaryTarget 配置以何种方式到处库

module

rules loader, 配置模块的读取和解析规则。{test:/.js/,use:['babel-loader'],include:'',exclude:''}

noParse 不用解析和处理的模块

plugin 各种各样的 plugin 几乎可以让 web pack 做任何与构建相关的事情

resolve webapck在启动的时候会从配置的入口模块出发找到所有依赖的模块,resolve配置webapck如何寻找模块对应的文件。

alias 别名

extensions 导入没有带文件后缀时,extensions用于配置在尝试过程中用到的后缀列表。

modules 配置 webpack 去哪些目录下寻找第三方模块,默认只会去node_modules

resolveLoader 告诉 webpack 如何寻找 loader,用于加载本地的loader

target 配置webpack构建出针对不同运行环境的代码,比如web,node,async-node,webworker,electron-main,electron-renderer

devtool webpack 如何生成 sourceMap

watch webpack 监听模式,文件发生变化时重新编译

watchOptions 监听模式配置信息

externals webpack 要构建的代码中使用了哪些不用被打包的模块,也就是这些模块是外部环境。

Webpack 热更新原理

在我们开发时使用 Webpack Dev Server 提供热更新功能,webpack dev server 中存在两个server: Bundle Server , HMR Server。

初始化: webpack compiler 将源文件编译层 bundle.js,浏览器请求到bundle.js并与 webpack dev server 通过 websocket 建立连接。

热更新: webpack会对源文件进行监听,当修改了源文件,webpack compiler 重新编译,并通过 HMR server进行更新;

HMR 运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新。

Webpack Loader

因为 webpack 默认情况下只能识别 js 模块,如果我们需要处理不同格式文件,就需要配置对应的文件转换器,Loader 就是文件转换器。

Loader的职责应该保持单一性,我们只需要关心输入和输出,即Loader函数的参数是处理前的文件内容,返回处理后的内容。

通常需要将代码进行分析,构建AST,遍历进行定向修改后重新生成新的代码字符串。

  • 链式传递,按照配置的相反顺序链式执行
  • 基于 Node 环境,拥有较高权限
  • 可以同步也可以异步

Webpack Plugin

插件系统是 webpack 核心能力之一,在整个编译生命周期中,webpack 会触发许多事件钩子,plugin 可以监听这些事件,根据需求在相应的时间点对打包内容进行定向修改。

事件流机制:webpack 就像工厂中的一条产品流水线。原材料经过 Loader 与 Plugin 的一道道处理,最后输出目标产物。

Webpck 事件流编程规范核心是基于 Tapable, 是一种 观察者模式 的实现事件的订阅与广播。

Webpack 中两个最重要的类 CompilerCompilation 继承与 Tapable

Compiler: webpack 实例,全局唯一,从启动生成到结束。包含了当前webpack中所有的配置信息,比如 options, loaders,plugins等信息。启动时进行初始化创建,随着生命周期传递。

Compilation: 编译实例,当监听到文件发生变化时,webpack 会创建一个新的 compilation 对象,开始一次新的编译。包含当前的输入资源,输出资源,变化的文件,通过它提供的api可以监听每次编译过程中触发的事件钩子。

loader 和 plugin 有什么区别?

Webpack 默认只能打包 JS 和 JSON 文件,要打包其他模块,需要借助loader, loader就可以让模块中的内容转化成webpack或者其他loader可以识别的内容。

loader 用于模块内容转换,plugin参与整个webpack 打包流程,在合适的时机,可以处理不同的事件。

Webpack 编译优化途径

  1. 打包速度优化:

    1. 减少文件搜索范围(loader的test正则匹配,include,exclude)
    2. noParse:避免对非模块化文件的加载
    3. 别名alias(减少导入模块路径推导过程)缓存目录,避免重复寻址
    4. Happypack并行调用打包
    5. babel可以缓存编译结果,避免多次重复编译,配置cacheDirectory
    6. 第三方库模块缓存:DLLPlugin 可以提前进行打包并缓存,避免每次都重新编译
    7. 使用分析工具 webpack Analyse/ webpack-bundle-analyzer 对打包的文件进行分析,寻找耗时长可优化的地方
  2. 代码优化

    1. 无用代码消除,减少最终产物体积(tree-shaking)
    2. code-spliting 代码分割技术,将代码分割成多份进行懒加载或者异步加载,避免打包成一份后导致体积过大而影响页面的首屏加载速度。

Tree Shaking 原理

打包的时候会去除一些无用的代码。

原理: ES6的模块引入是静态分析的,所以在编译时能够正确判断到底加载了哪些模块。编译时分析程序流,判断哪些变量未被使用、引用,进而删除此代码。

ES6的import语法可以使用tree shaking,CommonJS的动态特性模块意味着tree shaking 不合适。

Webpack 动态导入原理

动态导入可以实现按需加载我们的代码,并使用 promise式的回调,获取加载的包。在代码中所有被import()的模块,都将打包成一个单独的包,放在 chunk存储的目录下。在浏览器运行到这行代码时,就会自动请求资源,从而实现了异步加载的效果。

为什么需要把源文件进行合并

将源文件合并到一个单独的bundle.js的原因是,浏览器不能像Node.js能够快速加载本地的一个个模块,而需要通过网络请求去加载还没有得到的文件。如果文件数量很多,会加载很长时间,所以合并到一个文件后,只需要加载一次就能达到目的。

bundle.js 能够直接在浏览器运行的原因是,在输出的文件中通过 _webpack_require_ 函数,定义了一个可以在浏览器中执行的加载函数模拟nodejs中的require语句。

标签: