【前端面试】HTML篇
【前端面试】CSS篇
【前端面试】JS篇
【前端面试】Vue篇
【前端面试】Git篇
【前端面试】前端性能优化篇
【前端面试】手写题篇
【前端面试】浏览器&网络篇
【前端面试】前端工程化篇
Webpack 核心概念
webpack 的作用
- 模块化打包:将前端项目的 JS、CSS、图片等各种资源视作模块,统一打包成浏览器可用的 bundle。
- 依赖管理:解析模块间依赖关系,按需加载,支持代码拆分(Code Splitting)和按模块更新。
- 优化与扩展:提供 Loader/Plugin 机制,对代码做转译、压缩、Tree Shaking、热更新等优化。
bundle、chunk、module 的区别
- module:最小构建单元,可以是 JS/TS/CSS 文件或组件,webpack 会递归解析依赖形成模块图。
- chunk:模块集合,用于按需加载,可能包含多个模块,支持同步或异步分块。
- bundle:最终输出文件,浏览器实际加载的文件,通常由一个或多个 chunk 打包而成。
webpack 构建过程(从 entry 到 output)
Webpack 从入口文件出发,解析依赖,应用 Loader 转码,打包成 chunk 并经过 Plugin 优化,最后输出到磁盘。
- 初始化 :读取配置文件,确定
entry(入口文件)、output(输出配置)、Loader、Plugin 等。 - 编译 :从
entry出发,递归解析模块依赖,并对模块应用 Loader 转换代码。 - 打包:将所有模块整合成一个或多个 chunk,并应用 Plugin 做优化(如压缩、Tree Shaking、Code Splitting)。
- 输出 :将最终生成的 bundle 文件写入磁盘(
output.path),完成构建。
Loader 和 Plugin 的区别
Loader 负责转换模块内容 ,Plugin 负责扩展构建功能和优化流程。
- 作用范围
- Loader :用来处理模块 ,把文件从一种格式转换为 JS 模块,比如
babel-loader。 - Plugin :用来扩展 webpack 功能,影响整个构建过程,比如生成 HTML、压缩代码、清理目录。
- Loader :用来处理模块 ,把文件从一种格式转换为 JS 模块,比如
- 触发阶段
- Loader:在模块加载和解析阶段起作用。
- Plugin:在构建流程的各个阶段都可以挂钩(hook),更灵活。
常见 Loader 和 Plugin
常见 Loader
babel-loader:JS/TS 转码,支持最新语法向下兼容。css-loader:处理 CSS 引入,支持模块化 CSS。style-loader:把 CSS 注入到页面<style>标签中。sass-loader/less-loader:处理 Sass 或 Less 预处理语言。file-loader/url-loader/asset模块:处理图片、字体等资源。ts-loader:TypeScript 转 JS。
常见 Plugin
HtmlWebpackPlugin:生成 HTML 并自动引入 bundle。CleanWebpackPlugin:打包前清理输出目录。MiniCssExtractPlugin:提取 CSS 为单独文件。DefinePlugin:定义全局常量(如环境变量)。HotModuleReplacementPlugin:支持 HMR 热更新。CopyWebpackPlugin:复制静态资源到输出目录。
热更新(HMR)的实现原理
Webpack HMR 通过模块依赖图,只替换变更的模块,利用 runtime 处理更新,避免整页刷新,实现高效开发。
- 核心概念:只替换发生变化的模块,不刷新整个页面,提高开发效率。
- 工作流程 :Webpack 编译变化模块生成补丁 → HMR runtime 接收 → 调用模块的
accepthandler 更新模块。 - 关键依赖:模块依赖图决定哪些模块需要更新,框架(React/Vue)提供热更新 API 支持。
externals / alias 配置的作用
externals 用于排除不打包的依赖,alias 用于路径别名简化导入。
externals
- 告诉 Webpack 某些依赖不需要打包,通常由外部 CDN 或全局变量提供,避免重复打包。
alias
- 为模块路径设置别名,简化导入路径,减少相对路径复杂度,提高项目可维护性。
性能优化 & 构建优化
Tree Shaking 原理、优缺点
Tree Shaking 通过静态分析 ES Module 去掉未用代码,减小体积,但对 CommonJS 或有副作用模块要注意。
- 概念:去除未使用代码,减小打包体积。
- 原理:基于 ES6 静态模块分析,通过 Webpack 标记未使用代码,再由 Terser 删除未用代码,减小 bundle 体积。
- 优点:减小 bundle,提升加载性能,和 Code Splitting 配合效果更好。
- 缺点 :只支持 ES Module;有副作用的模块需在
package.json标注"sideEffects": false;动态引用不一定能分析。
Code Splitting
Code Splitting 就是把代码拆成多个可按需加载的 chunk,优化加载性能和首屏速度。
- 概念
- 将应用代码拆分成多个 chunk,按需加载,而不是一次性打包成一个大文件。
- 目的
- 减少初始加载体积,提高页面首屏加载速度。
- 支持按需加载和懒加载,提高性能和用户体验。
- 实现方式
- Entry Points:多个入口文件生成不同 bundle。
- 动态 import :使用
import()动态加载模块。 - SplitChunksPlugin:Webpack 内置插件,提取公共模块。
Source Map 的概念与生成方式
Source Map 是压缩/打包后代码和源代码的映射表,方便调试和追踪错误,通过 Webpack 的 devtool 配置生成。
- 概念:把压缩或打包后的代码映射回源代码,方便调试。
- 作用:浏览器调试时能看到原始代码,生产报错也能追踪到源代码位置。
- 生成方式 :Webpack 通过
devtool配置生成.map文件,开发和生产模式可选不同类型(如eval、source-map)。
提高打包速度的策略
- 缓存 :缓存 loader 或模块结果 ,减少重复编译,Webpack 5 提供 persistent caching,下次构建只重新编译变动模块。
- DLL / 预构建依赖:第三方库打包成独立 bundle,减少重复编译。
- 多线程/进程(
thread-loader/HappyPack) :对babel-loader、ts-loader等耗时 loader 使用多线程。 - 减少无用 loader/plugin:精简不必要操作。
- 按需编译 / Code Splitting:只编译变动模块或懒加载。
- 优化模块解析 :合理配置
resolve.extensions和alias,减少查找耗时。
减少打包体积
- Tree Shaking:移除未使用的 ES Module 导出。
- Code Splitting:拆分成多个 chunk,实现按需加载。
- 压缩 :
TerserPlugin压缩 JS,css-minimizer-webpack-plugin压缩 CSS。 - 排除不必要依赖 :如通过
externals排除 CDN 加载的库。 - 按需引入:避免全量导入第三方库(如 lodash)。
Webpack 对比/升级
Webpack 5 对比 Webpack 4 优化点
Webpack 5 相比 4,提升构建速度(持久化缓存)、优化 bundle 体积(Tree Shaking、Scope Hoisting)、支持微前端(模块联邦)、改进输出管理,并增强现代技术兼容性。
- 持久化缓存:构建速度大幅提升,下次构建只重新编译变动模块。
- 模块联邦:支持微前端模块按需共享和加载,降低重复代码和版本冲突。
- 自动 Tree Shaking:默认启用 Tree Shaking,优化 bundle 体积和运行性能。
- 默认支持 WebAssembly、ES Module:更好地支持现代前端技术栈。
- 改进的输出与优化 :内置优化:自动生成 runtime chunk、优化
splitChunks、内置asset模块管理(无需file-loader/url-loader)。 - 更严格的依赖管理:Module resolution 更严格,减少潜在冲突和错误。
- 移除/替换部分过时插件 :移除
UglifyJsPlugin、CommonsChunkPlugin,使用内置TerserPlugin、SplitChunksPlugin。
hash、chunkhash、contenthash 的区别
hash 是整个构建唯一标识,chunkhash 根据 chunk 内容生成,contenthash 根据文件内容生成,越精细越利于缓存优化。
hash- 作用:整个编译过程的唯一标识,每次构建都会改变。
- 特点:只要项目重新打包,不论文件是否改动,hash 都会变化。
- 用途:适合整个项目打包的版本号,但不适合缓存优化。
chunkhash- 作用 :根据 chunk 内容 生成 hash。
- 特点:同一个 chunk 内容不变时 hash 不变,不同 chunk hash 不同。
- 用途:用于区分不同入口或代码分块的 bundle,配合缓存优化。
contenthash- 作用 :根据 文件内容 生成 hash(通常用于 CSS、JS 文件)。
- 特点:只有文件内容改变时 hash 才变,最精细化的缓存控制。
- 用途:配合长期缓存(长期 CDN 缓存)最有效。
前端新工具对比
Webpack vs Vite 区别
- 构建方式:Webpack 会先打包所有模块,Vite 开发模式下按需加载 ES Module。
- 启动和热更新:Webpack 项目大时启动慢,热更新基于 bundle;Vite 启动快,热更新即时。
- 生产构建:Webpack 和开发模式一致打包,Vite 生产模式用 Rollup,体积小、性能高。
- 生态与适用场景:Webpack 插件丰富,适合复杂工程;Vite 更适合现代前端快速开发。
Vite 如何处理 CommonJS
Vite 开发模式下用 esbuild 把 CommonJS 转成 ESM,生产模式用 Rollup + CommonJS 插件打包,保证浏览器可以按模块加载。
esbuild 与 webpack 的区别
- 定位不同 :
- Webpack 是完整打包工具,负责模块解析、打包、优化、Loader/Plugin 扩展。
- esbuild 是高性能构建工具,核心是 超快的 JS/TS 编译和打包,功能比 Webpack 简单。
- 性能差异 :
- Webpack 用 JS 实现,构建大项目较慢。
- esbuild 用 Go 语言实现,速度非常快,尤其是依赖预构建和转码。
- 生态与扩展 :
- Webpack 插件和 Loader 丰富,适合复杂工程化需求。
- esbuild 插件生态简单,但可以用于 Vite 等工具的高速构建。
Vite 的依赖预构建(pre-bundling)机制
Vite 的依赖预构建是用 esbuild 将第三方依赖转换为 ESM 并缓存,提升冷启动和热更新速度,同时兼容 CommonJS 模块。
- 概念
- Vite 会在开发环境启动时 预先构建依赖(尤其是 npm 包),生成优化后的 ESM 模块,以提升页面热更新和模块加载速度。
- 原理
- 使用 esbuild 将依赖库从 CommonJS/UMD 等格式转换为 ESM。
- 构建后的依赖会被缓存,下次启动或修改依赖时无需重复构建。
- 作用
- 加快冷启动 :浏览器只加载已优化的 ESM 依赖,无需动态解析整个
node_modules。 - 提高 HMR 效率:依赖模块已经是 ESM,模块热更新更快。
- 兼容 CommonJS:通过预构建将 CommonJS 转为 ESM,解决浏览器直接加载问题。
- 加快冷启动 :浏览器只加载已优化的 ESM 依赖,无需动态解析整个
esbuild 的 transform 与 bundle 原理
transform 是单文件语法转换,不处理依赖,bundle 是依赖分析后打包成最终文件,二者都利用 esbuild 高速编译器实现快速处理。
transform- 概念:对单个文件进行语法转换,如 TypeScript → JavaScript、ESNext → ES5。
- 原理 :使用 高速的 Go 语言编译器解析文件成 AST,然后转换语法并生成新的 JS 代码,不处理依赖。
- 特点:速度极快,适合处理单文件转换或 loader 场景。
bundle- 概念:将多个模块及其依赖打包成一个或多个文件。
- 原理:esbuild 会从入口文件出发,递归解析依赖、构建模块图(module graph),然后将模块组合成一个 bundle,支持代码分割和 tree shaking。
- 特点:兼顾转换和打包,输出最终可直接运行的文件。
工程化与工具链
Babel 的概念与原理
Babel 是把现代 JS/JSX/TypeScript 转换为向后兼容的代码的编译工具,通过解析 AST → 转换 AST → 生成代码实现语法兼容和扩展功能。
- 概念
- Babel 是一个 JS 编译工具 ,用于将 ES6+ 或未来标准语法 转换为 向后兼容的 ES5 代码,让旧版浏览器也能运行现代 JS。
- 同时可以处理 JSX、TypeScript 等扩展语法。
- 原理
Babel 的核心流程分为三步:- 解析(Parse):将源代码转成 AST(抽象语法树)。
- 转换(Transform):对 AST 进行修改,如把箭头函数、类、模板字符串等转换为兼容写法。
- 生成(Generate):把转换后的 AST 生成新的 JS 代码。
- 作用
- 兼容性:支持旧浏览器运行现代 JS。
- 语法扩展:支持 JSX、TypeScript、实验性语法等。
- 与构建工具集成:通常结合 Webpack、Rollup、Vite 使用,提高前端开发效率。
CSS 工程化理解
CSS 工程化就是用规范、模块化和自动化管理样式,提高可维护性、复用性,减少冲突,并结合构建工具和组件化实现高效开发。
- 概念
- CSS 工程化是指通过 规范、模块化和自动化 管理 CSS,使大型项目的样式可维护、可复用、可扩展。
- 目的
- 提高 可维护性:样式结构清晰、命名规范。
- 减少 冲突:通过模块化或命名空间避免样式污染。
- 提升 复用性:组件级样式可在不同项目或模块复用。
- 提高 自动化和效率:使用构建工具自动处理前缀、压缩、合并等。
- 实践方式
- CSS 预处理器:Sass、Less,支持变量、嵌套、Mixin。
- CSS 模块化:CSS Modules、BEM 命名规范、原子化 CSS(Tailwind)等。
- 构建工具集成:PostCSS、Autoprefixer、cssnano,自动加前缀和压缩。
- 组件化管理:与前端组件框架(React/Vue)结合,样式随组件封装。
CSS Module / CSS in JS / PostCSS / Tailwind 等的优劣
| 技术 | 优点 | 缺点 / 注意事项 |
|---|---|---|
| CSS Modules | - 样式局部化,避免全局冲突 - 与组件绑定,复用性高 | - 对全局样式支持有限 - 配置依赖构建工具(Webpack/Vite) |
| CSS-in-JS (如 styled-components) | - 样式与组件紧密绑定,动态样式灵活 - 支持 JS 逻辑控制样式 | - 运行时性能略差,增加 bundle 体积 - 学习成本高,调试相对复杂 |
| PostCSS | - 自动加前缀(Autoprefixer)、压缩、优化 - 可扩展插件生态丰富 | - 主要是样式处理工具,不解决模块化问题 - 需要构建工具支持 |
| Tailwind CSS | - 原子类快速开发,提高开发效率 - 易于维护大型项目样式 | - 类名过多可读性差 - 需要设计规范和团队约定 |
Monorepo 理解
Monorepo 把多个项目或包放在同一仓库,便于共享代码和统一管理,但仓库大、版本管理复杂,需要合适的工具链支持。
- 概念
- Monorepo 是指 多个项目或包维护在同一个代码仓库,而不是为每个项目单独建仓。
- 每个项目可以是独立应用,也可以是共享的库/模块。
- 优点
- 统一管理:依赖、版本、CI/CD 配置统一管理,减少重复配置。
- 共享代码:库或工具模块可以在不同项目间直接复用。
- 跨项目改动可见:修改共享模块时,仓库内其他项目可以即时测试兼容性。
- 缺点 / 挑战
- 仓库体积大,可能影响 git 操作和 CI/CD 构建速度。
- 版本控制复杂,不同包可能有不同依赖版本。
- 工具链要求高 ,需要
pnpm workspace、Lerna、Yarn Workspaces等配合管理。
- 常用工具
pnpm workspace/Yarn Workspaces/Lerna,管理依赖和子包构建。
- 适用场景
- 大型前端或全栈项目,有多个应用或共享组件库。
- 团队希望统一版本管理和构建流程。
pnpm 比 npm 快的原因
pnpm 通过全局存储+硬链接、严格依赖树、并行下载和缓存机制,避免重复安装和下载,速度比 npm 快很多。
- 独特的存储方式
- pnpm 使用 全局内容寻址存储 ,不同项目共享依赖的硬链接,而不是每个项目都复制一份
node_modules。 - 节省磁盘空间,也减少安装重复依赖的时间。
- pnpm 使用 全局内容寻址存储 ,不同项目共享依赖的硬链接,而不是每个项目都复制一份
- 严格的依赖树
- pnpm 构建的是 非扁平化、严格的依赖树,避免重复安装依赖,提升解析速度。
- 并行安装
- pnpm 会 最大化并行下载依赖,提高安装速度。
- 增量安装 / 缓存
- 已下载过的包会被缓存,下次安装时直接复用,不用重复下载。
整理不易,有回答错误之处欢迎在评论区指正交流~