前端面试-构建工具

2021/9/9 Interview

# webpack和babel的原理

webpack原理

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

babel原理

babel的转译过程分为三个阶段:解析(Parsing)、转换(Transforming)和生成(Generating),以ES6转译为ES5为例,babel转译的具体过程如下:

  1. ES6代码输入;
  2. Parsing(解析):Babel解析器(babel-parser)将代码转换成抽象语法树(AST);
  3. Transforming(转换):Babel中的变换插件对AST进行各种操作,比如添加、删除节点或者替换节点。例如常用的babel-traverse对AST树进行遍历;
  4. Generating(生成):Babel生成器(babel-generator)将转换后的AST转换回代码。

# 优化Webpack的构建性能

  • 使用最新版本的Webpack:Webpack的每个新版本都会带来性能改进和新特性,因此建议使用最新版本。

  • 开启缓存:Webpack5内置了持久化缓存功能,能将打包结果缓存到磁盘,下次打包时,若文件无变化,可直接使用缓存结果。

  • 优化Loader配置

    • 减少Loader的使用范围:使用includeexclude选项,限制Loader的处理范围。
    • 缓存Loader的结果:使用cache-loaderbabel-loadercacheDirectory选项,缓存Loader的结果。
  • 使用DllPlugin预编译依赖:使用DllPlugin将不常变动的第三方库(如React、Lodash)预编译成单独的bundle,减少构建时间。

  • 使用HappyPack(webpack5已弃用)或Thread-loader并行处理:使用HappyPackthread-loader将Loader的处理过程并行化,提高构建速度。

  • 使用SplitChunksPlugin优化代码分割:将公共代码提取到单独的Chunk中,减少重复代码。

    optimization: {
      splitChunks: {
        chunks: 'all',
      },
    }
    
    1
    2
    3
    4
    5
  • 使用Tree Shaking移除未使用代码

    optimization: {
      usedExports: true,
    }
    
    1
    2
    3
  • 使用HardSourceWebpackPlugin缓存构建结果,减少重复构建时间。

  • 使用IgnorePlugin忽略不必要的模块,减少构建体积。

  • 使用externals排除外部依赖(如通过CDN引入的库),减少构建体积。

  • 使用Profile分析构建性能,使用--profile参数生成构建性能分析报告,找出性能瓶颈。

  • 使用webpack-bundle-analyzer查看可视化分析打包结果,查看模块体积。

  • 使用speed-measure-webpack-plugin测量每个插件和loader的耗时。

# webpack中loader和plugin的区别

  1. 什么是loader?

    loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中。

  2. 什么是plugin?

    在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。

  3. 区别

    • 对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为A.css,单纯的文件转换过程。
    • plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务。
  4. 常用的plugin和loader loader:

    • file-loader:用于处理文件类型资源,如jpg,png等图片。
    • url-loader:它与file-loader作用相似,也是处理图片的,只不过url-loader可以设置一个根据图片大小进行不同的操作,如果该图片大小大于指定的大小,则将图片进行打包资源,否则将图片转换为base64字符串合并到js文件里。
    • style-loader:通过注入style标签将CSS插入到DOM中。
    • css-loader:仅处理css的各种加载语法(@importurl()函数等)。
    • postcss-loader。
    • less-loader:解析less,转换为css。
    • ts-loader:打包编译Typescript。
    • babel-loader:基于babel,用于解析JavaScript文件。
    • markdown-loader:markdown编译器和解析器。
    • raw-loader:可将文件作为字符串导入。

    plugin:

    • HtmlWebpackPlugin:生成html文件,为html引入外部资源如script、link。
    • copy-webpack-plugin:将已经存在的单个文件或整个目录复制到构建目录。
    • clean-webpack-plugin:删除webpack的output.path中的所有文件。
    • mini-css-extract-plugin:将CSS提取到单独的文件中,为每个包含CSS的JS文件创建一个CSS文件。
    • SplitChunksPlugin:代码分割。
    • webpack.HotModuleReplacementPlugin:热更新插件。
    • webpack.DefinePlugin:创建一个在编译时可以配置的全局常量。
    • webpack-bundle-analyzer:看到项目各模块的大小,可以按需优化。

# webpack热更新原理

简单来说就是:hot-module-replacement-plugin包给webpack-dev-server提供了热更新的能力,它们两者是结合使用的,单独写两个包也是出于功能的解耦来考虑的。

  • webpack-dev-server(WDS)的功能提供bundle server的能力,就是生成的bundle.js文件可以通过localhost://xxx的方式去访问,另外WDS也提供livereload(浏览器的自动刷新)。
  • hot-module-replacement-plugin的作用是提供HMR的runtime,并且将runtime注入到bundle.js代码里面去。一旦磁盘里面的文件修改,那么HMR server会将有修改的js module信息发送给HMR runtime,然后HMR runtime去局部更新页面的代码。因此这种方式可以不用刷新浏览器。

热更新步骤:

  1. 当修改了一个或多个文件;
  2. 文件系统接收更改并通知webpack;
  3. webpack重新编译构建一个或多个模块,并通知HMR服务器进行更新;
  4. HMR Server使用webSocket通知HMR runtime需要更新,HMR运行时通过HTTP请求更新json;
  5. HMR运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新。

# Vite与Webpack区别

Vite和Webpack是两种流行的前端构建工具,它们在设计理念、构建机制和性能优化上有显著的区别。Vite之所以比Webpack快,主要是因为它在开发模式下利用了现代浏览器的原生ES模块(ESM)支持,以及更高效的构建策略。以下是它们的详细对比:

  1. 构建机制

    Webpack

    • 基于打包的构建工具。
    • 在开发模式下,Webpack会将所有模块打包成一个或多个bundle文件(如main.js)。
    • 每次代码更改时,Webpack需要重新构建整个bundle,导致开发服务器的启动和热更新较慢。

    Vite

    • 基于原生ES模块(ESM)的构建工具。
    • 在开发模式下,Vite不会打包所有模块,而是利用浏览器的原生ESM支持,按需加载模块。
    • 每次代码更改时,Vite只需要重新构建改动的模块,因此启动和热更新速度更快。
  2. 开发服务器

    Webpack

    • 开发服务器依赖于打包后的bundle文件。
    • 启动时需要先打包所有模块,导致启动时间较长。
    • 热更新(HMR)需要重新构建整个bundle或部分chunk。

    Vite

    • 开发服务器直接基于原生ESM,按需加载模块。
    • 启动时只需要启动开发服务器,不需要预先打包,因此启动时间极短。
    • 热更新(HMR)只更新改动的模块,速度更快。
  3. 生产构建

    Webpack

    • 在生产模式下,Webpack会将所有模块打包成优化的bundle文件。
    • 支持多种优化策略(如代码分割、Tree Shaking、压缩等)。

    Vite

    • 在生产模式下,Vite使用Rollup进行打包。
    • Rollup的打包机制更轻量,生成的代码更简洁,性能更好。

Vite为什么比Webpack快?

  1. 开发模式下的按需加载

    • Vite利用浏览器的原生ESM支持,按需加载模块。
    • 只有在浏览器请求某个模块时,Vite才会动态编译并返回该模块。
    • 这种方式避免了Webpack在开发模式下需要预先打包所有模块的开销。
  2. 更快的启动速度

    • Vite的启动时间几乎与项目规模无关,因为它不需要预先打包所有模块。
    • Webpack的启动时间随着项目规模的增长而增加,因为它需要预先打包所有模块。
  3. 更快的热更新

    • Vite的热更新只更新改动的模块,而不是整个bundle。
    • Webpack的热更新需要重新构建整个bundle或部分chunk,速度较慢。
  4. 基于Rollup的生产构建

    • Vite在生产模式下使用Rollup进行打包,生成的代码更简洁,性能更好。
    • Webpack虽然功能强大,但在某些场景下生成的代码可能比Rollup更臃肿。

功能对比

特性 Vite Webpack
开发模式构建机制 基于原生ESM,按需加载模块 基于打包,生成bundle文件
启动速度 极快,与项目规模无关 较慢,随项目规模增长而增加
热更新(HMR) 只更新改动的模块,速度更快 需要重新构建bundle或chunk
生产构建工具 使用Rollup 使用Webpack自身
插件生态 插件生态较新,但增长迅速 插件生态非常成熟
配置复杂度 配置简单,开箱即用 配置复杂,功能强大但学习成本高
Tree Shaking 支持,基于Rollup 支持,功能强大
代码分割 支持,基于Rollup 支持,功能强大

总结

  • Vite的优势在于开发模式下的极速启动和热更新,适合现代前端项目和快速迭代的开发环境。
  • Webpack的优势在于功能强大、插件生态成熟,适合复杂项目和高度定制化的构建需求。

如果你的项目需要快速启动和热更新,并且对现代前端技术栈有较好的支持,Vite是一个更好的选择。如果你的项目需要高度定制化的构建流程,或者依赖Webpack的插件生态,Webpack仍然是更合适的选择。

# Webpack5有哪些更新

  1. 长期缓存与持久化缓存

    • 内置持久化缓存:Webpack5引入了内置的持久化缓存机制,可将打包过程中的中间结果存储在磁盘上。下次打包时,若文件没有发生变化,Webpack可以直接使用缓存结果,显著提高打包速度。
    • 改进的长期缓存:Webpack5改进了对文件哈希的生成算法,能够更好地处理文件内容变化,确保只有真正发生改变的文件其哈希值才会变化,从而提高长期缓存的命中率。
  2. 模块联邦(Module Federation)

    模块联邦是Webpack5最重要的特性之一,它允许在不同的Webpack构建之间动态共享模块。

  3. 资源模块(Asset Modules)

    简化资源处理:Webpack5引入了资源模块类型,用于替代之前的file-loader、url-loader和raw-loader。资源模块可以更方便地处理不同类型的资源文件,如图片、字体等。

  4. 性能优化

    • 更好的Tree Shaking:Webpack5改进了Tree Shaking算法,能够更准确地识别和移除未使用的代码,减少打包后的文件体积。
    • 更快的打包速度:通过优化内部算法和数据结构,Webpack5在打包大型项目时速度有了显著提升。
最近更新: 2025年03月19日 13:31:15