webpack面试问题
一、核心概念
-
Webpack的构建流程是什么?
- 答案:
- 初始化:读取配置,创建Compiler对象
- 编译:从入口文件开始,递归分析依赖关系,生成依赖图
- 模块处理:调用Loader转换模块(如babel-loader)
- 输出:将处理后的模块组合成Chunk,生成最终文件
- 答案:
-
Loader和Plugin的区别?
- Loader:文件加载器(转换非JS文件,如
.less
→.css
) - Plugin:扩展Webpack功能(如生成HTML文件、压缩代码)
- 关键区别:Loader处理单个文件,Plugin作用于整个构建流程
- Loader:文件加载器(转换非JS文件,如
二、配置与优化
-
如何实现代码分割(Code Splitting)?
- 答案:
optimization: {splitChunks: {chunks: 'all', // 分离node_modules和公共模块cacheGroups: {vendor: {test: /[\\/]node_modules[\\/]/,name: 'vendors'}}} }
- 答案:
-
Tree Shaking生效的条件?
- 必要条件:
- 使用ES6模块语法(
import/export
) - 生产模式(
mode: 'production'
) - 在
package.json
中配置"sideEffects": false
- 使用ES6模块语法(
- 必要条件:
三、性能优化
-
如何优化Webpack构建速度?
- 高频策略:
- 缓存:
babel-loader?cacheDirectory=true
- 多线程:
thread-loader
- 缩小搜索范围:
resolve.modules
指定node_modules
路径 - DLL预构建:对稳定第三方库提前打包
- 缓存:
- 高频策略:
-
如何分析打包体积过大问题?
- 工具:
# 生成分析文件 webpack --profile --json > stats.json # 使用工具可视化 webpack-bundle-analyzer
- 工具:
四、原理与插件开发
-
Webpack的HMR(热更新)原理?
- 流程:
- 文件变动触发Webpack重新编译
- 新模块通过
websocket
推送到浏览器 - 客户端运行时替换旧模块(保留应用状态)
- 流程:
-
如何编写一个自定义Plugin?
- 示例(生成版本文件插件):
class VersionPlugin {apply(compiler) {compiler.hooks.emit.tap('VersionPlugin', compilation => {const version = new Date().toISOString();compilation.assets['version.txt'] = {source: () => version,size: () => version.length};});} }
- 示例(生成版本文件插件):
五、实战场景
-
如何配置多页应用打包?
- 关键点:
entry: {page1: './src/page1.js',page2: './src/page2.js' }, plugins: [new HtmlWebpackPlugin({ template: './page1.html', chunks: ['page1'] }),new HtmlWebpackPlugin({ template: './page2.html', chunks: ['page2'] }) ]
- 关键点:
-
Webpack5对比Webpack4的重大改进?
- 主要升级:
- 模块联邦(Module Federation)
- 持久化缓存(
cache: { type: 'filesystem' }
) - 内置Asset Modules(替代
file-loader
)
- 主要升级:
六、高频代码题
- 手写一个简易Webpack
- 核心实现:
// 1. 解析模块依赖 function createAsset(filename) {const code = fs.readFileSync(filename, 'utf-8');const ast = parser.parse(code, { sourceType: 'module' });const dependencies = [];traverse(ast, {ImportDeclaration: ({ node }) => dependencies.push(node.source.value)});return { filename, dependencies, code }; }// 2. 生成依赖图 function createGraph(entry) {const graph = [createAsset(entry)];for (const asset of graph) {asset.mapping = {};asset.dependencies.forEach(relativePath => {const absolutePath = path.join(path.dirname(asset.filename), relativePath);const childAsset = createAsset(absolutePath);asset.mapping[relativePath] = childAsset.id;graph.push(childAsset);});}return graph; }
- 核心实现: