前端面试八股—Webpack和Vite

687 阅读8分钟

模块化

讲讲你对对前端模块化的认知, 为什么要打包?

  • 前端模块化:前端模块化是指将一个复杂的Web应用拆分成多个互相依赖的、单独的模块,从而方便代码的组织、管理和维护。
  • 为什么要打包:框架、ES6语法、Less/Sass等语法想要在浏览器运行,需要将他们编译成浏览器能识别的js、css等语法,才能在浏览器中运行,打包工具可以完成这些事情。除此之外,打包工具还能压缩代码、处理兼容性、提升代码性能。

ESModule和CommonJS的区别

  1. 用法不同:ES module是Javascript模块化语法,使用import/export 关键字实现模块的导入和导出。而CommonJs是Node的模块化方案,采用require和module.exports实现模块的导入和导出
  2. 加载方式不同:ES module是在编译时静态加载,就是说在编译时就能确定依赖关系,而CommonJs是在运行时动态加载,也就是说在运行时才能确定依赖关系
  3. 导入和导出特性不同:ES module支持异步导入,动态导入和命名导入等特性,可以根据需要动态导入导出。而CommonJs只支持同步导入导出
    // 异步导入模块
    import('./module.js')
      .then((module) => {
        // 使用导入的模块
      })
      .catch((err) => {
        // 处理导入错误
      });
    
    // 动态导入模块
    const modulePath = './module.js';
    import(modulePath)
      .then((module) => {
        // 使用导入的模块
      })
      .catch((err) => {
        // 处理导入错误
      });
    
    // 命名导入
    import { namedExport1, namedExport2 } from './module.js';
    
  4. 循环依赖处理方式不同:ES module在编译阶就会检查循环引用,而CommonJS只能在运行时检查循环引用
  5. ESmodule需要在支持ES6的浏览器或者Nodejs的版本才能使用,而CommonJS的兼容性会更好

1. Webpack

Webpack的流程?

  • 初始化:开始构建,读取与合并配置参数,加载plugin,实例化compiler
  • 编译:compiler根据entry找到所有入口文件,并递归地解析每个入口文件的依赖关系,然后将它们交给对应的loader进行编译
  • 输出:所有模块被编译完成后,compiler将它们打包成一系列的chunk,并且通过output将这些chunk输出到文件系统中

Webpack有哪些配置?

entry入口、output出口、loader加载器、plugins插件、model模式

Webpack的plugin和loader区别?

  • loader:webpack 默认支持处理 JS 与 JSON 文件,其他类型都处理不了,所以需要借助loader来对不同类型的文件进行处理
  • plugin:可以扩展webpack的功能,让webpack具有更多的灵活性

Webpack常用的loader?

loader的配置:在module.rules中指定多个loader,loader的执行顺序是从下->上(或从右->左)

module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
          { loader: 'sass-loader' },
        ],
      },
    ],
  },
};
  • babel-loader:把 ES6 转换成 ES5
  • ts-loader: 将 TypeScript 转换成 JavaScript
  • awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader
  • sass-loader:将SCSS/SASS代码转换成CSS
  • less-loader: 将LESS代码转换成CSS
  • css-loader:加载 CSS,使用css-loader必须要配合使用style-loader
  • style-loader: css文件被打包到js中,当js文件夹加载时,动态创建一个style标签来生成样式【会出现闪屏现象】
  • postcss-loader:自动配置css,使其满足浏览器的兼容性
  • eslint-loader:通过 ESLint 检查 JavaScript 代码
  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片、字体、音频、视频)(webpack5已内置:asset/resource)
  • url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片)(webpack5已内置:asset)
    • 将图片转换为base64的优缺点:
      • 优点:减少HTTP请求,因为将图片转为base64后可以直接嵌入到HTML、CSS或JS中,避免了浏览器发起额外的请求
      • 缺点:base64编码后的大小大于原始图片,且原图片越大,base64编码相对于原图片的大小越大
      • 应用场景:将图片转为base64编码适用于小图片和少量图片的情况
  • thread-loader:多进程打包
  • vue-loader:加载并编译 Vue 组件

Webpack常用的plugin?

  • html-webpack-plugin:自动将打包后的资源文件,如:js 或者 css 文件引入到 html 中
  • mini-css-extract-plugin:将css文件打包成单独的css文件,通过link标签加载【代替style-loader,防止闪屏现象】
  • css-minimizer-webpack-plugin:压缩CSS代码
  • terser-webpack-plugin:压缩js代码
  • @babel/plugin-transform-runtime:禁用Babel自动对每个文件的辅助代码注入,而是引用
  • image-minimzer-webpack-plugin:压缩图片
  • eslint-webpack-plugin:eslint配置
  • @vue/preload-webpack-plugin:预加载,可以设置preload或prefetch,实际上就是
    <link href='需要预加载的js/css/图像/字体等资源文件' rel='preload(或prefetch)'>
    
  • webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度

source map是什么?

source map即源代码映射:

  • 打包压缩后的代码不具备良好的可读性,入生产环境下代码会被压缩成一行,想要清晰、方便地调试源码就需要 soucre map
  • 它会生成一个xxx.map文件,里面包含源代码和打包压缩后的代码的映射关系。当打包后的代码出错时,会通过xxx.map文件找到源代码出错位置,从而让浏览器提示源代码的出错位置

如何提高webpack的打包速度?

  1. 开发时我们修改了其中一个模块的代码,webpack默认会将所有模块全部重新打包编译,速度很慢。模块热替换(HMR - hot module replacement),又叫做热更新,在不需要刷新整个页面的同时更新模块,能够提升开发的效率和体验。热更新时只会局部刷新页面上发生了变化的模块,同时可以保留当前页面的状态,比如复选框的选中状态等。当发生模块更新时,HMR会通过重新加载模块代码,并使用虚拟DOM进行比较和更新。它会将新的模块代码与当前页面中的虚拟DOM进行比较,找到差异并进行局部更新,而不是重新渲染整个页面。
  2. 打包时每个文件都会经过所有的loader,这也就意味着,一个文件要被多个loader过滤一遍,这在有些情况下是很冗余的,因此,我们希望每个文件只匹配一个loader,这便是oneOf的作用。
  3. 开发时我们需要使用第三方插件或库,这些文件会被下载到node_modules中,这些文件不需要经过编译就可以使用,所以我们在处理Js文件时需要排除node_modules下的文件。可以用include只处理xxx文件,或exclude除了xxx文件外其他文件都处理。
  4. 每次打包时js文件时,所有js文件都要经过Eslint检查和Babel编译,速度比较慢,可以使用cache缓存之前的Eslint检查和Babel编译结果,Eslint检查和Babel编译只处理被修改了的js文件,从而加快第二次打包的速度。
  5. 使用thead-loader多进程打包文件

如何用webpack减少代码体积?

  1. 开发时我们定义了一些工具函数库,或引用了第三方的工具函数库或组件库。如果没有特殊处理的话,我们打包时会引入整个库,但我们可能只用了其中极小部分的功能。tree shaking是一个术语,通常用于移除js中没有使用的代码。
  2. babel会在编译每个文件时插入辅助代码,以便能够支持ES6语法。这些辅助代码通常会增加编译后代码的体积。使用babel/plugin-transform-runtime禁用Babel自动对每个文件的辅助代码注入,而是引用。
  3. image-minimzer-webpack-plugin压缩项目中的图片,减少图片体积

如何用webpack来优化代码运行性能?

  1. 打包代码时会将所有js文件打包到一个文件中,体积太大了。所以需要将打包生成的文件进行code split(代码分割)。在webpack中可以通过配置splitChunks实现代码分割、提取公共代码到一个单独的文件。然后通过import()动态导入实现按需加载(懒加载)。
  2. 只用按需加载(懒加载)也会有问题,比如:用户点击按钮后才动态导入资源,如果该资源过大,用户会感到明显的卡顿。我们想在浏览器的空闲时间,加载后续需要使用的资源,就需要预加载preloadprefetch。preload会让浏览器立即加载资源,只能加载当前页面需要使用的资源。prefetch会让浏览器在空闲时加载资源,可以记载当前页面资源,也可以加载下一个页面需要使用的资源。在webpack中可以通过@vue/preload-webpack-plugin插件设置。
  3. network cache
  4. Babel用来处理js的兼容性,能将ES6的一些语法进行编译转换,比如箭头函数、扩展运算符等。但是Babel不能处理async、promise、数组的一些方法。core-js
  5. PWA

2. Vite

Webpack 和 Vite 的区别?

vite的原理

vite的热更新原理?

vite的原理和底层实现 vite冷启动