深入探究Vite与Webpack之间的异同

深入探究Vite与Webpack之间的异同

在现代 JavaScript 的动态特性中,必须牢记 “旧” 并不一定意味着 “过时”,而 “新” 也不一定意味着 “更好”。

选择正确技术的关键在于它是否符合您的项目需求。在考虑 JavaScript 模块打包程序时,这一原则引起了强烈反响。无论打包工具是经受了时间的考验,还是刚刚问世,每种打包工具都有其独特的优势和局限性。

本文将探讨两个重要的流行工具:Vite 和 Webpack。我们将根据这些打包工具的功能、区别、架构理念以及它们如何集成到开发人员生态系统中进行评估。

什么是 JavaScript 模块打包程序?

JavaScript 打包

JavaScript 打包

JavaScript 打包程序是网络开发中使用的一种工具,用于将多个 JavaScript 文件合并为一个文件,即打包文件。它通过减少网络应用程序所需的请求次数来简化 JavaScript 代码的管理,最终提高性能。

例如,假设有两个独立的 JavaScript 文件:module1.jsmodule2.jsmodule1.js 包含以下内容:

// module1.js
export const greet = (name) => {
console.log(`Hello, ${name}!`);
}

module2.js 包含:

// module2.js
export const farewell = (name) => {
console.log(`Goodbye, ${name}!`);
}

要将这些模块打包到一个文件中,可以使用 Rollup、Webpack 或 Parcel 等打包工具。例如,在项目目录下创建一个 index.js 文件,代码如下:

// index.js
import { greet } from './module1.js';
import { farewell } from './module2.js';
greet('Wbolt');
farewell('Server Troubles');

在使用 JavaScript 打包程序时,它会将 module1.jsmodule2.jsindex.js 组合成一个为您的网络应用程序量身定制的优化打包程序。

虽然现代网络浏览器支持 ES 模块HTTP/2 等技术,从而解决了请求开销问题,但 JavaScript 打包程序对于一系列代码增强仍然不可或缺。它们可以执行基本的代码转换,包括最小化、转码和优化。

此外,JavaScript 模块打包程序还能确保不同浏览器之间的兼容性。无论用户选择何种网络浏览器,它们都能帮助解决浏览器特有的问题,确保用户获得一致的体验。

这种打包过程不仅能加快网络应用程序的加载速度,还能确保高效的性能,尤其是在生产环境中。现在,您已经了解了 JavaScript 打包程序及其在网络开发中的作用,让我们把重点转移到 Vite 和 Webpack 上。

Vite 和 Webpack:简介和概述

很明显,Vite 和 Webpack 在快速发展的现代网络开发领域处于领先地位,在这一领域,资源管理和优化打包至关重要。不过,在进行详细比较之前,让我们先快速了解一下这两个打包程序,并了解它们的优势所在。

Vite:Swift 和按需开发

Vite 的发音是 “veet”,它改变了网络开发人员的游戏规则,将速度和效率放在首位。让 Vite 脱颖而出的是它的按需打包方法。Vite 不预先打包所有代码和资产,而是利用现代浏览器中的本地 ES 模块,在开发过程中直接向浏览器提供代码。这使得热模块替换(HMR)几乎立竿见影,减少了冷启动时间。

Vite 的开发服务器采用这种按需方法,使开发人员无需重新编译即可快速查看更改。它还使用 Rollup,实现高效的生产构建。因此,Vite 的开发速度快如闪电,生产性能稳定可靠。

Webpack:组织有序、适应性强

Webpack 是现代网络开发的基石,自 2012 年以来一直在稳步发展。Webpack 的优点在于它如何组织网站组件。它通过将代码组织成模块来优化加载时间和用户体验。

Webpack 的适应性是一个显著优势。开发人员可以为简单或复杂的任务定制项目。它使开发人员能够定制工作流程,并精确地构建流程。

Vite 和 Webpack 的异同

既然我们已经掌握了 Vite 和 Webpack 的基本概念,下面就让我们来详细探讨它们的异同。在分析这两个打包程序时,我们将从各个方面进行研究,以全面了解它们之间的比较以及各自的优势所在。

  1. 构建与哲学
  2. 受欢迎程度、社区和生态系统
  3. 配置和易用性
  4. 开发服务器
  5. 构建时间和软件包大小
  6. 构建优化
  7. 静态资产处理
  8. 静态网站支持
  9. 服务器端渲染支持
  10. JSON 支持
  11. Vue.js 和 JSX 支持
  12. TypeScript 支持
  13. 全局导入支持
  14. Web Workers 支持
  15. 库开发能力
  16. 浏览器兼容性

1. 构建与哲学

这两种打包程序都从独特的角度构建和优化网络应用程序。它们的共同点是都采用了插件方式,允许社区创建更多有益的插件来扩展其功能,从而使它们成为开发人员的多功能工具。

Vite 的核心理念是精简和可扩展性。它坚持极简策略,专注于最常见的网络应用程序开发模式。这种方法确保了项目的长期可维护性。

Vite 依靠基于卷积的插件系统,通过外部插件实现功能,防止核心臃肿。这有助于精简核心,并鼓励维护良好的插件生态系统蓬勃发展。此外,Vite 还积极与 Rollup 项目合作,以保持兼容性和共享的插件生态系统。

Webpack 为开发人员提供了自定义功能,使他们能够根据从基本任务到复杂工作的具体需求定制项目。它可以灵活配置构建过程的方方面面,是寻求个性化开发体验的开发人员的首选。

此外,Webpack 引入了模块化方法,类似于为网络项目组装乐高积木。对于 Webpack 来说,代码库中的所有内容都是一个模块,它可以通过多种方式表达其依赖关系。以下是几个例子:

  1. ES2015 import 语句。
  2. CommonJS require() 语句。
  3. AMD definerequire 语句
  4. css/sass/less 文件中的 @import 语句。
  5. 样式表 url() 或 HTML <img src=""> 文件中的图片 URL。

Vite 哲学

Vite 精益和可扩展的架构理念在其构建网络应用程序的方法中体现得淋漓尽致。假设您正在开发一个网络应用程序,并希望加入 ES 模块等现代 JavaScript 功能。有了 Vite,您可以毫不费力地做到这一点。下面是一个简化的示例:

// app.js
import { greet } from './utilities.js';
const worker = new Worker(new URL('./worker.js', import.meta.url));
// Simulate a calculation in the web worker
worker.postMessage({ input: 42 });
worker.onmessage = (e) => {
const result = e.data.result;
console.log(`Result from the web worker: ${result}`);
};
const message = greet('Hello, Vite!');
console.log(message);

在这个代码片段中,Vite 使用了 ES 模块,它能毫不费力地即时打包代码,避免了开发过程中耗时的打包步骤。这种模块化方法可以有效管理依赖关系,创建可维护的代码库。这体现了 Vite 对极简主义和开发人员友好体验的承诺。

Webpack 哲学

Webpack 的模块化理念对大型项目尤其有益。想象一下,您正在用各种 JavaScript 模块构建一个大型网络应用程序。有了 Webpack,您就可以无缝地组合这些模块,从而提高可读性、可维护性和网站加载时间。下面是一个简化的示例:

// webpack.config.js
const path = require('path');
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
],
},
};

在本示例中,Webpack 可让您配置构建流程、优化代码并高效处理资产。通过将项目组织成模块并使用 Babel 等加载器,您可以编写简洁的模块化代码,从而改善用户体验。这表明 Webpack 致力于提供定制化和灵活性,确保开发人员可以根据特定需求定制自己的项目。

虽然 Vite 和 Webpack 的架构理念各不相同,但它们都致力于推动现代网络开发的发展。Vite 专注于现代编码模式,推广源代码的 ECMAScript 模块(ESM),并鼓励采用现代标准,如 Web Worker 的新 Worker 语法。

另一方面,Webpack 是为了应对 Node.js 和 CommonJS 带来的挑战而发展起来的,推动了模块在网络开发中的应用。Webpack 的自动依赖关系收集和性能改进确保了开发人员的无缝体验。

2. 受欢迎程度、社区和生态系统

Vite 和 Webpack 有着不同的时间线,这决定了它们的受欢迎程度和社区。

Vite 和 Webpack 在过去 5 年的谷歌趋势对比

Vite 和 Webpack 在过去 5 年的谷歌趋势对比。

Vite 才诞生不到 5 年,于 2020 年首次亮相。尽管诞生时间相对较短,但 Vite 已迅速获得关注,成为现代网络开发领域的一个有前途的参与者。

相比之下,Webpack 成立于 2012 年,起步较早。它在业界的时间使其发展出一个成熟的生态系统和一个强大的社区。

过去 5 年中 Vite 和 Webpack 在 npmtrends 上的对比

过去 5 年中 Vite 和 Webpack 在 npmtrends 上的对比。

上图来自 npmtrends,展示了 Vite 和 Webpack 的下载量对比。它清楚地表明,Webpack 在下载次数方面始终保持着突出的地位,这凸显了它在开发者社区中的长期存在和广泛使用。

Vite 和 Webpack 获得星星数的历史数据对比

Vite 和 Webpack 获得星星数的历史数据对比。

当我们使用 star-history(衡量受欢迎程度和社区支持度的标准)查看 GitHub 星星数据时,我们发现 Vite 拥有令人印象深刻的 60,318 个星星,而 Webpack 则拥有 63,598 个星星,保持着强大的影响力。这些星星数量反映了这两个项目的认可度和活跃度。Vite 的快速发展和 Webpack 的持续流行使它们成为网络开发领域的宝贵财富。

3. 配置和易用性

Vite 和 Webpack 都提供了大量配置选项,可根据您的特定需求量身定制打包包。不过,它们之间也有明显的区别,值得您注意。让我们来探讨一下这两种工具的配置和易用性。

Vite 的精简配置

Vite 与众不同之处在于其零配置理念,旨在简化您的网站开发过程。这意味着您只需花费最少的精力就能创建一个基本的 Vue 3 组件库。下面是这样一个项目的简单 Vite 配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})

在上述示例中,我们只导入并安装了 Vite 的 Vue.js 官方插件。Vite 的神奇之处在于它能自动检测大多数项目的正确设置。

Webpack 配置的复杂性

另一方面,Webpack 往往需要更详细的配置。虽然在最近的版本中,Webpack 已经趋向于零配置方法,但它并不像 Vite 那样自动。对于 Vue 3,Webpack 的基本设置可能如下所示:

const webpack = require('webpack');
const path = require('path');
const { HotModuleReplacementPlugin } = require('webpack');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /.vue$/,
use: {
loader: 'vue-loader',
},
},
{
test: /.css$/,
use: ['vue-style-loader', 'css-loader'],
},
],
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
},
},
plugins: [
new HotModuleReplacementPlugin(),
new VueLoaderPlugin(),
]
};

与 Vite 相比,Webpack 的配置需要更多的手动设置。其复杂性包括指定入口和输出路径、为不同文件类型配置加载器,以及为特定功能设置插件。下面让我们对配置的每个部分进行分解,并指出其中的复杂之处:

  • Entry 和 Outputentry 指定应用程序的入口点,即 Webpack 开始打包的位置。在本例中,它被设置为 ./src/main.js,假设应用程序的主 JavaScript 文件在 src 目录中,而 output 则定义了打包文件的保存位置。输出路径使用 path.resolve 解析,生成的打包文件命名为 bundle.js,并保存在 build 目录中。
  • 模块规则:module.rules 部分定义了如何处理不同类型的文件。在本例中,有针对 JavaScript 文件(用于转译的 babel-loader )、Vue 单文件组件( vue-loader )和 CSS 文件(用于处理样式的 vue-style-loadercss-loader)的规则。
  • 别名配置resolve.alias 部分定义了模块导入的别名。在本例中,它将 Vue 的别名配置为 vue/dist/vue.js
  • 插件:插件部分包括支持热模块替换的 HotModuleReplacementPlugin,该功能可让你在开发过程中无需重新加载整个页面即可查看更改,而 VueLoaderPlugin 则是处理 Vue 单文件组件所必需的。

最后,Vite 在易用性方面表现突出,提供了简化的设置和精简的开发体验。它的配置要求最低,并使用本地 ES 模块,因此非常适合初学者和快速开发。

相比之下,Webpack 广泛的可配置性虽然有利于复杂的项目,但也会给新手开发者带来挑战。复杂的设置和维护会拖慢开发速度,尤其是对于小型项目而言。

4. 开发服务器

开发服务器在开发人员的工作流程中起着至关重要的作用,影响着效率和生产力。让我们对 Vite 和 Webpack 进行比较,评估它们的开发服务器性能和可用性,从而为您的网站开发项目找到最合适的工具。

服务器配置

Vite 凭借其内置的、开箱即用的开发服务器脱颖而出,通常无需进行大量配置。

相比之下,Webpack 具有灵活性,但需要额外的设置。开发人员可以选择 Webpack 的观察模式webpack-dev-serverwebpack-dev-middleware 等选项,以便在变更时自动编译代码。不过,通常需要对这些选项进行配置和微调。

冷启动速度

传统的基于打包程序的设置涉及急切抓取,需要在提供服务前构建整个应用程序,从而导致明显的延迟,尤其是在复杂的项目中。

Vite 从根本上改变了冷启动方式,大大缩短了初始化时间:

esbuild打包速度

Esbuild 使用默认设置从头开始制作了一个包含 10 份 three.js 库的生产打包。(图片来源:Esbuild

(1)高效的依赖关系处理:Vite 利用基于 Go 语言的高性能打包程序 esbuild 来预打包依赖项,包括纯 JavaScript 和大型模块。作为预打包流程的一部分,Vite 通过将 ESM 依赖项与众多内部模块合并为一个模块来优化性能。例如,lodash-es 包含 600 多个内部模块。例如,lodash-es 包含 600 多个内部模块。使用传统方法导入 debounce 这样的函数时,会触发 600 多个 HTTP 请求。Vite 的解决方案是将 lodash-es 预先打包成一个模块,从而将 HTTP 请求减少到一个。请求的大幅减少大大提高了开发服务器的页面加载速度。

基于 ESM 的开发服务器图

基于 ESM 的开发服务器图 (图片来源:Vite

(2)按需加载源代码:Vite 利用本地 ES 模块提供源代码,最大限度地减少服务器负载和延迟。源代码转换和服务在浏览器请求时进行,从而提高了效率,减少了等待时间。

基于打包的开发服务器图

基于打包的开发服务器图。(图片来源:Vite

另一方面,Webpack 采用基于打包的方法,预先打包源代码和依赖项,从而延长了开发过程中的服务器启动时间。与 Vite 的高效初始化相比,Webpack 的服务器设置时间本来就更长。

不过,当用户导航到需要额外数据、CSS 和资产的路由时,Vite 的按需加载方法可能会带来轻微的延迟。如果这些资源需要进一步的打包步骤,这种延迟就尤为明显。相反,Webpack 的策略能确保所有网站数据可用,从而加快浏览器导航到开发服务器内新网页的速度。

HMR(热模块替换)

Vite 采用 HMR 而非本地 ESM,通过将一些打包工作卸载到浏览器来减少服务器负载和延迟。这可确保快速更新,而无需重新加载整个页面,这对开发过程中的实时反馈至关重要。

Webpack 也支持 HMR,可在开发过程中实现实时更新并保留应用程序状态。不过,利用本地 ES 模块的潜在限制可能会导致更高的服务器负载和延迟。

缓存性能

缓存对于提高网络应用性能、通过重复使用存储资产来减少负载和构建时间至关重要。

Vite 中的缓存文件系统缓存管理,根据 package.jsonlockfilesvite.config.js 中的变化更新依赖关系。它通过缓存已解决的依赖请求来优化页面重载。

Webpack 也使用文件系统缓存,在监视模式下清除修改过的文件,在非监视模式下每次编译前清除缓存,需要自定义配置以优化缓存。

在开发服务器的比较中,Vite 和 Webpack 提供了不同的开发服务器方法:

  • Vite 提供了开箱即用的开发服务器,最大限度地减少了配置开销。
  • Webpack 配置灵活,但需要额外设置。
  • Vite 在冷启动速度和快速更改代码的 HMR 方面表现出色
  • 由于预先打包了网站数据,Webpack 在浏览器导航速度方面表现更好。
  • 两者都支持 HMR,但有不同的模块服务机制。
  • Vite 可无缝管理本地和浏览器缓存,而 Webpack 则需要自定义配置。

5. 构建时间和软件包大小

现在,让我们比较一下 Vite 和 Webpack 的构建时间和打包包大小(考虑到开发构建、开发服务器期间的热更改和生产构建)。

我们的测试环境包括:

  • 在配备苹果 M1 芯片和 8 核 GPU 的 MacBook Air 上运行测试。
  • 一个中等规模的 Vue 3 项目,包含 10 个组件,使用 Vuex 进行状态管理,使用 Vue Router 进行路由。
  • 包含样式表(CSS/SASS)、图片和字体等资产,以及适量的依赖关系。

我们先来比较一下打包时间:

Vite [v4.4.11] Webpack [v5.89.0]
开发人员首次构建 376ms 6s
热更新 立即 1.5s
最终打包 2s 11s

在打包速度方面,Vite 明显胜出,大大缩短了构建时间。虽然 Webpack 提供了可配置性和强大的开发工具,但还是落后于 Vite。

Vite [v4.4.11] (KB) Webpack [v5.89.0] (KB)
最终打包文件大小 866kb 934kb

这些数据基于一个中等规模的 Vue.js 应用程序,其依赖项数量适中。实际的打包包大小会因项目复杂性、依赖关系和优化技术的不同而变化。

Vite 的打包大小较小,这是因为它使用 esbuild 和本地 ES 模块进行了高效的预打包。

Webpack 的打包包大小可以通过各种配置选项和插件进行优化,但由于其打包过程非常全面,通常会产生较大的打包包。

6. 构建优化

说到优化现代网络开发中的构建流程,Vite 和 Webpack 提供了不同的方法,每种方法都有自己的特性和功能。让我们通过探讨 Vite 和 Webpack 之间的主要区别来深入了解构建优化。

生成预加载指令

Vite 会自动为入口块生成 <link> 指令,并在构建的 HTML 中直接导入。这样就能根据需要有效地预载模块,从而缩短加载时间。

因此,在检查页面时,可能会出现以下情况:

<!-- Vite - Module Preloading -->
<link rel="modulepreload" href="/module-a.js">

Webpack 本身并不支持浏览器对资源的提示。但从 Webpack v4.6.0 开始,它支持预取和预加载。在声明导入时使用内联指令可以让 Webpack 输出资源提示,为浏览器提供有关何时加载导入文件的信息。例如

import(/* webpackPreload: true */ '/module-a.js');

这将输出:

<!-- Webpack - Manual Module Preloading -->
<link rel="preload" as="script" href="/module-a.js">

CSS 代码拆分

Vite 以其精简的 CSS 代码分割方法脱颖而出。它能自动提取模块在异步分块中使用的 CSS,并生成单独的文件。这意味着在加载相关的异步块时,只需通过 <link> 标签加载必要的 CSS。

值得注意的是,Vite 可确保在加载 CSS 后才对异步分块进行评估,从而防止出现无样式内容 Flash (FOUC)。由于该功能已预先配置,因此您可以继续导入 CSS 文件,无需任何额外步骤:

import './main.css';

Webpack 提供了灵活性,但在拆分 CSS 代码时需要更多配置。它允许开发人员使用各种插件和配置选项(如 mini-css-extract-plugin )来拆分 CSS。

// Webpack - CSS Code Splitting
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

代码拆分和分块加载

代码分割是一种基本技术,用于将代码分割成更小、更易于管理的片段,只在需要时加载所需的内容。这种做法大大缩短了初始加载时间,节约了资源。

(1)Vite 的分块方法

在某些情况下,Vite 会使用 Rollup 自动将代码分割成独立的代码块,如动态加载或多个入口点,也有一种方法可以通过 output.manualChunks 选项明确告诉 Rollup 哪些模块要分割成独立的代码块。

除了 Vite 预配置的代码分割功能外,Vite 还支持使用变量的动态导入

const module = await import(`./dir/${wbolt}.js`)

Vite 还允许开发人员使用官方的 splitVendorChunkPlugin() 来分割 vendor 块:

import { splitVendorChunkPlugin } from 'vite'
export default defineConfig({
plugins: [splitVendorChunkPlugin()],
})

在动态导入和代码拆分的过程中,代码通常会结构化为模块或块,其中一些模块会在网络应用程序的不同部分之间共享。Vite 可以识别常见的模块,并优化加载过程。为了更好地理解这一点,让我们看看 Vite 官方文档中的示例。

显示两个异步分块所需的普通分块的图表

显示两个异步分块所需的普通分块的图表(图片来源:Vite

在没有优化的情况下,当用户打开网络应用程序的某个部分(我们称之为依赖于共享代码 “Common Chunk C” 的 Section A)时,浏览器会首先获取 Section A,在解析 Section A 时,浏览器会意识到它需要 “Common Chunk C“:

Entry (Section A) ---> async chunk A ---> common chunk C

另一方面,Vite 采用了一种名为 “异步大块加载优化“(Async Chunk Loading Optimization)的复杂功能。它不会等待浏览器发现自己的需求,而是主动做好准备。当用户请求 Section A 时,Vite 会同时发送 Section A 和 Common Chunk C。这种并行获取所需数据块的方式大大加快了加载过程:

Entry (Section A) ---> (async chunk A + common chunk C)

然而,问题并不止于此。Common Chunk C 可能有自己的依赖关系,在非优化方案中可能会造成更多的往返。Vite 不会忽视这一点。它会严格分析这些依赖关系,确保一次性高效加载所需的所有内容(无论其深度如何)。这就消除了额外网络往返的必要性,保证了网络应用程序的高度响应性。

Vite 的异步分块加载方法通过主动获取和并行提供所有必要的代码块来优化加载过程。这样就避免了额外的网络往返,从而带来更快的网络体验。这就好比为浏览器提供了一个精心准备的旅行路线,确保浏览器能接收到所有必要的资源,而不会出现不必要的延迟。

(2)Webpack 的代码拆分方法

对于 Webpack,有三种通用的代码拆分技术:

  1. Entry points:这是分割一段代码最简单的方法。我们只需在配置文件中定义一个新的入口点,Webpack 就会将其添加为一个单独的块:
    const path = require('path');
    module.exports = {
    mode: 'development',
    entry: {
    index: './src/index.js',
    another: './src/separate-module.js',
    },
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    },
    };

    不过,这种方法也有局限性。如果模块以不同的入口块导入,它们最终会同时出现在两个打包包中,导致代码重复。此外,在需要拆分核心应用程序逻辑时,这种方法的适应性也不强。

  2. Prevent duplication:另一种方法是使用 entry 依赖关系或 SplitChunksPlugin 来分割代码块,这有助于减少冗余。下面举例说明如何使用 entry 依赖关系配置代码分割:
    const path = require('path');
    module.exports = {
    mode: 'development',
    entry: {
    index: {
    import: './src/index.js',
    dependOn: 'shared',
    },
    another: {
    import: './src/another-module.js',
    dependOn: 'shared',
    },
    shared: 'lodash',
    },
    output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
    runtimeChunk: 'single',
    },
    };
  3. Dynamic imports:最后,Webpack 支持动态导入,这是一种按需加载代码的重要功能。它使用的语法符合 ECMAScript 关于动态导入的提议。这种方法更加灵活和细化,适用于各种代码拆分场景。
    const { default: _ } = await import('lodash');

    我们还可以使用 Webpack 的 “魔法注释“(Magic Comments)来设置分块的名称、懒加载分块、指定模块导出并设置获取优先级:

    import(
    /* webpackChunkName: "my-chunk-name" */
    /* webpackMode: "lazy" */
    /* webpackExports: ["default", "named"] */
    /* webpackFetchPriority: "high" */
    'module'
    );

Tree-Shaking

树形抖动(Tree-Shaking)是一项重要的优化技术,Vite 和 Webpack 都使用它来缩减 JavaScript 包的大小。

Vite 利用 Rollup,它不仅能使用 ES 模块,还能静态分析您导入的代码。这意味着 Vite 可以排除模块中不使用的部分,从而缩小打包包的大小。例如,如果您的模块有多个函数,但只使用了其中一个,Vite 就会在打包包中只包含该函数。下面是一个简单的例子:

  • 如果不使用 ES 模块,要从 ./utils.js 导入 ajax,就需要导入整个文件。
    const utils = require('./utils');
    const query = 'Wbolt';
    // Use the 'ajax' method of the 'utils' object
    utils.ajax(`https://api.example.com?search=${query}`).then(handleResponse);
  • 另一方面,使用 ES 模块可以只导入所需的内容,从而使库和应用程序更轻、更快、更复杂。由于 Vite 使用显式 import 和 export 语句,因此它可以执行高效的树状结构转换,而无需完全依赖自动精简器来检测未使用的代码。
    import { ajax } from './utils';
    const query = 'Wbolt';
    // Call the 'ajax' function
    ajax(`https://api.example.com?search=${query}`).then(handleResponse);

最后,对于 Vite,我们可以使用 Rollup 预配置的树状抖动选项

Webpack 也支持树状结构,但其机制不同。它会分析代码中的依赖关系,并在打包过程中删除未使用的部分。虽然这种方法很有效,但可能不如 Vite 的方法彻底,尤其是在处理大型模块或库时。

此外,根据 Webpack 的文档。我们需要将文件标记为 side-effect free,以保证不会移除任何在生产中依赖于该文件的代码。

实现这一点的方法是使用 sideEffects package.json 属性:

{
"name": "wbolt-app",
"sideEffects": false
}

值得注意的是,在 Vite 的 Rollup 选项中也有类似的配置选项来定义 side effects

7. 静态资产处理

静态资产(如图像、字体和其他文件)是网页开发不可或缺的一部分。Vite 和 Webpack 处理这些资产的方式各不相同,各有优势和优化之处。

Vite 的资产处理

Vite 处理静态资产的方法精简高效。导入静态资产时,Vite 会在提供时返回已解析的公共 URL。例如,当您导入这样一张图片时:

import wboltImage from './wbolt-image.png';

在开发过程中, imgUrl 将解析为 /img.png。在生产构建过程中,它将变成类似 /assets/img.2d8efhg.png,为缓存和性能进行了优化。

Vite 可以使用绝对公共路径或相对路径来处理这些导入,从而灵活地满足项目需求。Vite 还能无缝处理 CSS 中的 URL 引用。

此外,如果您在 Vue 单文件组件(SFC)中使用 Vite,模板中的资产引用会自动转换为导入,从而简化您的开发工作流程。

Vite 的资产处理功能更进一步,它可以检测常见的图像、媒体和字体文件类型,并将其视为资产。这些资产将作为构建资产图的一部分,获得散列文件名,并可由插件进行优化处理。

Webpack 的资产处理

另一方面,Webpack 在处理静态资产时采用了不同的方法。使用 Webpack,你可以像平常一样导入资产:

import wboltImage from './wbolt-image.png';

Webpack 在处理该导入时,会将图片添加到输出目录中,并为您提供图片的最终 URL。这样就能轻松处理资产,还能在 CSS 中使用 url('./my-image.png') 进行处理。Webpack 的 css-loader 会将其识别为本地文件,并将路径替换为输出目录中的最终图片 URL。当使用 html-loader 来处理 <img src="./wbolt-image.png" /> 时也是如此。

第 5 版中引入的 Webpack 资产模块可以处理各种类型的资产,而不仅仅是图片。例如,您可以配置 Webpack 来处理字体文件:

module.exports = {
module: {
rules: [
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
},
],
},
};

该配置允许您通过 @font-face 声明在项目中加入字体。

8. 静态网站支持

静态网站具有众多优势,如加载时间快、安全性高、托管简单等。静态网站由 HTMLCSSJavaScript 组成,可提供简化的用户体验和高效的内容交付。Vite 和 Webpack 都能帮助开发人员生成性能良好的静态网站,但效率却不尽相同。

Vite 静态网站生成方法

Vite 提供专门的静态网站部署说明,利用其简化的开发和部署方法,特别适合静态网站。

Vite 的另一个亮点是它有一个 preview 脚本,可以帮助开发人员在本地启动生产构建,查看应用程序的最终运行结果。这一功能允许开发人员在将生产构建部署到实时服务器之前对其进行测试和预览。

不过,需要注意的是,Vite 的 preview 脚本是用于在本地预览构建,而不是用作生产服务器。这意味着它是开发人员在部署前测试应用程序的好工具,但不适合托管实时生产网站。

{
"scripts": {
"preview": "vite preview --port 3000"
}
}

值得强调的是 VitePress,它是 Vite 生态系统中最强大的工具之一。VitePress 是一种静态网站生成器(SSG),用于快速生成以内容为重点的网站。VitePress 采用基于 Markdown 的源文本,应用一个主题,然后输出静态 HTML 页面,这些页面可以在云服务器上快速部署。

Webpack 的静态网站生成方法

虽然 Webpack 并不是专门为生成静态网站而设计的,但它可以通过各种插件和配置来创建静态网站。不过,与 Vite 相比,Webpack 的过程通常更为复杂,而且不够精简。Webpack 的主要重点在于打包和优化 JavaScript 模块,这使其成为构建复杂网络应用程序的强大工具。

9. 服务器端渲染支持

服务器端渲染(SSR)是一种网络开发技术,它在服务器上渲染网页,并将完全渲染后的 HTML 发送到客户端浏览器。让我们比较一下这两种打包程序对 SSR 的支持:

  • Vite:Vite 支持服务器端渲染,为需要 SSR 的应用程序提供了一种简化的方法。有了 Vite,可以在 Node.js 中运行同一应用程序、将其预渲染为 HTML 并随后在客户端进行水合的前端框架就可以无缝集成。这使得 Vite 成为需要 SSR 功能的应用程序的绝佳选择,为开发人员提供了优化服务器渲染应用程序所需的工具。
  • Webpack:Webpack 也可用于服务器端渲染。不过,使用 Webpack 实现 SSR 往往更为复杂,需要对配置和设置有更深入的了解。与 Vite 提供的更精简的方法相比,开发人员可能需要投入更多时间来使用 Webpack 设置 SSR。

10. JSON 支持

Vite 和 Webpack 都支持导入 JSON 文件。除 Vite 外,还支持以 JSON 命名的导入,以帮助实现 tree-shaking。

// import an object
import json from './example.json'
// import a root field as named exports.
import { test } from './example.json'

11. Vue.js 和 JSX 支持

著名的 JavaScript 框架 Vue.js 遵循 SFC(单文件组件)语法,简化了创建用户界面的过程。相比之下,JSX 是一种 JavaScript 语法扩展,主要用于 React,使开发人员能够使用类似 HTML 的标记和元素来定义用户界面结构。

Vite 提供一流的 Vue.js 支持,其官方插件可将 Vite 与 Vue 无缝集成。由于采用了 esbuild 转译技术,它还能处理开箱即用的 JSX 文件( .jsx.tsx )。Vue.js 用户可以使用 @vitejs/plugin-vue-jsx 插件,该插件专为 Vue 3 量身定制,提供 HMR、全局组件解析、指令和插槽等功能。

如果 JSX 与 ReactPreact 等其他框架一起使用,Vite 允许通过 esbuild 选项配置自定义的 jsxFactoryjsxFragment。这种灵活性对于需要定制 JSX 的项目来说非常有价值。

// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment',
},
})

另一方面,Webpack 缺乏对 Vue.js 或任何其他特定库或框架的本地支持。开发人员需要安装相关的加载器和依赖项,才能为现代 JavaScript 框架建立项目,这就使得开发过程变得更加手动和复杂。

12. TypeScript 支持

Vite 为 TypeScript 提供本机支持,可将 .ts 文件无缝整合到项目中。它使用 esbuild 转译器在开发过程中快速转换代码。Vite 专注于转译,而非类型检查。它希望你的集成开发环境和构建过程能处理类型检查。

Webpack 缺乏对 TypeScript 的原生支持,因此开发人员需要使用 typescript 编译器和 ts-loader 手动设置 TypeScript。这需要配置 tsconfig.json,以指定 TypeScript 选项。设置完成后,Webpack 会使用 ts-loader 来编译 TypeScript 代码。虽然这引入了额外的配置步骤,但它提供了灵活性并与其他 Webpack 功能兼容。

13. 全局导入支持

Vite 支持全局导入。该功能用于通过 import.meta.glob 函数从文件系统导入多个模块:

const modules = import.meta.glob('./wbolt/*.js')

这将输出:

const modules = {
'./wbolt/isCool.js': () => import('./wbolt/isCool.js'),
'./wbolt/isAwesome.js': () => import('./wbolt/isAwesome.js'),
'./wbolt/isFun.js': () => import('./wbolt/isFun.js'),
}

Vite 还支持 Glob Import As,使用 import.meta.glob 将文件导入字符串。下面是一个代码示例:

const modules = import.meta.glob('./wbolt/*.js', { as: 'raw', eager: true })

它将变成这个样子:

const modules = {
'./wbolt/rocks.js': 'export default "rocks"\n',
'./wbolt/rules.js': 'export default "rules"\n',
}

{ as: 'url' } 也支持将资产加载为 URL。

Webpack 需要额外的插件,如 webpack-import-glob-loaderglob-import-loader 来执行 Glob Imports。它们将扩展此功能:

import modules from "./test/**/*.js";

为:

import * as moduleOne from "./foo/1.js";
import * as moduleTwo from "./test/bar/2.js";
import * as moduleThree from "./test/bar/3.js";
var modules = [moduleOne, moduleTwo, moduleThree];

14. Web Workers 支持

Web Worker 是在后台运行繁重任务而不冻结主网页的关键。下面介绍 Vite 和 Webpack 如何处理它们:

Vite 可让您轻松使用 Web Worker。您可以创建一个单独的 Web Worker 文件,将其导入主代码并与之通信。Vite 提供了两种在主代码中导入 Worker 的方法:

1. 新 Worker() 和新 SharedWorker() 构造函数:

const worker = new Worker(new URL('./worker.js', import.meta.url));
// OR
const worker = new SharedWorker(new URL('./worker.js', import.meta.url));

2. 通过附加 ?worker?sharedworker 直接导入:

import MyWorker from './worker?worker';
const worker = new MyWorker();
myWorker.postMessage('Hello from the main thread!');

Webpack 还支持 Web Worker,而且从 Webpack 5 开始,我们就不需要使用加载器来使用 Worker 了。

Const worker = new Worker(new URL('./worker.js', import.meta.url));

15. 库开发能力

库和框架使开发人员能够创建和共享工具,从而加快网络应用程序的开发。Vite 和 Webpack 都提供了强大的解决方案。

Vite 通过其专门的库模式将库开发提升到一个新的水平,简化了创建以浏览器为中心的库的过程。此外,Vite 还能灵活地将特定的依赖关系(如 Vue 或 React)外部化,而您可能不希望在库打包中包含这些依赖关系。

另一方面,Webpack 是一个多功能的打包工具,也能满足库作者的需求。如果您使用 Webpack 创建 JavaScript 库,您可以对其进行配置,以满足您打包库的独特需求。它允许你定义如何打包库的代码,因此适合构建各种库。

16. 浏览器兼容性

Vite 优先使用现代浏览器,目标是那些支持本地 ES 模块的浏览器,如 Chrome 浏览器 >=87、Firefox 浏览器 >=78、Safari 浏览器 >=14 和 Edge 浏览器 >=88。
也可以从 es2015 开始,通过 build.target 设置自定义目标。通过 @vitejs/plugin-legacy 支持传统浏览器。

Webpack 支持所有兼容 ES5 的浏览器(不包括 IE8 及以下版本)。为适应旧版浏览器,需要对 import()require.ensure() 等功能进行多填充。

就浏览器兼容性而言,两者都很不错,但您的选择应取决于项目的目标受众及其浏览器功能。

小结

Vite 采用原生 ES 模块方法,开发速度快如闪电,更新迅速,并提供广泛的自定义选项。相反,Webpack 以其健壮性和广泛的生态系统而著称,在生产构建中表现出色,但需要更陡峭的学习曲线。

在 Vite 和 Webpack 之间做出选择时,应考虑项目需求和对复杂配置的熟悉程度。两者各有优势,请根据项目的具体要求进行选择。

评论留言