vite性能优化提升开发体验之hmr和预编译

一、vite中的预编译

1. 预编译概念介绍

Vite,一个由Vue.js开发者尤雨溪开发的新型前端构建工具,主要利用了现代浏览器支持的ESM(ES模块)来进行快速开发。Vite在法语中意为“快”,其中最大的亮点就是其开发服务器启动的速度,能够在几乎看不到的时间内完成启动,这主要得益于两大关键性的技术:模块热更新HMR)和预编译Pre-Bundling)。

预编译,也就是预构建(Pre-Bundling)。在传统的打包过程中(比如使用Webpack),所有的模块都会被构建到一个文件中去,这个过程就是所谓的“打包Bundling)”。在Vite中,引入了“预编译”的这么一个概念,Vite会在服务器启动时对所有的依赖进行一次性的编译,并将其缓存起来。在后续的模块导入的时候,就不需要再对这些模块进行编译,提高了加载效率。

node_modules/.vite中的文件就是vite预编译文件
在这里插入图片描述

Vite的预编译主要针对那些大型的、复杂的第三方库,对把它们转换成可以直接在浏览器运行的ES模块。这样做主要有以下优化效果:

  1. 加快了开发过程中的加载速度。因为在开发模式下,运行Vite的应用,会请求大量的小模块,但是对于一些大型的库(比如Vue、React等),如果按照模块化的方式加载明显不合理,会造成请求过多。通过预编译,Vite将这些库提前编译到一个文件里,从而减少请求的开销。
  2. 加快了构建速度。因为预编译阶段已经预先处理了依赖的模块,所以在最终打包构建的时候,只需要处理自身的源代码逻辑,无需再额外处理依赖模块,以此提高构建速度。
  3. 改善了兼容性问题。由于很多第三方模块可能包含一些现在浏览器所不支持的代码,比如JSX,通过预编译,Vite将这些代码转换成浏览器可以直接运行的ES模块,解决了兼容性问题。
  4. 更好地实现了按需加载和代码拆分。预编译将大库拆分成多个小模块,可以按需加载,无需加载整个库,从而优化了性能。

举例来说,假设一个前端项目用到了vue、vant、axios等多个库,在没有使用预编译的情况下,每次启动项目时都需要去加载和解析这些库,耗时较长。而使用了Vite的预编译功能后,这些库可以在项目启动前就已经编译完成,后续只需要引用即可,大大提高了前端项目的启动速度和运行效率。

2. 配置预编译选项

在 Vite 项目中,我们在 vite.config.js 文件中配置 optimizeDeps 选项来实现对第三方库预编译的控制。optimizeDeps 选项允许手动设置需要预构建的依赖。exclude 属性用于排除某些不需要预编译的依赖,include 属性用于添加需要预编译的依赖。

以下是一个基本的配置示例:

// vite.config.js
module.exports = {
    
    
  optimizeDeps: {
    
    
    include: ['lodash'],   // 将 lodash 加入预编译
    exclude: ['moment']   // 将 moment 移除预编译
  }
}

在上面的示例中,lodash 将会被预编译,而 moment 将不会被预编译。

注意:开发依赖和已知不需要编译的依赖会自动被 Vite 排除掉。如果遇到有问题的依赖预编译,可以使用 exclude 来手动排除。而对于 Vite 默认没有预编译,但你需要其预编译的依赖,可以使用 include 来手动添加。

3. vite和webpack目前在预编译阶段的对比

功能/框架 Vite Webpack
预编译 Vite 使用 esbuild 进行预编译,因为 esbuild 是用 Go 语言写的,所以预编译的速度比 webpack 快很多。但是 esbuild 的兼容性和插件系统不如 webpack 完善 Webpack 使用 babel 作为默认的预处理工具,与 esbuild 相比较,速度慢很多。但是 webpack 的社区更加活跃,有很多插件可以使用,并且兼容性更好。
Tree Sharking Vite使用ES模块导入进行tree shaking,可以直接消除无用代码,效率更高。更好的支持动态导入(import())和CSS导入。 Webpack的tree shaking需要在生产模式下才能执行,而且不支持动态导入和CSS导入的tree shaking,可能会保留无用代码。
缓存机制 Vite在开发模式下没有使用缓存,但在生产模式下使用Rollup打包时,会进行缓存以优化构建速度。Vite的缓存机制更侧重于模块的热更新,并且,支持服务器端的模块缓存。 Webpack使用硬盘缓存,初次构建慢,但是再次构建会从缓存中加载模块,从而提高构建速度。但在大型项目中,如果不合理配置,缓存可能会导致内存飙升。
优点 构建速度快,开发体验好,方便快捷。支持Vue 3.0、Hot Module Replacement(HMR)等新特性。 提供了丰富的配置和插件系统,适合大型项目。
劣势 对于大型项目,可能会出现一些隐藏的问题和兼容性问题。 配置复杂,学习曲线陡峭。初次构建速度慢。

二、热模块替换hmr

1. 什么是hmr

热模块替换Hot Module Replacement,HMR)是一种机制,它使得应用在运行时能够更新各种模块,而无需进行完全刷新。例如,某些库可以针对这个API进行优化,以达到接近无刷新更改的效果。这项技术主要针对单页面应用(SPA)。

举例说明,如果我们在编写一个网页应用,并且同时运行着一个开发服务器,那么当我们修改了代码并保存后,整个页面会自动刷新以显示出新的结果。而如果使用了HMR,就无需刷新整个页面,我们改动的部分(模块)会被自动替换掉并立即显示出新的效果

应用HMR之后的好处有:

  1. 保持应用状态:传统的整页刷新会导致当前应用的状态被丢失,而HMR能够在无需刷新整个页面的情况下替换、添加或删除模块,从而能够保持应用的状态。
  2. 只更新更改的部分:当修改一个或多个代码模块,整个应用不需要全部更新,只更新被改动的模块。
  3. 开发速度:由于只替换更改的内容,所以测试新的变更变得更快。
  4. 样式调整快:如果应用的修改仅仅是 CSS/SCSS 样式,那么 HMR 就会变得非常有用。这是因为,当你调整样式的时候,无需刷新页面,调整立马生效,这对于样式调整而言是非常有利的。

注意,HMR 主要在开发环境,生产环境通常不需要开启。

2. vite中hmr的相关配置

Vite构建的项目中,默认情况下是启用了热模块替换(HMR)的。如果你需要修改HMR相关的配置,你可以在Vite的配置文件(vite.config.js)中,对server.hmr进行设置。

例如,如果你需要禁用HMR,可以在vite.config.js中这样配置:

export default {
    
    
  server: {
    
    
    hmr: false
  }
}

如果需要设置连接超时或者跳过检查脏模块,可以如下配置:

export default {
    
    
  server: {
    
    
    hmr: {
    
    
      timeout: 30000,
      overlay: false
    }
  }
}

ViteHMR能力非常强大,它不仅可以处理JavaScript模块的热替换,还能处理CSS、HTML等其他类型的模块。只要你的代码里含有HMR相关的接口,Vite就能自动完成热替换。

在大部分情况下,你并不需要手动设置HMR,Vite默认的设置已经可以满足绝大多数应用场景。如果你需要设置HMR,通常是遇到了一些特殊的情况,比如需要修改连接超时时间、需要禁用HMR等。

3. vite中hmr的执行过程

热模块替换(Hot Module Replacement,HMR)是Vite(以及其他现代前端构建工具,如Webpack)的一个重要特性,但Vite的实现方式相对更优。

在对JavaScript、Vue、React等进行HMR时,Vite会有些不同的处理方式,总的来说,有以下几个步骤:

  1. 文件改变后,Vite的开发服务器首先会通知在客户端运行的更新代码,文件已经被更新。

  2. 在客户端,Vite有一个运行时处理器,它会找到这个文件对应的模块。

  3. 查看这个模块的其他依赖或者倚赖(即这个模块被哪些模块引用),建立依赖关系图。

  4. 如果这个模块可以被更新(即符合HMR规则)则直接更新,如果不能则会通知引用它的父模块更新。

  5. 这个过程是递归的,直至找到可以进行HMR的模块,或者已经到达了应用的顶级模块,此时则会进行全量刷新。

相比WebpackVite不需要额外的代码进行热更新,因为Vite是基于ES6模块的,可以依靠浏览器原生的import语法去请求和缓存模块,使得HMR更加高效。而Webpack是基于CommonJS的,需要通过上下文去理解模块的引用关系,实现热更新则需要额外的编译输出和运行时支持。

此外,Vite对于CSS的处理也具有优势,当CSS文件更新时,Vite直接通过inject的方式在客户端更新样式,而不会影响正在运行的JavaScript代码。

4. vite和webpack中的hmr对比

项目 Vite 中的 HMR Webpack 中的 HMR
定义 Vite 中,HMR 是通过 ESM 在浏览器中的原生支持,无需进行额外的浏览器以下代码处理,并且具有极快的速度。 Webpack 中,HMR 是一种模块热替换的功能,可以在应用程序运行过程中替换、添加、删除模块,无需进行整个页面的刷新。
速度 速度比较快,因为它只需要更新修改过的部分,而不是整个应用。 相对较慢,因为当文件修改时,Webpack 会重新构建并刷新整个页面。
热更新范围 由于 Vite 利用原生 ESM 进行模块热更新,所以其影响范围较小,只对被修改的模块进行更新。 若一个模块的值发生了改变,改变会冒泡,使得依赖这个模块的所有模块全部更新,更新范围相当大。
原理 利用了 ES6 module 的系统,当文件改变时,直接只请求改变的文件并重新执行。 默认会给每个模块添加热更新代码,当文件发生变化后,通过打包工具发出的文件更改信号,触发更新函数,让客户端重新加载文件。
配置复杂度 Vite 的配置相对较简单,只需要简单的几步就能启用 HMR。 Webpack 的配置要复杂一些,除了需要添加 HMR 插件,还需要对其进行详细的配置。
兼容性 Vite 要求 Node.js 版本 12.0.0 以上,并且只支持现代浏览器,不支持 IE。 Webpack 对于旧版本的浏览器兼容性更好。

三、总结

对于hmr,在Vite中,当改动一个文件时,Vite会立即使用WebSocket向浏览器推送更新,然后浏览器通过重新获取被改动的模块,替换原有的代码,从而实现了部分更新,而不必要进行页面的整体刷新。这大大提高了开发时的用户体验。

Vite中的预编译: 在传统的 SPA 打包器中,无论 JavaScript 文件的大小,文件需要被完全解析并转换为浏览器可运行的代码,这个过程可能会比较耗时。而Vite则采取了另一个策略,即预编译。
Vite 开发服务器在启动时,会预编译所有被 import 的依赖。预编译将ES6+ 语法或 TypeScript 编译为可以在浏览器中运行的 ESnext 语法,并尽可能的粗略编译,只做最小化处理(实际上只做语法编译和依赖导入的重写),所以在具有缓存的情况下,预编译是非常快的。
在第一次页面加载时,仅需要加载和解析实际需要的代码。之后的页面导航都会只加载需要的代码,并且因为服务器在内存中保留有编译代码,所以这个过程是非常快的。

猜你喜欢

转载自blog.csdn.net/jieyucx/article/details/131664802