31.说一说 webpack 的构建流程是什么?
⚫ 初始化流程:
◼ 从配置文件和 Shell 语句中读取与合并参数
◼ 初始化需要使用的插件和配置插件等执行环境所需要的参数
⚫ 编译构建流程:
◼ 从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容
◼ 再找到该 Module 依赖的 Module,递归地进行编译处理
⚫ 输出流程:
◼ 对编译后的 Module 组合成 Chunk,
◼ 把 Chunk 转换成文件,输出到文件系统
32.如何优化 webpack 打包速度
⚫ 并行编译:
◼ thread-loader: 利用多个 CPU 内核并行运行加载器。
◼ HappyPack : 将模块转换任务分解到多个 worker 进程中并行处理。
⚫ 代码拆分:
◼ splitChunksPlugin 代码分割,将公共代码、第三方库等抽离到单独的 chunk,减
少重复编译的工作量。
⚫ 动态导入(import()):
◼ 根据路由或其他条件按需加载代码,避免一次性打包所有代码。
⚫ Tree Shaking:
◼ 确保你的 ES 模块使用的是静态导入导出, Tree Shaking 移除未使用的代码。
⚫ DllPlugin/DllReferencePlugin:
◼ 不经常变动的依赖(如 React, Vue 库)提前打包成 DLL(动态链接库) ,避免每次
构建都重新打包这些库。
⚫ 减少 Loader 和 Plugin 的数量:
◼ 只使用必要的 loader 和 plugin,每个额外的处理都会增加构建时间。
⚫ 提升 Loader 性能:
◼ Babel 等慢速 loader,使用针对性的配置优化,比如@babel/preset-env 的
useBuiltIns 和 targets 选项,减少不必要的 polyfill。
⚫ Excluding node_modules:
◼ 不需要转译的 node_modules 模块,可以在.babelrc 或 webpack 配置中排除它们。
⚫ Cache:
◼ 利用 Webpack 的持久化缓存特性,如 cacheDirectory 选项,或使用 hard-source
webpack-plugin 等第三方插件来缓存编译结果。
⚫ 优化 Resolving 配置:
◼ 减少模块解析的搜索范围,通过 resolve.modules, resolve.alias 等配置项提高模块 查找速度。
⚫ Source Map 优化:
◼ 开发环境使用 cheap-module-eval-source-map 或更快的 source-map 选项,生产
环境考虑是否需要 source map 或使用更简洁的格式。
⚫ 升级 Webpack 和其他依赖:
◼ 保持 Webpack 及其相关 loader、plugin 的版本是最新的,新版本往往带来性能改
进。
⚫ 分析和监控:
◼ 使用 webpack-bundle-analyzer 分析包大小,找出可以进一步优化的地方。
33.说说 webpack 中常见的 Loader?解决了什么问题?
⚫ loader 对模块的"源代码"进行转换,
⚫ 在 import 或"加载"模块时预处理文件
⚫ webpack 分析各种模块依赖关系,然后形成资源列表,最终打包生成到指定的文件中。
⚫ babel-loader :用 babel 来转换 ES6 文件到 ES5
⚫ html-minify-loader: 压缩 HTML
⚫ style-loader: 将 css 添加到 DOM 的内联样式标签 style 里
⚫ css-loader : 允许将 css 文件通过 require 的方式引入,并返回 css 代码
⚫ less-loader: 处理 less → css
⚫ sass-loader: 处理 sass → css
⚫ postcss-loader: 用 postcss 来处理 CSS
⚫ autoprefixer-loader: 处理 CSS3 属性前缀,已被弃用,建议直接使用 postcss
⚫ file-loader: 分发文件到 output 目录并返回相对路径
⚫ url-loader: 和 file-loader 类似,但是当文件小于设定的 limit 时可以返回一个 Data Url
34.说说 webpack 中常见的 Plugin?解决了什么问题?
⚫ Plugin(Plug-in)是一种计算机应用程序,它和主应用程序互相交互,以提供特定的功
能
⚫ 是一种遵循一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统下,
因为其需要调用原纯净系统提供的函数库或者数据
⚫ webpack 中的 plugin 也是如此,plugin 赋予其各种灵活的功能,例如打包优化、资源
管理、环境变量注入等,它们会运行在 webpack 的不同阶段(钩子 / 生命周期),贯穿
了 webpack 整个编译周期
⚫ HtmlWebpackPlugin
◼ 在打包结束后,自动生成一个 html 文文件,
◼ 并把打包生成的 js 模块引入到该 html 中
⚫ clean-webpack-plugin 删除(清理)构建目录
⚫ mini-css-extract-plugin 提取 CSS 到一个单独的文件中
⚫ DefinePlugin 允许在编译时创建配置的全局对象,是一个 webpack 内置的插件,不需
要安装
⚫ copy-webpack-plugin
◼ 复制文件或目录到执行区域,
◼ 如 vue 的打包过程中,如果我们将一些文件放到 public 的目录下,那么这个目录
会被复制到 dist 文件夹中
35.请详细说说 webpack 中的 plugin 和 loader 之间的区别是什么?
⚫ loader
◼ 是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩
等,最终一起打包到指定的文件中
⚫ plugin
赋予了 webpack 各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader 无法实现的其他事
36.说说你对 promise 的了解
Promise 承诺 ,异步编程的一种解决方案,比传统解决方案(回调函数)更合理和更加强大
回调函数 形成了经典的回调地狱
promise 解决异步操作的优点:
⚫ 链式操作减低了编码难度
⚫ 代码可读性明显增强
⚫ 状态
promise 对象仅有三种状态
⚫ pending(进行中)
⚫ fulfilled(已成功)
⚫ rejected(已失败)
对象的状态不受外界影响,只有异步操作的结果,可以决定当前状态
用法
⚫ Promise 对象是一个构造函数,用来生成 Promise 实例
const promise = new Promise(function(resolve, reject) {});
⚫ Promise 构造函数接受一个函数作为参数,函数的两个参数分别是 resolve 和 reject
⚫ resolve 函数: 将 Promise 对象的状态从“未完成”变为“成功”
⚫ reject 函数: 将 Promise 对象的状态从“未完成”变为“失败”
⚫ 实例方法 then() catch() finally()
⚫ then()
◼ 实例状态发生改变时的回调函数
◆ 第一个参数是 resolved 状态的回调函数,
◆ 第二个参数是 rejected 状态的回调函数
◼ then 方法返回的是一个新的 Promise 实例,也就是 promise 能链式书写的原因
⚫ catch()
.then(null, rejection)或.then(undefined, rejection) 的别名,
◼ 用于指定发生错误时的回调函数
◼ Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止
◼ 一般来说,使用 catch 方法代替 then()第二个参数
⚫ finally()
◼ 用于指定不管 Promise 对象最后状态如何,都会执行的操作
构造函数方法 all() race() allSettled() resolve() reject()
⚫ all()
◼ Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.all([p1, p2, p3]);
◼ 接受一个数组作为参数,数组成员都为 Promise 实例
◼ 实例 p 的状态由 p1、p2、p3 决定,分为两种: ◆ 只有 p1、p2、p3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、
p2、p3 的返回值组成一个数组,传递给 p 的回调函数
◆ 只要 p1、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected,此时第
一个被 reject 的实例的返回值,会传递给 p 的回调函数
◼ 注意,如果作为参数的 Promise 实例,自己定义了 catch 方法,那么它一旦被
rejected,并不会触发 Promise.all()的 catch 方法
⚫ race()
◼ Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.race([p1, p2, p3]);
◼ 只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变
◼ 率先改变的 Promise 实例的返回值则传递给 p 的回调函数
⚫ allSettled()
◼ Promise.allSettled()方法接受一组 Promise 实例作参数,包装一个新 Promise 实例
◼ 只有等到所有这些参数实例都返回结果,不管是 fulfilled 还是 rejected,包装实例
才会结束
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();
⚫ resolve()
◼ 将现有对象转为 Promise 对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
◼ 参数可以分成四种情况:
◆ 参数是一个 Promise 实例,promise.resolve 将不做任何修改、原封不动地返
回这个实例
◆ 参数是一个thenable对象,promise.resolve会将这个对象转为 Promise对象,
然后就立即执行 thenable 对象的 then()方法
◆ 参数不是具有 then()方法的对象,或根本就不是对象,Promise.resolve()会返
回一个新的 Promise 对象,状态为 resolved
◆ 没有参数时,直接返回一个 resolved 状态的 Promise 对象
⚫ reject()
◼ Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为
rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
使用场景
⚫ 将图片的加载写成一个 Promise,一旦加载完成,Promise 的状态就发生变化 ⚫ 通过链式操作,将多个渲染数据分别给个 then,让其各司其职。或当下个异步请求依
赖上个请求结果的时候,我们也能够通过链式操作友好解决问题
⚫ 通过 all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个 loading 即可
⚫ 通过 race 可以设置图片请求超时