Vite: 毫秒级局部更新之HMR

概述

  • 在代码变更之后,如何实时看到更新后的页面效果呢?
  • 在很久之前的方案是通过 live reload也就是自动刷新页面的方式来解决的。
  • 不过随着前端工程的日益庞大,开发场景也越来越复杂,这种 live reload 的方式在诸多的场景下却显得十分鸡肋,简单来说就是 模块局部更新 + 状态保存 的需求在 live reload 的方案没有得到满足,从而导致开发体验欠佳。
  • 当然,针对部分场景也有一些临时的解决方案,比如状态存储到浏览器的本地缓存(localStorage 对象)中,或者直接 mock 一些数据。但这些方式未免过于粗糙,无法满足通用的开发场景,且实现上也不够优雅。
  • 那么,如果在改动代码后,想要进行模块级别的局部更新该怎么做呢?业界一般使用
    HMR 技术来解决这个问题,像 Webpack、Parcel 这些传统的打包工具底层都实现了一
    套 HMR API,而我们今天要讲的就是 Vite 自己所实现的 HMR API,相比于传统的打包
    工具,Vite 的 HMR API 基于 ESM 模块规范来实现,可以达到毫秒级别的更新速度,性
    能非常强悍。

关于 HMR

  • HMR 的全称叫做 Hot Module Replacement ,即 模块热替换 或者 模块热更新
  • 在计算机领域当中也有一个类似的概念叫 热插拔 ,我们经常使用的 USB 设备就是一个典型的代表,
    当我们插入 U 盘的时候,系统驱动会加载在新增的 U 盘内容,不会重启系统,也不会修改系统其它模块的内容
  • HMR 的作用其实一样,就是在页面模块更新的时候,直接把页面中发生变化的模块替换为新的模块,同时不会影响其它模块的正常运作。具体来说,你可以观察下面这个实现 HMR 的例子
  • 在这里,我改变了页面的一个状态 count ,当我对页面再次进行调整的时候,比如把最上面的 Logo 图片去掉,这个时候大家可以实时地看到图片消失了,但其他的部分并没有发生改变,包括组件此时的一些数据。如此一来,通过 HMR 的技术我们就可以实现 局部刷新 和 状态保存 ,从而解决之前提到的种种问题

HMR API

  • Vite 作为一个完整的构建工具,本身实现了一套 HMR 系统,值得注意的是,这套 HMR
    系统基于原生的 ESM 模块规范来实现,在文件发生改变时 Vite 会侦测到相应 ES 模块的
    变化,从而触发相应的 API,实现局部的更新。
  • Vite 的 HMR API 设计也并非空穴来风,它基于一套完整的 ESM HMR 规范来实现,这
    个规范由同时期的 no-bundle 构建工具 Snowpack、WMR 与 Vite 一起制定,是一个
    比较通用的规范。我们可以直观地来看一看 HMR API 的类型定义:
    interface ImportMeta {
         
         
      readonly hot ? : {
         
         
        readonly data: any
        accept(): void
        accept(cb: (mod: any) => void): void
        accept(dep: string, cb: (mod: any) => void): void
        accept(deps: string[], cb: (mods: any[]) => void): void
        prune(cb: () => void): void
        dispose(cb: (data: any) => void): void
        decline(): void
        invalidate(): void
        on(event: string, cb: (...args: any[]) => void): void
      }
    }
    
  • 这里稍微解释一下, import.meta 对象为现代浏览器原生的一个内置对象,Vite 所做的
    事情就是在这个对象上的 hot 属性中定义了一套完整的属性和方法。因此,在 Vite 当
    中,你就可以通过 import.meta.hot 来访问关于 HMR 的这些属性和方法,比如
    import.meta.hot.accept() 。接下来,我们就来一一熟悉这些 API 的使用方式。

模块更新时逻辑: hot.accept

  • 在 import.meta.hot 对象上有一个非常关键的方法 accept ,因为它决定了 Vite 进行热
    更新的边界,那么如何来理解这个 accept 的含义呢?从字面上来看,它表示接受的意思。没错,它就是用来接受模块更新的。 一旦 Vite 接受了这个更新,当前模块就会被认为是 HMR 的边界。那么,Vite 接受谁的更新呢?这里会有三种情况:
    • 接受自身模块的更新
    • 接受某个子模块的更新
    • 接受多个子模块的更新
  • 这三种情况分别对应 accept 方法三种不同的使用方式

1 )接受自身更新

  • 当模块接受自身的更新时,则当前模块会被认为 HMR 的边界。
  • 也就是说,除了当前模块,其他的模块均未受到任何影响。
  • 下面是我准备的一张示例图,你可以参考一下:
  • 展示一下整体的目录结构

    .
    ├── favicon.svg
    ├── index.html
    ├── node_modules
    │    └── ...
    ├── package.json
    ├── src
    │    ├── main.ts
    │    ├── render.ts
    │    ├── state.ts
    │    ├── style.css
    │    └── vite-env.d.ts
    └── tsconfig.json
    
  • 如下面的 index.html :

    <!DOCTYPE html>
    <html lang="en">
     <head>
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href=
<think>我们正在比较Vite和Webpack这两个前端构建工具。根据引用内容,我们可以从以下几个方面进行对比:开发模式、构建机制、性能、生态扩展以及使用场景。1.**开发模式**:- **Webpack**:采用打包编译(bundle-based)的方式。在开发服务器启动时,它会对整个应用进行打包,生成bundle文件,然后启动开发服务器。这意味着随着项目规模增大,启动时间会变长(引用[1][2][4])。- **Vite**:采用即时编译(on-demand compilation)的方式。它利用浏览器原生支持ES模块的特性,在开发阶段将代码分为依赖和源码两部分。依赖部分使用esbuild预构建,源码部分则按需编译并提供。因此Vite的启动速度非常快(引用[1][2][4])。2.**构建机制**:- **Webpack**:属于打包工具(bundler),它会将所有模块打包成一个或多个bundle文件。它通过loader处理非JavaScript文件,通过插件系统进行功能扩展(引用[1][3])。- **Vite**:在开发环境中采用No-Bundle(按需编译)的方式,而在生产环境中使用Rollup进行打包。这样既保证了开发速度,又确保了生产环境的优化(引用[2][4])。 3. **性能对比**:-**开发阶段**:- **启动速度**:Vite冷启动极快(毫秒级),因为无需打包;Webpack则随着项目增大启动时间显著增加(引用[4])。-**热更新HMR)**:Vite的热更新是模块级的,速度极快;Webpack需要重新构建整个bundle,速度较慢(引用[2][4])。- **内存占用**:Vite内存占用低,Webpack内存占用高(引用[4])。-**生产阶段**:两者都会进行打包优化,Vite使用Rollup,而Webpack本身具有强大的优化能力(引用[2][4])。 4. **生态扩展**:-**Webpack**:拥有庞大的插件生态,功能强大但配置复杂(引用[1][2][4])。-**Vite**:基于Rollup的插件生态,配置更简单,开箱即用(引用[2][4])。5.**使用场景**:- **Webpack**:适合大型复杂项目,需要高度定制化构建流程的场景(引用[1][2])。-**Vite**:适合现代浏览器项目,尤其是开发阶段对速度要求高的场景(如Vue/React项目)(引用[1][2][4])。 总结表格:|特性| Webpack| Vite| |--------------|------------------------------------|-------------------------------------|| **开发模式** |打包编译(全量打包)|即时编译(按需编译)| |**启动速度**|慢(项目越大越慢)|极快(毫秒级)|| **热更新**|较慢(重新构建bundle)|极快(模块级更新)| |**构建机制**|打包所有模块|开发环境按需编译,生产环境用Rollup打包|| **配置**|复杂,功能强大|简单,开箱即用|| **生态**|庞大的插件生态|基于Rollup的插件,兼容部分Webpack插件 || **适用场景** |大型复杂项目,需要深度定制|现代浏览器项目,追求开发效率| 相关问题: 1. Vite在生产环境中是如何进行构建的?2.Webpack有哪些优化生产构建的常用配置? 3.在迁移现有Webpack项目到Vite时需要注意哪些问题?4.Vite如何处理非JavaScript资源(如图片、样式)?</think>### Vite 与 Webpack 特性对比及使用场景分析 #### 1. 核心架构差异 - **Vite**:基于浏览器原生 **ES 模块(ESM)** 的按需编译架构 $$开发模式:\text{请求驱动编译} \quad \Rightarrow \quad \text{毫秒级启动}$$ 通过 `esbuild` 预构建依赖,源码按需转换(如 `.vue`/`.jsx` 文件)[^1][^2]。 - **Webpack**:基于 **打包编译(Bundle)** 的静态分析架构 $$开发模式:\text{全量打包} \quad \Rightarrow \quad \text{生成内存 Bundle}$$ 启动时必须构建完整依赖图,项目越大启动越慢[^1][^4]。 #### 2. 性能对比(开发环境) | 指标 | Vite | Webpack | |--------------|--------------------------|--------------------------| | **冷启动** | ⚡️ 50ms~500ms(按需编译) | ⏳ 30s~120s(全量打包) | | **热更新** | 🔥 局部模块更新毫秒级) | 🔄 整包重建(秒级) | | **内存占用** | 🟢 较低(无中间产物) | 🔴 较高(需维护 Bundle) | > 引用数据:Vite 在 1000+ 模块的项目中启动快 10~20 倍,热更新快 3~7 倍[^4]。 #### 3. 构建机制区别 ```mermaid graph LR A[浏览器请求] --> B{Vite处理流程} B -->|1. 请求HTML| C[返回基础HTML] B -->|2. 请求JS模块| D[按需编译源码] B -->|3. 预构建依赖| E[esbuild转换node_modules] F[源码变更] --> G{Vite更新} G -->|仅更新单模块| H[浏览器快速响应] I[Webpack流程] --> J[打包所有资源] J --> K[生成内存Bundle] K --> L[启动开发服务器] ``` #### 4. 生态与配置 - **Vite**: ✅ 开箱支持 TypeScript/JSX/CSS Modules ✅ 兼容 Rollup 插件(生产构建基于 Rollup) ⚠️ 插件生态较新(但 Vue/React 官方深度集成) - **Webpack**: ✅ 超 20000+ 插件(如 Babel/PostCSS 等) ✅ 成熟代码分割/懒加载方案 ⚠️ 配置复杂(需 loader 链处理非 JS 资源)[^3] #### 5. 生产构建差异 | 特性 | Vite | Webpack | |--------------|--------------------------|--------------------------| | **构建工具** | Rollup(高效 ES 模块输出)| 自带优化(Tree-shaking) | | **输出格式** | 优先 ESM(现代浏览器) | 兼容 CommonJS/UMD | | **优化重点** | 轻量依赖 + 原生 ESM 加载 | Bundle 拆分 + 缓存优化 | #### 6. 使用场景推荐 - **选择 Vite 当**: 🔸 开发体验优先级高(如高频热更新需求) 🔸 现代浏览器项目(无需兼容 IE) 🔸 Vue/React 新项目(框架官方支持) > 案例:Next.js/Nuxt.js 的轻量替代方案 - **选择 Webpack 当**: 🔸 需要深度定制构建流程(复杂 loader 链) 🔸 兼容旧浏览器(需 Babel 多重 polyfill) 🔸 超大型项目(成熟生态保障稳定性) > 案例:企业级应用/微前端架构底层 --- ### 相关问题 1. Vite 如何实现毫秒级更新?其与 Webpack 的 HMR 机制有何本质区别? 2. 在需要兼容 IE11 的传统项目中,迁移到 Vite 可能遇到哪些挑战?如何解决? 3. Webpack 的 Tree-shaking 和 Vite 的生产构建优化策略有何异同? 4. 如何根据团队技术栈(React/Vue/Angular)选择最适合的构建工具? [^1]: Vite 与 Webpack 的核心构建方式差异 [^2]: 开发模式性能对比分析 [^3]: Webpack 的 loader 机制与插件生态 [^4]: Vite 在开发效率与技术原理上的优势
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值