前端工程化面试题
- webpack有哪些常见的loader? 你用过哪些loader?
- webpack 有哪些常见的Plugin? 你用过哪些Plugin?
- 说说Loader 和Plugin 的区别
- 作用上
- 结构上
- webpack 构建流程简单说一下
- 使用webpack开发时,使用过哪些可以提高效率的插件?
- 如何优化webpack 的构建速度?
- 文件指纹是什么?怎么用?
- js的文件指纹设置。
- css 文件指纹设置
- 图片相关文件指纹设置
- 是否写过loader?简单描写一下编写loader 的思路?
- 是否写过Plugin?简单描写一下编写Plugin的思路?
- 说说Babel 原理
- source map 是什么?生产环境怎么用?
- 文件监听原理
- webpack 热更新原理
- webpack 事件机制了解吗?
- webpack5 相比于 webpack4 有哪些提升?
- 对模块联班的理解
- Webpack 中有哪些核心概念?
- webpack 的mode是什么?
- 什么是代码分割(code Splitting),如何在webpack中实现?
webpack有哪些常见的loader? 你用过哪些loader?
- raw-loader: 加载文件原始内容
- file-loader: 把文件输出到文件夹中,在代码中通过相对url
去引用输出的文件夹(包含图片、字体) - url-loader:和file-loader类似,区别是用户可以设置一个阈值。 大于阈值会交给file-loader 处理,小于阈值则返回base64编码(一般用来处理图片)
- source-map-loader:加载额外的source-map文件,方便断点调试
- image-loader:加载并压缩图片文件
- json-loader:加载json文件
- babel-loader:将es6 装换成es5
- ts-loader:将ts 语音转换成 js
- sass-loader、less-loader:都是转换成css
- css-loader:加载css
- style-loader: 把css注入到js中,通过dom操作加载css
- postcss-loader:扩展css语法,使用下一代css
- eslint-loader、tslint-loader:代码检查。
- vue-loader:加载vue.js单文件组件。
webpack 有哪些常见的Plugin? 你用过哪些Plugin?
- define-plugin:定义环境变量,
- ignore-plugin:忽略部分文件
- html-webpack-plugin:简化html文件创建
- uglifyjs-webpack-plugin:压缩js
- mini-css-extract-plugin:分离样式文件,css提取为单独的文件,支持按需加载
- clean-webpack-plugin:目录清理(用在下次打包之前把上次打包的内容清理掉)
- webpack-bundle-analyzer:可视化webpack输出文件的体积。用于分析当前项目依赖文件的体积大小。
- speed-measure-webpack-plugin:分析每个loader 和 plugin 执行耗时
说说Loader 和Plugin 的区别
作用上
- loader 本质是一个函数,对接收到的内容进行转换,返回转换后的结果,webpack只认识js,所以loader就相当于翻译官。对其他类型资源进行转译和预处理。
- plugin:是插件,它基于事件流框架 Tabable,可以扩展webpack功能,在webpack运行的生命周期中会广播很多事件。plugin可以监听这些事件,在适合的时机通过webpackapi 改变输出的结果。
结构上
- loader :在module.rules 中进行配置,类型是数组,每项都是对象,对象包含(test 文件类型,loader 对应的模块加载器,option 对应的参数)
- plugin:每个plugin都需要单独配置,类型是数组,每项plugin是一个plugin实例,参数通过构造函数传入。
webpack 构建流程简单说一下
它的运行流程是一个串行的过程。
1、初始化参数:从配置文件和shell语句中读取并且合并参数,得到最终的参数。
2、开始编译:初始化Compiler 对象,加载所有的配置插件。执行对象run方法,开始编译。
3、确定入口:根据配置的entry找到所有入口
4、翻译模块:调用所有的loader 对模块进行编译,找出模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过处理。
5、完成模块编译:经过loader翻译完所有模块后的最终内容以及它们之间的依赖关系。
6、输出资源:根据入口以及它们依赖关系组装成一个个包含多个模块的chunk,再把每个chunk 转换成单独的文件加入到输出列表中。(这里可以修改输出内容,并且是修改的最后一个机会)
7、输出完成:根据配置确定输出的路径和文件名,把文件内容写到文件中,
在上面流程中,webpack 会在特定的时间点广播特定事件,插件在监听到事件后调用webpack 提供的API 改变输出结果
使用webpack开发时,使用过哪些可以提高效率的插件?
- webpack-dashboard:更友好的展示的展示相关包信息。
- webpack-merge:提取公共配置,可以将多个配置文件合并,减少代码重复。
- speed-measure-webpack-plugin:分析 loader 和 plugin 的耗时,从而分析构建过程中的性能瓶颈。
- size-plugin:监控资源体积变化,尽早发现问题。
- HotModuleReplacementPlugin:模块热替换。
如何优化webpack 的构建速度?
- 使用高版本webpack 和 node.js
- 多进程 / 多实例构建 thread-loader
- 压缩代码 、多进程并行压缩‘
1)webpack-paralle-uglify-plugin :可以并行运行 UglifyJS 插件,从而更加充分、合理的使用 CPU
资源,从而大大减少构建时间 2)terser-webpack-plugin :它使用 Terser 库来执行压缩,Terser
是一个用于压缩 JavaScript 代码的工具,可以对代码进行简单的混淆,以及删除未使用的代码和注释等优化。 3)
mini-css-extract-plugin : 可以避免将 CSS 代码打包到 JavaScript 文件中,减少 JavaScript
的体积,同时也可以使得 CSS 文件可以被浏览器缓存,提高页面加载速度。
- 图片压缩 (imagemin、image-webpack-plugin)
- 缩小打包作用域
1)、通过exclude / include 来确定loader 规则范围。
2)、设置resolve.extensions.
3)、resolve.modules - 提取页面公共的资源
1)、基础包分离
2)、使用html-webpack-externals-plugin 基础包通过cdn引入,不打入bundle 中。
3)、使用splitchunksplugin 将代码拆分成多个块,以便在不同的环境中按需加载。 - 充分利用缓存提升二次构建速度。
1)、使用 babel-loader 开启缓存
2)、terser-webpack-plugin 开启缓存
3)、cache-loader 或者 hard-source-webpack-plugin - tree shaking (没有用过的模块代码进行标记,从最终bundle 中去掉)
文件指纹是什么?怎么用?
文件指纹:打包后输出的文件名后缀。
- hash:和整个项目构建相关的,只有项目文件改变、项目的hash值就会改变。
- chunkHash:和webpack打包的chunk 相关,不同的entry产生不同的chunkHash,
- contentHash:根据文件内容定义hash,文件内容不变contentHash就不会变。
js的文件指纹设置。
主要设置output 的 fllename 使用 chunkhash.
module.exports = {entry: {spp: './src/app.js', search: './src/search.js'},output: {filename: '[name][chunkhash:8].js',path:_dirname + '/dist'}// chunkhash:8 是设置对应的长度
}
css 文件指纹设置
设置MiniCssExtractPlugin
module.exports = {entry: {spp: './src/app.js', search: './src/search.js'},output: {filename: '[name][chunkhash:8].js',path:_dirname + '/dist'},plugin: [new MinCssExtractPlugin({ filename:'[name][contenthash:8].css'}),]
}
图片相关文件指纹设置
设置 file-loader 中的name
const path = require('path');
module.exports = {entry: './src/index.js',output: {filename: 'bundle.js',path: path.resolve(__dirname, 'dist')}, module: {rules: [{test: /\.(png|svg|jpg|gif)$/,use: [{loader: 'file-loader',option: {name: 'img/[name][hash:8].[ext]'},}],}]}
}
是否写过loader?简单描写一下编写loader 的思路?
- loader是支持链式调用的,所有在开发中需要严格遵守单一职责。每个loader负责自己所需要负责的事情。
- loader是运行在nodejs中的,可以调用nodejs api 或者安装第三方模块调用。 传给loader 的原内容是 utf-8
- 编码的字符串 尽可能异步化loader ,如果计算量小的化同步也可以
- loader 是无状态的,不应该在loader 中保留状态
- 开发loader 时可以使用 loader-utils schema-utils 工具 还可以(通过npm link 和
resolveloader 这两种方式)加载本地的loader 方法
是否写过Plugin?简单描写一下编写Plugin的思路?
- webpack运行生命周期中会广播出很多事件,让plugin 监听事件,特定阶段钩入想要添加的自定义插件内容
- webpack通过tapable 事件流机制保证插件有序性,使系统的扩张性很好
compiler 暴露整个生命周期相关钩子
compliation 暴雷与模块和依赖有关的颗粒度更小的事件钩子
plugin 需要再原型上绑定apply方法,才能访问compiler 实例。
插件接收 compiler 、compliation 都是同一个引用。
找到合适的事件点完成想要执行的内容,
说说Babel 原理
在 webpack 中,Babel 主要用于将 ECMAScript 2015+版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其它环境中。Babel 的转化过程如下:
- Parse:使用 babylon 将原始代码转换为抽象语法树。
- Transform:通过 babel-traverse 对前面的抽象语法树进行遍历修改,并获得新的抽象语法树。
- Generator:使用 babel-generator 将抽象语法树转换为代码。
这三个操作通过 babel-core 合成一个对外的 API 供外界使用。
source map 是什么?生产环境怎么用?
Source Map 用于将编译、打包、压缩后的代码映射回源代码。
在生产环境中使用 Source Map 有以下几个步骤:
- 在构建过程中生成 Source Map:确保你的构建工具(如 webpack)配置为生成 Source Map。
- 部署包含 Source Map 的构建产物:将生成的 Source Map 与构建后的代码一起部署到生产环境。
- 在开发工具中启用 Source Map:这样可以在浏览器中查看原始源代码,并在调试时将错误和堆栈跟踪映射回原始代码。
使用 Source Map 的好处包括:
- 更好的调试体验:可以在生产环境中查看和调试原始代码。
- 提高开发效率:更容易找到和解决问题。
然而,需要注意的是,使用 Source Map 会增加构建产物的大小,可能会对性能产生一定影响。因此,在生产环境中应谨慎使用,并在必要时进行优化。
文件监听原理
Webpack 文件监听的原理是通过轮询判断文件的最后编辑时间是否发生变化。Webpack 首先存储文件的修改时间,下次再有修改时会与上次修改时间进行比对。如果发现不一致,Webpack 不会立即告诉监听者,而是将文件修改缓存起来,等待一段时间。在等待期间,如果有其他文件发生变化,Webpack 会将变化列表一起构建,并生成到 bundle 文件夹。
文件监听的目的是在发现文件发生变化时,自动重新构建出新的输出文件。开启监听模式有两种方式,在启动 Webpack 命令时带上 --watch 参数,或在配置 webpack.config.js 中设置 watch: true。构建完成后,输出的文件会被放入磁盘中。
webpack 热更新原理
Webpack 的热更新(Hot Module Replacement,简称 HMR)是一种在开发时提供实时更新的功能,它使得在修改代码后,不需要完全刷新页面就能立即看到更新的效果。HMR 的原理涉及以下几个主要步骤:
-
启动时建立 WebSocket 连接:在项目启动时,Webpack 会创建与开发服务器的 WebSocket 连接,用于在构建完成后接收更新的模块。
-
构建编译阶段:Webpack 在编译时会在每个模块中注入 HMR 运行时代码。该代码会监听源代码的变化,并通知 HMR runtime 进行处理。
-
文件监控:Webpack 会监控所有入口文件及其依赖的文件,一旦检测到文件发生变化,会触发重新编译。
-
构建完成:当编译完成后,Webpack 会将编译结果发送给开发服务器,并通过 WebSocket 通知客户端有新的资源可用。
webpack 事件机制了解吗?
常见事件
- before-run:开始执行构建之前触发,可以用于清理上一次构建的临时文件或状态
- run:开始执行构建触发
- before-compile:开始编译代码前触发,可以用于添加一些额外的编辑配置或者预处理代码
- compile:开始编译,可用于监听编译过程或编译处理错误
- this-compilation:创建新的 Compilation 对象触发,Compilation 对象代表当前编辑过程中的所有状态和信息。
- compilataion:编辑代码期间触发,可用于监听编译过程或编译处理错误
- emit:输出文件之前触发,修改输出文件或者生成一些附件文件。
- after-emit:输出文件后触发,用于清理中间文件
- done:构建完成时触发,用于生成构建报告。
事件机制
Webpack采用事件驱动的方式打包web应用,其事件机制主要依靠一个很小的核心库tapable来实现。封装了事件订阅发布机制。compile、compilataion 都是tapable类的实例对象,
webpack5 相比于 webpack4 有哪些提升?
- 更快的构建速度,尤其是开发模式下构建速度明显的提升
- Tree Shaking:优化算法。更准确哪些代码无用,更好的优化构建输出的文件大小
- 内置的持久化缓存:可以缓存每个模块编译结果,加速后续构建,
- 支持 WebAssmbly
- 模块联班:Module federation 解决模块共享、远程加载。为微前端架构提供支持。
对模块联班的理解
模块联邦(Module Federation)是 Webpack 5 推出的一个新特性。它是一种模块共用机制,允许本地调用远程的模块。通过模块联邦可以解决微前端依赖问题,对比 npm 库具有实时性的优势。
Webpack 中有哪些核心概念?
Webpack 中有以下几个核心概念:
- 模块:Webpack 会将各种资源(如 JavaScript、CSS、图像等)视为模块。
- 入口:指定 Webpack 开始构建的起点。
- 输出:定义了最终生成的文件。
- 加载器:用于处理特定类型的文件。
- 插件:扩展 Webpack 的功能。
- 依赖关系:模块之间的依赖关系。
- 模块解析:确定如何查找和加载模块。
- 代码分割:将代码分割成多个块,按需加载。
- 优化:例如压缩、合并等,提高性能。
- 热更新:在运行时自动更新变更的模块。
webpack 的mode是什么?
Webpack 的 mode 提供了 mode 配置选项,告知 Webpack 使用相应模式的内置优化。mode 的值可以是 development、production 或 none,分别代表开发模式、生产模式和无预设模式(需要从头开始配置)。如果 mode 没有被设置,系统会默认使用 production 模式。
- development(开发模式):提供了一些有助于开发和调试的特性,更详细的错误信息和警告。实时重新加载。 未压缩的代码以提高调试效率。
- production(生产模式):用于准备上线的构建,强调优化和性能:代码压缩和混淆。 资源优化。
- none (无预设模式) 关闭任何默认优化选项,只执行基本打包功能,不做任何额外处理。
什么是代码分割(code Splitting),如何在webpack中实现?
代码分割是将一个应用程序的代码拆分成多个独立的块,以便可以按需加载。可以减少初始加载时间提高页面性能,在 Webpack 中配置实现代码分割,有以下多种方式:
1、入口起点配置:配置多个入口起点,不同部分打包成独立的代码块。
2、动态导入 (Dynamic import):可以将模块作为单独的代码块按需加载,这样模块需要时才会下载执行,
3、使用 SplitChunksPlugin 插件来自动分割代码。自动拆分公共模块,并创建独立代码块,避免重启加载,提高缓存利用率。
4、使用 CommonsChunkPlugin 插件来提取公共模块。