慢慢认识世界,慢慢更新自己。
大家好,我是柒八九。
由于,新公司的项目打包是用的Vite
,而之前的所参与的项目都是用Webpack
作为打包工具,原来对Vite
的了解,只是一个把玩工具,没有过多的深入了解。本着干一行,爱一行的职业态度。所以,就找了很多相关资料学习和研究。
以下的内容,都是基于本人对Vite
的个人见解。不一定对,随便看看
你能所学到的知识点
- vite 是个啥? 推荐阅读指数 ⭐️⭐️⭐️
- vite 打包阶段 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
- 打包阶段的插件执行顺序 推荐阅读指数 ⭐️⭐️⭐️⭐️⭐️
- Vite+React的项目打包优化(简单版) 推荐阅读指数 ⭐️⭐️⭐️⭐️
好了,天不早了,干点正事哇。
这里再多絮叨几句,下面的大部分内容,都是从Vite
打包阶段聊,针对HMR
一些内容,没有涉及。
vite 是个啥?
Vite
是一种现代化的前端构建工具,它的目标是提供一种快速、简单和易于使用的开发体验。Vite
使用了一些新的技术来实现快速的开发体验,这些技术包括ES模块
、即时编译
和热重载
。
ES模块
是一种新的JavaScript模块格式
,它是浏览器原生支持的。ES模块
提供了一种简单、可读、可扩展的方式来组织代码,同时还提供了静态分析和优化的机会。Vite
利用ES模块
的特性,将应用程序拆分成更小的代码块,使得应用程序的加载时间更快。
- 如果想了解更多的关于
ES模块
的相关概念,可以参考之前的文章。你真的了解ESM吗?
Vite
使用即时编译来实现更快的开发体验。
- 即时编译是指在代码更改时,
Vite
会立即编译代码并将其发送到浏览器中。 - 这意味着开发人员不需要等待编译过程完成,就可以看到更改后的效果。
- 这大大缩短了开发周期,并提高了开发效率。
- 最后,
Vite
还使用热重载技术。
- 热重载是指在开发过程中,如果更改了代码,应用程序将自动重新加载,而无需手动刷新页面。这使得开发人员能够更快地看到更改后的效果,并且不会丢失任何数据。
想了解更多关于Vite
的介绍,可以参考官网
vite 打包阶段
在讨论Vite时,通常关注的是其作为开发服务器
以及它如何在开发过程中
实现即时反馈。但是,在生产环境下,Vite
也会大放异彩。
下面,我们就一起来了解一下Vite
是如何在生产环境下,对你的项目代码进行打包/优化处理的。本文假定你已经对Vite
有一定的了解。如果第一次听说,你可以先移步到为什么选 Vite来了解相关的设计理念和解决哪些痛点。
用上帝视角看Vite如何处理资源
在一个 Vite
项目中,index.html
在项目最外层而不是在 public
文件夹内。这是有意而为之的:在本地开发阶段 Vite
是一个资源服务器,而 index.html
是该 Vite
项目的入口文件。
Vite
将index.html
视为源码和模块图的一部分。
这个HTML
文件用于引入网站资源信息。
- 通过设置
type="module"
的script
标签引入JS
资源(外部资源和内联脚本) - 通过设置
rel="stylesheet"
的link
引入外部样式
<!DOCTYPE html> <html> <head> <script type="module" src="/src/main.ts"></script> <script type="module"> // 内联 script </script> <link rel="stylesheet" href="/src/main.css"> <style> internal-style: {} </style> </head> <body> <div id="app"></div> </body> </html> 复制代码
Vite
接收HTML
文件,来查找每个加载的JS模块、内联脚本和CSS样式表。
JS源代码
通过Rollup
处理,解析和转换内部依赖项,生成一个不经常更新的vendor.js
(带有依赖项)和一个index.js
(应用程序的其余部分)。
- 这些源文件被转换成的带有
hash
值的文件,以实现强缓存。
- 任何在JS中被引用的
CSS
文件都会被打包到index.css
文件中
- 内部样式不会被处理。
script
或import
可以指向任何文件类型,只要Vite
知道如何转译它们即可。在上面的情况下,main.ts
文件需要在打包过程中转换为JS文件。在Vite
中,使用基于Go
的打包工具esbuild实现对应资源的转换。
在资源被转换后,也会生成一个新的资源地址,用它们替换原来的资源地址。并且Vite
执行import
的静态分析,为每个JS插入模块预加载标记(rel="modulepreload"
),使浏览器可以并行加载这些资源,从而避免加载瀑布效应。
<!DOCTYPE html> <html> <head> <script type="module" src="/assets/index.d93758c6.js"></script> <link rel="modulepreload" href="/assets/vendor.a9c538d6.js"> <link rel="stylesheet" href="/assets/index.3015a40c.css"> <style> internal-style: {} </style> </head> <body> <div id="app"></div> </body> </html> 复制代码
Vite
针对JS
和CSS
资源支持代码分割。当遇到动态导入时,会生成一个异步JS块和一个CSS块。
其他资源,如图像、视频、wasm
,可以使用相对路径导入。在生成其输出文件时,Vite
还会对这些文件进行hash
处理,并重写JS和CSS文件中的URL
并指向它们。
另一方面,public
文件夹中的资源会按原样复制到输出根目录,并允许用户通过绝对路径引用这些文件。
每个JS和CSS块都需要在生产中进行压缩。自从Vite 2.6.0以来,Vite
也使用esbuild
为两种语言执行压缩任务,以加快构建过程。
Rollup的二次封装
Vite
应用的打包过程
,是基于Rollup 的二次封装 。Vite
中可以直接使用现有的Rollup
插件,实现很多开箱即用的功能。
但是,需要注意一些与 Rollup
插件的兼容性问题,但大多数来自 Rollup
生态系统的插件都可以直接作为 Vite
插件工作。
Vite内部打包流程
下图展示了,Vite
打包的大体流程。 上面的流程主要分四个步骤
- 收集打包配置信息
- 确认打包后的资源路径
- 使用
rollup
打包处理- 资源输出
当你执行vite build
时,Vite CLI
被运行。运行命令是用cac实现的。该操作触发了 build
函数。
await build({ root, base, mode, config, logLevel, clearScreen, buildOptions }) 复制代码
build
又调用 doBuild
。实现逻辑如下。
async function doBuild(inlineConfig: InlineConfig = {}): RollupOutput{ // ①收集打包配置信息 const config = await resolveConfig(inlineConfig, 'build', 'production') // ②确认打包后的资源路径 const outDir = resolve(config.build.outDir) prepareOutDir(outDir, config) // ③打包处理 const bundle = await rollup.rollup({ input: resolve('index.html'), plugins: config.plugins }) // ④资源输出 return await bundle.write({ dir: outDir, format: 'es', exports: 'auto', sourcemap: config.build.sourcemap, entryFileNames: path.join(config.assetsDir, `[name].[hash].js`), chunkFileNames: path.join(config.assetsDir, `[name].[hash].js`), assetFileNames: path.join(config.assetsDir, `[name].[hash].[ext]`), manualChunks: createMoveToVendorChunkFn() }) } 复制代码
首先,调用resolveConfig
用于解析用户配置
、项目配置文件
和Vite默认值
来生成一个具体的ResolvedConfig
。
const config = await resolveConfig(inlineConfig, 'build', 'production') 复制代码
接下来,确认好输出目录,并在生成资产之前清空它。这个函数还将publicDir
的内容复制到项目dist文件夹
。
const outDir = resolve(config.build.outDir) prepareOutDir(outDir, config) 复制代码
然后,基于index.html
和config.plugins
创建了rollup
的bundle
对象。
const bundle = await rollup.rollup({ input: resolve('index.html'), plugins: config.plugins }) 复制代码
最后,bundle.write
被调用,用于生成输出目录中的资源信息。
return await bundle.write({ dir: outDir, format: 'es', exports: 'auto', sourcemap: config.build.sourcemap, entryFileNames: path.join(options.assetsDir, `[name].[hash].js`), chunkFileNames: path.join(options.assetsDir, `[name].[hash].js`), assetFileNames: path.join(options.assetsDir, `[name].[hash].[ext]`), manualChunks: createMoveToVendorChunkFn() }) 复制代码
createMoveToVendorChunkFn
函数定义了默认的分块策略,定义了JS
被打包后,何种资源被分配到index.js
和vendor.js
中。
具体实现如下:
function createMoveToVendorChunkFn() { return (id, { getModuleInfo }) => { if ( id.includes('node_modules') && !isCSSRequest(id) && staticImportedByEntry(id, getModuleInfo) ) { return 'vendor' } } } 复制代码
通过对Vite
的打包做了一个简单的分析,我们可以得知:
Vite
的构建过程,就是以Rollup
为基础,借助插件对资源的二次处理过程。
常见的插件
插件在开发阶段
和打包阶段
是通过resolvedPlugins
进行解析。Vite
在打包时
通过resolveBuildPlugins
插入额外的插件,以处理压缩和其他优化。
有一些关键插件。
vite:build-html
和vite:html-inline-proxy-plugin
用于处理HTML
,将JS
和CSS
替换为经Vite
优化过的对应资源。vite:css
和vite:css-post
用于处理CSS
和预处理器。vite:esbuild
用于为每个模块转换TypeScript
和JSX
。vite:asset
用于管理静态资源。vite:build-import-analysis
用于预加载优化
、支持全局导入和URL
重写。vite:esbuild-transpile
用于将chunks
转换为合适的目标和压缩对应资源。
还有一些插件是官方Rollup插件
alias
commonjs
rollup-plugin-dynamic-import-variables
打包阶段的插件执行顺序
就webpac
k来说,plugins
的作用在于强化其构建过程中,所遇到的一些工程化的问题,比如代码压缩,资源压缩等,所以vite
作为新时代的构建工具,理应当也具有插件系统来解决构建项目的整个生命周期中所遇到的工程化的问题,说白了,插件就是为了解决某一类型的问题而出现的一个或一种工具函数。比如lodash
,他被称之为一个库,也可以认作是一个插件。所以vite
会在不同的生命周期中调用不同的插件去达成不同的目的。
让我们深入了解每个插件的使用方式和职责权限。
一个 Vite
插件可以额外指定一个 enforce
属性来调整它的应用顺序。enforce
的值可以是pre
或 post
。解析后的插件将按照以下顺序排列:
Alias
- 带有
enforce: 'pre'
的用户插件(前置插件) Vite
核心插件- 没有
enforce
值的用户插件(常规插件) Vite
构建用的插件- 带有
enforce: 'post'
的用户插件(后置插件) Vite
后置构建插件(最小化,manifest
,报告)
Vite
构建运行的插件的执行顺序如下:
alias
- 带有
enforce: 'pre'
的用户插件(前置插件) vite:modulePreload
vite:resolve
vite:html-inline-proxy-plugin
vite:css
vite:esbuild
vite:json
vite:wasm
vite:worker
vite:asset
- 没有
enforce
值的用户插件(常规插件) vite:define
vite:css-post
vite:build-html
commonjs
vite:data-uri
rollup-plugin-dynamic-import-variables
vite:asset-import-meta-url
- 带有
enforce: 'post'
的用户插件(后置插件) vite:build-import-analysis
vite:esbuild-transpile
vite:terser
vite:manifest
vite:ssr-manifest
vite:reporter
1. alias
该插件,用于在打包时定义别名,与Webpack
中的resolve.alias
相同。
- 可以是一个对象
Record
- 或一个
{ find, replacement, customResolver }
的数组
Array<{ find: string | RegExp, replacement: string, customResolver?: ResolverFunction | ResolverObject }>
用以下方法配置Vite
resolve: { alias: { '@components': path.resolve(__dirname, 'src/components') } } 复制代码
可以让你从源码中的任何地方导入你想要导出的数据
import Button from '@components/Button.tsx' 复制代码
这个插件解析路径并将它们转译为真实的路径
import Button from '../../components/Button.tsx' 复制代码
2. 带有 enforce: 'pre'
的用户插件(前置插件)
这些是带有enforce: 'pre'
的插件。例如,@rollup/plugin-image
配置了该属性,它就在Vite
的内置插件之前运行。
import image from "@rollup/plugin-image" export default { plugins: [ { ...image(), enforce: 'pre', }, ] } 复制代码
3. vite:modulePreload
- 类型:
boolean
|{ polyfill?: boolean, resolveDependencies?: ResolveModulePreloadDependenciesFn }
- 默认值:
{ polyfill: true }
默认情况下,一个模块预加载polyfill
会被自动注入。该 polyfill
会自动注入到每个 index.html
入口的的代理模块中。
如果构建通过 build.rollupOptions.input
被配置为了使用非 HTML 入口的形式,那么必须要在你的自定义入口中手动引入该 polyfill
:
import 'vite/modulepreload-polyfill' 复制代码
polyfill 实现
<script> function processPreload () { const fetchOpts = {}; if (script.integrity) fetchOpts.integrity = script.integrity; if (script.referrerpolicy) fetchOpts.referrerPolicy = script.referrerpolicy; if (script.crossorigin === 'use-credentials') fetchOpts.credentials = 'include'; else if (script.crossorigin === 'anonymous') fetchOpts.credentials = 'omit'; else fetchOpts.credentials = 'same-origin'; fetch(link.href, fetchOpts) .then(res => res.ok && res.arrayBuffer()); } const links = document.querySelectorAll('link[rel=modulepreload]'); for (const link of links) processPreload(link); </script> 复制代码
该
polyfill
允许Vite
预加载模块以避免加载瀑布,支持非Chromium
浏览器。
4. vite:resolve
它使用Node解析算法来定位node_modules
中的第三方模块。它与官方的rollup
插件不同,因为需要对Vite
特定功能(SSR
和devServer
)进行特殊处理。
Node 文件定位
5. vite:html-inline-proxy-plugin
该插件将入口HTML文件
中的内联脚本作为单独的模块加载。这些脚本由vite:build-html
插件将其从HTML
中删除,并替换为一个type="module"
的script
。
6. vite:css
该插件与vite:css-post
插件一起使用来实现Vite的CSS功能。支持预处理器(postCSS
、sass
、less
),包括解析导入的URL。
7. vite:esbuild
类型: ESBuildOptions | false
ESBuildOptions
继承自 esbuild
转换选项。
最常见的用例是自定义 JSX:
export default defineConfig({ esbuild: { jsxFactory: 'h', jsxFragment: 'Fragment', }, }) 复制代码
默认情况下,esbuild
会被应用在 ts
、jsx
、tsx
文件。你可以通过 esbuild.include
和 esbuild.exclude
对要处理的文件类型进行配置。
此外,你还可以通过 esbuild.jsxInject
来自动为每一个被 esbuild
转换的文件注入 JSX helper。
export default defineConfig({ esbuild: { jsxInject: `import React from 'react'`, }, }) 复制代码
8. vite:json
处理JSON
文件的导入。
// 导入整个对象 import json from './example.json' // 导入一个根字段作为命名的出口, 便于tree shaking import { field } from './example.json' 复制代码
9. vite:wasm
此插件允许用户直接导入预编译的.wasm
文件。
预编译的 .wasm
文件可以通过 ?init
来导入。默认导出一个初始化函数,返回值为所导出 wasm
实例对象的 Promise
:
import init from './example.wasm?init' init().then((instance) => { instance.exports.test() }) 复制代码
在生产构建当中,体积小于 assetInlineLimit
的 .wasm
文件将会被内联为 base64
字符串。否则,它们将作为资源复制到 dist
目录中,并按需获取。
10. 'vite:worker'
通过构造器导入
一个 Web Worker
可以使用 new Worker()
和 new SharedWorker()
导入。与 worker
后缀相比,这种语法更接近于标准,是创建 worker
的 推荐 方式。
const worker = new Worker( new URL('./worker.js', import.meta.url) ) 复制代码
worker
构造函数会接受可以用来创建 “模块” worker
的选项:
const worker = new Worker( new URL('./worker.js', import.meta.url), { type: 'module',} ) 复制代码
带有查询后缀的导入
你可以在导入请求上添加 ?worker
或 ?sharedworker
查询参数来直接导入一个 web worker
脚本。默认导出会是一个自定义 worker
的构造函数:
import MyWorker from './worker?worker' const worker = new MyWorker() 复制代码
默认情况下,worker
脚本将在生产构建中编译成单独的 chunk。如果你想将 worker
内联为 base64
字符串,请添加 inline
查询参数:
import MyWorker from './worker?worker&inline' 复制代码
如果你想要以一个 URL
的形式读取该 worker
,请添加 url
这个 query
:
import MyWorker from './worker?worker&url' 复制代码
11. 'vite:asset'
该插件用于资源的处理。
将资源引入为 URL
引入一个静态资源会返回解析后的公共路径:
import imgUrl from './img.png' document.getElementById('hero-img').src = imgUrl 复制代码
例如,imgUrl
在开发时会是 /img.png
,在生产构建后会是 /assets/img.2d8efhg.png
。
- 常见的
图像
、媒体
和字体文件
类型被自动检测为资源。你
- 可以使用
assetsInclude
选项扩展内部列表。(当从HTML
引用它们或直接通过fetch
或XHR
请求它们时,它们将被插件转换管道排除在外。)
- 引用的资源作为构建资源图的一部分包括在内,将生成散列文件名,并可以由插件进行处理以进行优化。
- 较小的资源体积小于
assetsInlineLimit
选项值 则会被内联为base64 data URL
。
显式 URL 引入
未被包含在内部列表或 assetsInclude
中的资源,可以使用 ?url
后缀显式导入为一个 URL。
import workletURL from 'extra-scalloped-border/worklet.js?url' CSS.paintWorklet.addModule(workletURL) 复制代码
将资源引入为字符串
资源可以使用 ?raw
后缀声明作为字符串引入。
import shaderString from './shader.glsl?raw' 复制代码
public 目录
如果你有下列这些资源:
- 不会被源码引用(例如 robots.txt)
- 必须保持原有文件名(没有经过 hash)
- ...或者你压根不想引入该资源,只是想得到其 URL。
那么你可以将该资源放在指定的 public
目录中,它应位于你的项目根目录。该目录中的资源在开发时能直接通过 / 根路径访问到,并且打包时会被完整复制到目标目录的根目录下。
目录默认是 /public
,但可以通过 publicDir
选项 来配置。
请注意:
- 引入
public
中的资源永远应该使用根绝对路径
- 举个例子,
public/icon.png
应该在源码中被引用为/icon.png
。
public
中的资源不应该被JavaScript
文件引用。
12. 常规插件
没有 enforce
值的用户插件
13. 'vite:define'
定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换。
类型: Record
String
值会以原始表达式形式使用,所以如果定义了一个字符串常量,它需要被显式地打引号。(例如使用 JSON.stringify
)
define: { __APP_VERSION__: `JSON.stringify(${version})` } 复制代码
对于使用 TypeScript
的项目,还需要 env.d.ts
或 vite-env.d.ts
文件中添加类型声明,以获得类型检查以及代码提示。
// vite-env.d.ts declare const __APP_VERSION__: string 复制代码
14. vite:css-post
这个插件用esbuild
对CSS资源进行最小化。
css资源的URL占位符被解析为其最终的构建路径。
它还实现了CSS代码拆分。Vite
会自动地将一个异步 chunk
模块中使用到的 CSS
代码抽取出来并为其生成一个单独的文件。这个 CSS
文件将在该异步 chunk
加载完成时自动通过一个 标签载入,该异步 chunk
会保证只在 CSS
加载完毕后再执行,避免发生 FOUC
。
{无样式内容的闪光|flash of unstyled content}(
FOUC
)是指在加载外部CSS样式表之前,网页以浏览器的默认样式短暂出现的情况,这是由于网络浏览器引擎在检索到所有信息之前渲染了该网页。
下面是Vite
中预加载插件的的简化版本。
function createLink(dep) { // JS -> <link rel="modulepreload" href="dep" /> // CSS -> <link rel="stylesheet" href="dep" /> } function preload(importModule, deps) { return Promise.all( deps.map(dep => { if (!alreadyLoaded(dep)) { document.head.appendChild(createLink(dep)) if (isCss(dep)) { // 等CSS资源加载,避免出现FOUC return new Promise((resolve, reject) => { link.addEventListener('load', resolve) link.addEventListener('error', reject) }) } } }) ).then(() => importModule()) } 复制代码
这个插件将使用上面的辅助函数来转换动态导入。以下是
import('./async.js') 复制代码
将被转换为
preload( () => import('/assets/async.js), ['/assets/async.css','/assets/async-dep.js'] ) 复制代码
如果build.cssCodeSplit
的值为false
,这些块会被vite:build-html
插件作为注入。
15. vite:build-html
这个插件会将 HTML
文件中的 标签编译成一个 JS 模块。
- 它会在
transform
钩子中移除HTML
中的script
标签,生成一个JS
文件,用于引入每个模块和资源文件。 - 随后,在
generateBundle
钩子中插入 JS 文件,并使用vite:asset
插件插入资源文件。
16. commonjs
它将CommonJS
模块转换为ES6
,这样它们就可以被包含在Rollup
的包中。
- 在开发过程中,
Vite
使用esbuild
进行资源的pre-bundling
,它负责将CommonJS
转换为ES6
- 但在构建过程中,没有
pre-bundling
的这步,所以需要commonjs
插件。
17. vite:data-uri
它从data-URI导入模块。
Data URL
,即前缀为data:
协议的URL
,其允许内容创建者向文档中嵌入小文件。
Data URL
由四个部分组成:前缀(data:
)、指示数据类型的MIME
类型、如果非文本则为可选的base64
标记、数据本身:data:[][;base64],
我们可以从DataURL
中导入模块。
import batman from 'data:application/json;base64, eyAiYmF0bWFuIjogInRydWUiIH0='; 复制代码
18. rollup/plugin-dynamic-import-vars
用于支持动态导入中的变量。
它是用build.dynamicImportVarsOptions
来配置的。
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars'; export default { plugins: [ dynamicImportVars({ // options }) ] }; 复制代码
允许用户编写动态解析的导入:
function importLocale(locale) { return import(`./locales/${locale}.js`); } 复制代码
19. vite:asset-import-meta-url
将 new URL(path, import.meta.url)
转换为内置URL
import.meta.url
是一个 ESM
的原生功能,会暴露当前模块的 URL
。将它与原生的 URL 构造器 组合使用,在一个 JavaScript
模块中,通过相对路径我们就能得到一个被完整解析的静态资源 URL:
const imgUrl = new URL('./img.png', import.meta.url).href document.getElementById('hero-img').src = imgUrl 复制代码
这个模式同样还可以通过字符串模板支持动态 URL:
function getImageUrl(name) { return new URL(`./dir/${name}.png`, import.meta.url).href } 复制代码
在生产构建时,Vite
才会进行必要的转换保证 URL 在打包和资源哈希后仍指向正确的地址。然而,这个 URL
字符串必须是静态的,这样才好分析。否则代码将被原样保留、因而在 build.target
不支持 import.meta.url
时会导致运行时错误。
// Vite 不会转换这个 const imgUrl = new URL(imagePath, import.meta.url).href 复制代码
20. 后置插件
带有 enforce: 'post'
的用户插件。
21. vite:build-import-analysis
这个插件会对 URL 导入
进行词法分析、解析、重写和分析。
动态导入会增加预加载指令。在客户端代码中注入一个辅助函数
,用于在异步块本身异步加载时并行预加载 CSS 和直接导入的异步块。
Glob 导入
会被识别并使用 transformImportGlob
进行转译。例如:
const modules = import.meta.glob('./dir/*.js') 复制代码
被转化为
const modules = { './dir/foo.js': () => import('./dir/foo.js'), './dir/bar.js': () => import('./dir/bar.js') } 复制代码
22. vite:esbuild-transpile
这个插件对每个渲染的块进行转译,以支持配置的目标。
如果build.minify
是 "esbuild"
(Vite3+
的版本是默认值),它也将使用esbuild
来最小化代码,避免了对terser
的需求。它比 terser
快 20-40 倍,压缩率只差 1%-2%
。
23. vite:terser
如果build.minify
是'terser'
,这个插件就会被用来使用terser
对每个渲染的块进行最小化。
24. vite:manifest
当设置为 true
,构建后将会生成 manifest.json
文件,包含了没有被 hash 过的资源文件名和 hash 后版本的映射。可以为一些服务器框架渲染时提供正确的资源引入链接。当该值为一个字符串时,它将作为 manifest
文件的名字。
25. vite:ssr-manifest
当设置为 true
时,构建也将生成 SSR
的 manifest
文件,以确定生产中的样式链接与资产预加载指令。当该值为一个字符串时,它将作为 manifest
文件的名字。
26. vite:reporter
一个记录进度的插件,以及一份包含生成块和资源信息的报告。
$ vite build vite v3.1.0 building for production... ✓ 34 modules transformed. dist/assets/favicon.17e50649.svg 1.49 KiB dist/assets/logo.ecc203fb.svg 2.61 KiB dist/index.html 0.52 KiB dist/assets/index.3015a40c.js 1.39 KiB / gzip: 0.73 KiB dist/assets/index.d93758c6.css 0.77 KiB / gzip: 0.49 KiB dist/assets/vendor.a9c538d6.js 129.47 KiB / gzip: 41.77 KiB Done in 2.90s. 复制代码
Vite+React的项目打包优化(简单版)
可以从以下几点出发
- 代码分割
- 预取/预加载
- 代码压缩
- 图片压缩
- 缓存策略
代码分割
使用代码分割可以将代码划分成较小的块,从而减少页面加载时间。可以使用Vite
提供的import()函数
或React
的React.lazy()
函数来实现代码分割。
import React, { lazy, Suspense } from 'react'; const LazyComponent = lazy(() => import('./LazyComponent')); function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> </div> ); } 复制代码
预取/预加载
使用预取/预加载可以在用户访问页面之前预加载页面所需的资源,从而加快页面加载时间。可以使用Vite提供的标签来实现预取/预加载。
<link rel="prefetch" href="./lazy-component.js" /> 复制代码
代码压缩
使用代码压缩可以减小文件大小,从而加快页面加载时间。可以在Vite
配置文件中启用代码压缩选项。
// vite.config.ts import { defineConfig } from 'vite'; import reactRefresh from '@vitejs/plugin-react-refresh'; import { terser } from 'rollup-plugin-terser'; export default defineConfig({ plugins: [reactRefresh(), terser()], }); 复制代码
图片压缩
使用图片压缩可以减小图片大小,从而加快页面加载时间。可以使用Vite
提供的imagemin
插件来实现图片压缩。
// vite.config.ts import { defineConfig } from 'vite'; import reactRefresh from '@vitejs/plugin-react-refresh'; import { imagemin } from 'rollup-plugin-imagemin'; export default defineConfig({ plugins: [ reactRefresh(), imagemin({ plugins: [ // add imagemin plugins here ], }), ], }); 复制代码
缓存策略
使用缓存策略可以减少重复的网络请求,从而加快页面加载时间。可以在Vite
配置文件中配置缓存策略选项。
// vite.config.ts import { defineConfig } from 'vite'; import reactRefresh from '@vitejs/plugin-react-refresh'; export default defineConfig({ plugins: [reactRefresh()], build: { // set cache options here cacheDir: '.vite-cache', }, }); 复制代码
后记
分享是一种态度。
参考资料:
全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。