51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

vite为什么比webpack快?

通过这篇文章你可以了解到WebpackVite有哪些差异化,没有更好和最好的工具只有最适合自己的工具。

webpack {#webpack}

在webpack开发时构建时,默认会抓取并构建你的整个应用,然后才能提供服务,这就导致了你的项目中存在任何一个错误(即使当前错误不是首页引用的模块),他依然会影响到你的整个项目构建。所以你的项目越大,构建时间越长,项目启动速度也就越慢。

vite {#vite}

vite不会在一开始就构建你的整个项目,而是会将引用中的模块区分为依赖和源码(项目代码)两部分,image-1691629092545
改进了开发服务器启动时间。依赖使用esbuild进行依赖预构建,源码就是我们代码中写的业务组件.vue,.ts文件等,是esmodule的形式,一方面浏览器可以直接解析esmodule,另一方面,我们可以通过动态导入,路由懒加载的方式,只有当路由跳转到对应页面才去加载该页面的资源。
image-1691629115268对于源码部分,他会根据路由来拆分代码模块,只会去构建一开始就必须要构建的内容。
同时vite以原生 ESM的方式为浏览器提供源码,让浏览器接管了打包的部分工作。

vite服务热更新更快 {#vite%E6%9C%8D%E5%8A%A1%E7%83%AD%E6%9B%B4%E6%96%B0%E6%9B%B4%E5%BF%AB}

在 Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活[1](大多数时候只是模块本身),使得无论应用大小如何,HMR 始终能保持快速更新。
Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。

缓存 {#%E7%BC%93%E5%AD%98}

文件系统缓存 {#%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E7%BC%93%E5%AD%98}

Vite 将预构建的依赖项缓存到 node_modules/.vite 中。它会基于以下几个来源来决定是否需要重新运行预构建步骤:

  • 包管理器的锁文件内容,例如 package-lock.json,yarn.lock,pnpm-lock.yaml,或者 bun.lockb;
  • 补丁文件夹的修改时间;
    -vite.config.js 中的相关字段;
  • NODE_ENV 的值。

只有在上述其中一项发生更改时,才需要重新运行预构建。
如果出于某些原因你想要强制 Vite 重新构建依赖项,你可以在启动开发服务器时指定 --force 选项,或手动删除 node_modules/.vite 缓存目录。

浏览器缓存 {#%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BC%93%E5%AD%98}

已预构建的依赖请求使用 HTTP 头 max-age=31536000, immutable 进行强缓存,以提高开发期间页面重新加载的性能。一旦被缓存,这些请求将永远不会再次访问开发服务器。如果安装了不同版本的依赖项(这反映在包管理器的 lockfile 中),则会通过附加版本查询自动失效。如果你想通过本地编辑来调试依赖项,您可以:

通过浏览器开发工具的 Network 选项卡暂时禁用缓存;
重启 Vite 开发服务器指定 --force 选项,来重新构建依赖项;
重新载入页面。

vite一些处理 {#vite%E4%B8%80%E4%BA%9B%E5%A4%84%E7%90%86}

npm依赖解析和构建 {#npm%E4%BE%9D%E8%B5%96%E8%A7%A3%E6%9E%90%E5%92%8C%E6%9E%84%E5%BB%BA}

原生 ES 导入不支持下面这样的裸模块导入:

import { someMethod } from 'my-dep'

上面的代码会在浏览器中抛出一个错误。Vite 将会检测到所有被加载的源文件中的此类裸模块导入,并执行以下操作:

  1. 预构建 它们可以提高页面加载速度,并将 CommonJS / UMD 转换为 ESM 格式。预构建这一步由 esbuild 执行,这使得 Vite 的冷启动时间比任何基于 JavaScript 的打包器都要快得多。

  2. 重写导入为合法的 URL,例如 /node_modules/.vite/deps/my-dep.js?v=f3sf2ebd 以便浏览器能够正确导入它们。

依赖是强缓存的
Vite 通过 HTTP 头来缓存请求得到的依赖,所以如果你想要编辑或调试一个依赖,请按照 这里 的步骤操作。

当你首次启动 vite 时,Vite 在本地加载你的站点之前预构建了项目依赖。默认情况下,它是自动且透明地完成的。

原因
这就是 Vite 执行时所做的"依赖预构建"。这个过程有两个目的:

  1. CommonJS 和 UMD 兼容性: 在开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将以 CommonJS 或 UMD 形式提供的依赖项转换为 ES 模块。
    在转换 CommonJS 依赖项时,Vite 会进行智能导入分析,这样即使模块的导出是动态分配的(例如 React),具名导入(named imports)也能正常工作:
// 符合预期
import React, { useState } from 'react'
  1. 性能: 为了提高后续页面的加载性能,Vite将那些具有许多内部模块的 ESM 依赖项转换为单个模块。
    有些包将它们的 ES 模块构建为许多单独的文件,彼此导入。例如,lodash-es 有超过 600 个内置模块!当我们执行 import { debounce } from 'lodash-es' 时,浏览器同时发出 600 多个 HTTP 请求!即使服务器能够轻松处理它们,但大量请求会导致浏览器端的网络拥塞,使页面加载变得明显缓慢。
    通过将lodash-es 预构建成单个模块,现在我们只需要一个HTTP请求!

注意
依赖预构建仅适用于开发模式,并使用 esbuild 将依赖项转换为 ES 模块。在生产构建中,将使用 @rollup/plugin-commonjs。

typescript {#typescript}

Vite 天然支持引入 .ts 文件。 请注意,Vite 仅执行 .ts 文件的转译工作,并不执行 任何类型检查。并假定类型检查已经被你的 IDE 或构建过程处理了。那怎么开启类型检查呢?

  • 在构建生产版本时,你可以在 Vite 的构建命令之外运行 tsc --noEmit

  • 在开发时,如果你需要更多的 IDE 提示,我们建议在一个单独的进程中运行 tsc --noEmit --watch,或者如果你喜欢在浏览器中直接看到上报的类型错误,可以使用 vite-plugin-checker。
    客户端类型 Vite 默认的类型定义是写给它的 Node.js API 的。要将其补充到一个 Vite 应用的客户端代码环境中,请添加一个 d.ts 声明文件:
    image-1691630011383
    tsconfig.json中需要加入一下
    image-1691630054251

或者官网提示可以这样做:
image-1691630106494

css {#css}

路径导入支持使用@import,eg: @import './base.css';
支持CSS Modules用法可以参考
image-1691631019586

  • 支持css预处理器

由于 Vite 的目标仅为现代浏览器,因此建议使用原生 CSS 变量和实现 CSSWG 草案的 PostCSS 插件(例如 postcss-nesting)来编写简单的、符合未来标准的 CSS。
话虽如此,但 Vite 也同时提供了对 .scss, .sass, .less, .styl 和 .stylus 文件的内置支持。没有必要为它们安装特定的 Vite 插件,但必须安装相应的预处理器依赖:

# .scss and .sass
npm add -D sass

# .less
npm add -D less

# .styl and .stylus
npm add -D stylus
  • 另外,vite.config.ts中需要增加如下配置:
    css: {
      // css预处理器
      preprocessorOptions: {
        scss: {
          additionalData: '@import "@/assets/style/mixin.scss";@import "@/assets/style/variable.scss";' //引入全局scss样式
        }
      }
    }

你还可以通过在文件扩展名前加上 .module 来结合使用 CSS modules 和预处理器,例如 style.module.scss。(react项目中这样用过)

  • 禁用 CSS 注入页面

静态资源处理 {#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E5%A4%84%E7%90%86}

  • 引入静态资源的方式
import imgUrl from './img.png' 
document.getElementById('hero-img').src = imgUrl

例如,imgUrl 在开发时会是/img.png,在生产构建后会是 /assets/img.2d8efhg.png

  • 常见的图像、媒体和字体文件类型被自动检测为资源。你可以使用 assetsInclude 选项 扩展内部列表。
  • 较小的资源体积小于 assetsInlineLimit 选项值 则会被内联为 base64 data URL。
  • 默认情况下,TypeScript 不会将静态资源导入视为有效的模块。要解决这个问题,需要添加 vite/client。(使用vue脚手架初始化项目,这些都会帮你配好的)

public 目录
如果你有下列这些资源:

  • 不会被源码引用(例如 robots.txt)
  • 必须保持原有文件名(没有经过 hash)
  • ...或者你压根不想引入该资源,只是想得到其 URL。
  • 那么你可以将该资源放在指定的 public 目录中,它应位于你的项目根目录。该目录中的资源在开发时能直接通过 / 根路径访问到,并且打包时会被完整复制到目标目录的根目录下

目录默认是 /public,但可以通过 publicDir 选项 来配置。

请注意:

  • 引入 public 中的资源永远应该使用根绝对路径 ------ 举个例子,public/icon.png应该在源码中被引用为 /icon.png
  • public 中的资源不应该被 JavaScript 文件引用。

image-1691631259207

vite快有什么问题? {#vite%E5%BF%AB%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F}

当源码中有commonjs模块加载,那么将会出现模块加载失败的问题。通过依赖与构建的方式解决该问题。
例如axios库就有相关的issue

总结: {#%E6%80%BB%E7%BB%93%EF%BC%9A}

Webpack 和 Vite 存在以下显著区别

1. 开发服务器启动速度

  • Webpack:在开发阶段,它需要对整个项目的模块进行打包构建,这导致启动开发服务器的过程较为缓慢,特别是项目规模较大时,可能需要较长时间等待。
  • Vite:充分利用现代浏览器对 ES Modules 的原生支持,在开发时无需打包,直接按需加载模块,因此启动速度非常快,几乎是瞬时启动。

2. 热更新(HMR)性能

  • Webpack:当代码发生更改时,Webpack 可能需要重新打包和处理相关的模块及依赖,这可能导致热更新有一定的延迟,影响开发效率。
  • Vite:基于浏览器对 ES Modules 的支持,能够实现真正的按需热更新,只更新修改的模块,极大地提高了热更新的效率,几乎能立即反映出代码的更改。

3. 构建方式

  • Webpack:对所有模块进行打包处理,无论在开发还是生产环境。
  • Vite:在开发环境采用即时编译,生产环境则会进行预构建,将依赖进行打包处理,以优化性能。

4. 配置复杂度

  • Webpack:拥有丰富且复杂的配置选项,需要开发者深入了解各种插件和 loader 的使用,以满足不同的项目需求。
  • Vite:配置相对简单直观,更容易上手和理解。

比如说,对于一个简单的前端应用原型开发,Vite 凭借其快速的启动和高效的热更新能显著提升开发体验。但对于一些大型的、具有复杂构建需求的企业级应用,Webpack 凭借其强大的扩展性和丰富的生态可能更能胜任。

赞(1)
未经允许不得转载:工具盒子 » vite为什么比webpack快?