aurora blog
  • JS

    • 基本汇总
    • Date时间
    • Array方法
    • String 方法
    • Object方法
    • RegExp正则
    • Es6 新特性等
    • Event-Loop
    • Http and Https
    • for in和for of区别
    • Web Worker
    • Promise && async
    • 堆内存 & 栈内存
    • JS设计模式探索
    • npm & yarn
    • Fetch和Axios的区别
    • 学习及面试问题整理
    • URL 输入到页面加载过程
    • 跨域&nginx本地项目代理
  • FE框架

    • react

      • 基本用法
      • react生命周期
      • hooks 16.8版本后
      • Route原理
      • Redux源码解析
      • React16 Fiber
      • React-VirtualDOM
      • React事件委托机制
      • React性能优化
      • 状态管理方案对比
      • React 18新特性
    • vue

      • vue 基本用法
      • vue 生命周期
      • VirtualDOM
      • vue 事件委托
      • vue 架构
      • vue 状态管理
      • 问题等
    • React-Native

      • React-Native 基本用法
    • 微前端

      • 遇到的问题
    • 鸿蒙 harmony

      • harmony 基础
  • 构建工具

    • webpack

      • Webpack配置详解
      • Webpack的使用
      • Babel-polyfill
      • webpack面试题
    • vite

      • vite配置详解
      • vite面试题
    • rollup

      • Rollup配置详解
      • rollup面试题
    • 构建工具对比
  • 性能优化

    • 性能优化策略
    • 缓存策略
    • 性能优化面试题
  • 浏览器

    • 浏览器渲染原理
    • 浏览器缓存机制
    • 浏览器面试题
  • 工程化

    • 代码规范
    • 工程化面试题
  • 前端安全

    • XSS 攻击与防护
    • CSRF 攻击与防护
    • 前端安全面试题
  • 移动端开发

    • 移动端适配
    • 移动端开发面试题
  • 前端监控

    • 错误监控
    • 性能监控
    • 前端监控面试题
  • Typescript

    • Typescript详解
  • Servers

    • Nodejs
    • Nginx
  • Git命令

    • git常用规范
  • 数据库

    • mongodb
    • mongodb
  • Other

    • Jenkins自动化部署

webpack面试题

  • 构建流程是什么?从读取配置到输出文件这个过程
  • 编写loader或plugin的思路
  • webpack的热更新是如何做到的
  • 提高webpack的构建速度
  • 提高webpack的构建速度
  • Loader 和 Plugin 的区别
  • 如何编写一个自定义 Loader
  • 如何编写一个自定义 Plugin
  • webpack 的代码分割原理
  • Tree Shaking 原理
  • webpack 的模块解析机制
  • webpack 的 hash、chunkhash、contenthash 区别
  • webpack 5 相比 webpack 4 的改进
  • webpack 的优化策略
  • webpack 如何处理 CSS
  • webpack 的 Source Map 原理
  • webpack 如何处理图片资源
  • webpack 的 Scope Hoisting 是什么
  • webpack 的模块联邦(Module Federation)
  • webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件
  • 不同的Loader来处理不同的文件,用Plugin来扩展webpack功能

构建流程是什么?从读取配置到输出文件这个过程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
1.初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
2.开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
3.确定入口:根据配置中的 entry 找出所有的入口文件;
4.编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
5.完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
6.输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
7.输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

编写loader或plugin的思路

Loader像一个"翻译官"把读到的源文件内容转义成新的文件内容,并且每个Loader通过链式操作,将源文件一步步翻译成想要的样子。

编写Loader时要遵循单一原则,每个Loader只做一种"转义"工作。
每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。
还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。
此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。相对于Loader而言,Plugin的编写就灵活了许多。
webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

webpack的热更新是如何做到的

solar

首先要知道server端和client端都做了处理工作
1.第一步,在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,
根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
2.第二步是 webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和
 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。
3.第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。
注意,这儿是浏览器刷新,和 HMR 是两个概念。
4.第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 
长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 
消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。
5.webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server
 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,
 也就没有后面那些步骤了。
6.HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime
 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,
 获取到最新的模块代码。这就是上图中 7、8、9 步骤。
7.而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,
检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
8.最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。

提高webpack的构建速度

  • 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
  • 利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
  • 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
  • 提取公共代码。

提高webpack的构建速度

  • 多入口情况下,使用CommonsChunkPlugin来提取公共代码
  • 通过externals配置来提取常用库
  • 利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
  • 使用Happypack 实现多线程加速编译
  • 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
  • 使用Tree-shaking和Scope Hoisting来剔除多余代码

Loader 和 Plugin 的区别

Loader:

  • 本质是一个函数,用于转换模块的源代码
  • 在模块加载时执行,将文件从不同语言转换为 JavaScript
  • 执行顺序:从右到左(或从下到上)
  • 常见:babel-loader、css-loader、file-loader

Plugin:

  • 本质是一个类,可以监听 webpack 生命周期钩子
  • 在 webpack 运行到某个时刻时执行,可以访问整个编译过程
  • 功能更强大,可以修改输出、优化资源等
  • 常见:HtmlWebpackPlugin、MiniCssExtractPlugin、CleanWebpackPlugin

如何编写一个自定义 Loader

// my-loader.js
module.exports = function(source) {
  // source 是源文件内容
  // this 是 loader 的上下文对象
  
  // 同步 loader
  return source.replace('foo', 'bar');
  
  // 异步 loader
  const callback = this.async();
  setTimeout(() => {
    callback(null, source.replace('foo', 'bar'));
  }, 100);
  
  // 使用 this.callback
  this.callback(null, transformedSource, sourceMap);
};

如何编写一个自定义 Plugin

// my-plugin.js
class MyPlugin {
  apply(compiler) {
    compiler.hooks.emit.tap('MyPlugin', (compilation) => {
      // compilation.assets 包含所有输出资源
      for (const filename in compilation.assets) {
        if (filename.endsWith('.js')) {
          const source = compilation.assets[filename].source();
          compilation.assets[filename] = {
            source: () => source.replace(/console\.log/g, ''),
            size: () => source.length,
          };
        }
      }
    });
  }
}

module.exports = MyPlugin;

webpack 的代码分割原理

代码分割方式:

  1. 入口起点:配置多个 entry
  2. 动态导入:使用 import() 语法
  3. SplitChunksPlugin:自动提取公共代码

原理:

  • webpack 会分析模块依赖关系
  • 将公共模块提取到单独的 chunk
  • 通过 __webpack_require__.e() 实现按需加载
  • 使用 JSONP 方式加载异步 chunk

Tree Shaking 原理

原理:

  1. ES6 模块是静态的,可以在编译时分析
  2. webpack 标记未使用的导出
  3. 使用 UglifyJS 或 Terser 删除未使用的代码

条件:

  • 使用 ES6 模块语法(import/export)
  • 配置 optimization.usedExports: true
  • 配置 optimization.sideEffects: false 或在 package.json 中声明
  • 生产模式自动启用

webpack 的模块解析机制

解析顺序:

  1. 绝对路径:直接使用
  2. 相对路径:相对于当前文件
  3. 模块路径:从 node_modules 查找
  4. 别名:使用 resolve.alias 配置

解析过程:

  • 根据 resolve.extensions 尝试添加扩展名
  • 根据 resolve.mainFields 查找 package.json 中的入口
  • 根据 resolve.modules 查找模块目录

webpack 的 hash、chunkhash、contenthash 区别

hash:

  • 与整个项目构建相关
  • 项目任何文件改变,hash 都会改变
  • 所有文件共享同一个 hash

chunkhash:

  • 与 chunk 相关
  • 同一个 chunk 的文件 hash 相同
  • 不同 chunk 的 hash 不同
  • 适合用于 JS 文件

contenthash:

  • 与文件内容相关
  • 只有文件内容改变,hash 才改变
  • 适合用于 CSS 文件(与 JS 分离)

webpack 5 相比 webpack 4 的改进

  1. 持久化缓存:显著提升构建速度
  2. Tree Shaking 增强:支持嵌套模块的 Tree Shaking
  3. 模块联邦(Module Federation):微前端解决方案
  4. 更好的 Tree Shaking:支持 CommonJS 的 Tree Shaking
  5. 资源模块:内置资源模块,无需 loader
  6. Top Level Await:支持顶层 await
  7. 更好的长期缓存:改进的 chunk ID 算法

webpack 的优化策略

开发环境:

  • 使用 cache 启用缓存
  • 使用 devtool: 'eval-cheap-module-source-map'
  • 减少 loader 处理范围(exclude)
  • 使用 DllPlugin 预编译第三方库

生产环境:

  • 代码压缩(TerserPlugin)
  • Tree Shaking
  • 代码分割(SplitChunksPlugin)
  • 使用 CDN(externals)
  • 启用 Gzip 压缩
  • 图片优化(压缩、懒加载)

webpack 如何处理 CSS

处理流程:

  1. css-loader:解析 CSS 文件,处理 @import 和 url()
  2. postcss-loader:使用 PostCSS 处理(autoprefixer 等)
  3. sass-loader/less-loader:编译预处理器
  4. style-loader:将 CSS 注入到 DOM(开发环境)
  5. MiniCssExtractPlugin.loader:提取 CSS 到文件(生产环境)

webpack 的 Source Map 原理

Source Map 作用:

  • 将编译后的代码映射回原始源代码
  • 方便调试和错误定位

常见类型:

  • eval:最快,但只能看到行号
  • source-map:最完整,但最慢
  • cheap-module-source-map:折中方案
  • hidden-source-map:生成但不引用

webpack 如何处理图片资源

处理方式:

  1. file-loader:将图片复制到输出目录,返回 URL
  2. url-loader:小图片转为 base64,大图片使用 file-loader
  3. asset/resource(webpack 5):内置资源模块
  4. image-webpack-loader:压缩图片

webpack 的 Scope Hoisting 是什么

作用:

  • 将所有模块的代码提升到一个作用域内
  • 减少函数声明和闭包
  • 减小代码体积,提升执行效率

原理:

  • 分析模块依赖关系
  • 将模块代码合并到同一个作用域
  • 需要 ES6 模块语法支持

webpack 的模块联邦(Module Federation)

概念:

  • webpack 5 新特性
  • 允许在运行时动态加载远程模块
  • 实现微前端架构

配置:

new ModuleFederationPlugin({
  name: 'host',
  remotes: {
    remote: 'remote@http://localhost:3001/remoteEntry.js',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
最近更新:: 2025/11/20 14:50
Contributors: sountstars, liyulai
Prev
Babel-polyfill