特性
在最基本的層面上,使用 Vite 進行開發與使用靜態檔案伺服器沒有太大區別。然而,Vite 提供了許多對原生 ESM 匯入的增強功能,以支援通常在基於綁定器的設定中看到的各種功能。
npm 依賴解析和預先綁定
原生 ES 匯入不支援像以下這樣的裸模組匯入
import { someMethod } from 'my-dep'
以上程式碼將在瀏覽器中拋出錯誤。Vite 將偵測到所有服務的原始碼檔案中的此類裸模組匯入,並執行以下操作
預先綁定它們以提高頁面載入速度,並將 CommonJS/UMD 模組轉換為 ESM。預先綁定步驟使用 esbuild 執行,使 Vite 的冷啟動時間比任何基於 JavaScript 的綁定器快得多。
將匯入重寫為有效的 URL,例如
/node_modules/.vite/deps/my-dep.js?v=f3sf2ebd
,以便瀏覽器可以正確匯入它們。
依賴項被強烈快取
Vite 通過 HTTP 標頭快取依賴項請求,因此如果您希望在本地編輯/偵錯依賴項,請按照此處的步驟操作。
熱模組替換
Vite 通過原生 ESM 提供HMR API。具有 HMR 功能的框架可以利用該 API 提供即時、精確的更新,而無需重新載入頁面或清除應用程式狀態。Vite 為 Vue 單檔案元件 和 React Fast Refresh 提供第一方 HMR 整合。還有通過 @prefresh/vite 提供的 Preact 的官方整合。
請注意,您不需要手動設定這些 — 當您通過 create-vite
建立應用程式時,所選範本將已為您預先配置這些。
TypeScript
Vite 支援直接匯入 .ts
檔案。
僅轉譯
請注意,Vite 僅對 .ts
檔案執行轉譯,而不執行類型檢查。它假設類型檢查由您的 IDE 和建構程序處理。
Vite 不執行類型檢查作為轉換過程一部分的原因是因為這兩個任務的工作方式從根本上不同。轉譯可以針對每個檔案進行,並且與 Vite 的按需編譯模型完美契合。相比之下,類型檢查需要了解整個模組圖。將類型檢查強行塞入 Vite 的轉換管道將不可避免地損害 Vite 的速度優勢。
Vite 的工作是盡可能快地將您的原始模組轉換為可以在瀏覽器中執行的形式。為此,我們建議將靜態分析檢查與 Vite 的轉換管道分開。此原則適用於其他靜態分析檢查,例如 ESLint。
對於生產環境建構,您可以除了 Vite 的建構命令之外,還執行
tsc --noEmit
。在開發期間,如果您需要比 IDE 提示更多的功能,我們建議在單獨的進程中執行
tsc --noEmit --watch
,或者如果您希望在瀏覽器中直接報告類型錯誤,則可以使用vite-plugin-checker。
Vite 使用 esbuild 將 TypeScript 轉譯為 JavaScript,這比原始 tsc
快約 20~30 倍,並且 HMR 更新可以在 50 毫秒內反映在瀏覽器中。
使用僅類型匯入和匯出語法來避免潛在的問題,例如僅類型匯入被錯誤地綁定,例如
import type { T } from 'only/types'
export type { T }
TypeScript 編譯器選項
tsconfig.json
中 compilerOptions
下的某些配置欄位需要特別注意。
isolatedModules
應設定為 true
。
這是因為 esbuild
僅執行不帶類型資訊的轉譯,它不支援某些功能,例如 const 列舉和隱式僅類型匯入。
您必須在 tsconfig.json
的 compilerOptions
下將 "isolatedModules": true
設定為,以便 TS 警告您不要使用與隔離轉譯不相容的功能。
如果依賴項不能很好地與 "isolatedModules": true
一起使用。您可以使用 "skipLibCheck": true
來暫時抑制錯誤,直到上游修復為止。
useDefineForClassFields
從 Vite 2.5.0 開始,如果 TypeScript 目標為 ESNext
或 ES2022
或更高版本,則預設值將為 true
。這與tsc
4.3.2 及更高版本的行為一致。這也是標準 ECMAScript 執行階段行為。
其他 TypeScript 目標將預設為 false
。
但是對於那些來自其他程式語言或舊版 TypeScript 的人來說,這可能與直覺相反。您可以在TypeScript 3.7 發行說明中閱讀有關轉換的更多資訊。
如果您使用大量依賴類別欄位的程式庫,請注意該程式庫的預期用法。
大多數程式庫都期望 "useDefineForClassFields": true
,例如MobX。
但是,一些程式庫尚未轉換為此新預設值,包括 lit-element
。在這些情況下,請明確將 useDefineForClassFields
設定為 false
。
target
Vite 忽略 tsconfig.json
中的 target
值,遵循與 esbuild
相同的行為。
若要在開發中指定目標,可以使用esbuild.target
選項,該選項預設為 esnext
以進行最小化轉譯。在建構中,build.target
選項的優先順序高於 esbuild.target
,並且也可以在需要時設定。
useDefineForClassFields
如果 tsconfig.json
中的 target
不是 ESNext
或 ES2022
或更新版本,或者如果沒有 tsconfig.json
檔案,則 useDefineForClassFields
將預設為 false
,這對於預設值為 esnext
的 esbuild.target
來說可能會產生問題。它可能會轉譯為靜態初始化區塊,您的瀏覽器可能不支援該區塊。
因此,建議將 target
設定為 ESNext
或 ES2022
或更高版本,或者在設定 tsconfig.json
時明確將 useDefineForClassFields
設定為 true
。
影響建構結果的其他編譯器選項
extends
importsNotUsedAsValues
preserveValueImports
verbatimModuleSyntax
jsx
jsxFactory
jsxFragmentFactory
jsxImportSource
experimentalDecorators
alwaysStrict
skipLibCheck
Vite 啟動器範本預設具有 "skipLibCheck": "true"
,以避免對依賴項進行類型檢查,因為它們可能選擇僅支援特定版本和 TypeScript 配置。您可以在vuejs/vue-cli#5688 中了解更多資訊。
客戶端類型
Vite 的預設類型用於其 Node.js API。若要模擬 Vite 應用程式中用戶端程式碼的環境,請新增 d.ts
宣告檔案
/// <reference types="vite/client" />
或者,您可以在 tsconfig.json
內部的 compilerOptions.types
中新增 vite/client
{
"compilerOptions": {
"types": ["vite/client"]
}
}
這將提供以下類型模擬
提示
若要覆寫預設的類型,請新增一個包含您類型定義的類型定義檔案。然後,在 vite/client
之前新增類型參考。
例如,要讓 *.svg
的預設匯入成為 React 元件
vite-env-override.d.ts
(包含您的類型定義的檔案)tsdeclare module '*.svg' { const content: React.FC<React.SVGProps<SVGElement>> export default content }
- 包含對
vite/client
參考的檔案ts/// <reference types="./vite-env-override.d.ts" /> /// <reference types="vite/client" />
HTML
HTML 檔案在 Vite 專案中居於首位,作為應用程式的進入點,使其可以輕鬆建構單頁和多頁應用程式。
專案根目錄中的任何 HTML 檔案都可以通過其各自的目錄路徑直接存取
<root>/index.html
->https://127.0.0.1:5173/
<root>/about.html
->https://127.0.0.1:5173/about.html
<root>/blog/index.html
->https://127.0.0.1:5173/blog/index.html
HTML 元素(例如 <script type="module" src>
和 <link href>
)引用的資源會被處理並作為應用程式的一部分進行捆綁。下面是支援元素的完整列表
<audio src>
<embed src>
<img src>
和<img srcset>
<image src>
<input src>
<link href>
和<link imagesrcset>
<object data>
<script type="module" src>
<source src>
和<source srcset>
<track src>
<use href>
和<use xlink:href>
<video src>
和<video poster>
<meta content>
- 僅當
name
屬性符合msapplication-tileimage
、msapplication-square70x70logo
、msapplication-square150x150logo
、msapplication-wide310x150logo
、msapplication-square310x310logo
、msapplication-config
或twitter:image
時 - 或僅當
property
屬性符合og:image
、og:image:url
、og:image:secure_url
、og:audio
、og:audio:secure_url
、og:video
或og:video:secure_url
時
- 僅當
<!doctype html>
<html>
<head>
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<img src="/src/images/logo.svg" alt="logo" />
<script type="module" src="/src/main.js"></script>
</body>
</html>
若要選擇不對特定元素進行 HTML 處理,您可以在元素上新增 vite-ignore
屬性,這在引用外部資源或 CDN 時很有用。
Vue
Vite 提供一流的 Vue 支援
- 透過 @vitejs/plugin-vue 支援 Vue 3 SFC
- 透過 @vitejs/plugin-vue-jsx 支援 Vue 3 JSX
- 透過 @vitejs/plugin-vue2 支援 Vue 2.7 SFC
- 透過 @vitejs/plugin-vue2-jsx 支援 Vue 2.7 JSX
JSX
也直接支援 .jsx
和 .tsx
檔案。JSX 轉譯也透過 esbuild 處理。
Vue 使用者應使用官方的 @vitejs/plugin-vue-jsx 外掛程式,其提供 Vue 3 的特定功能,包括 HMR、全域元件解析、指令和插槽。
如果使用不含 React 或 Vue 的 JSX,可以使用esbuild
選項設定自訂的 jsxFactory
和 jsxFragment
。例如,針對 Preact
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment',
},
})
更多詳細資訊請參閱 esbuild 文件。
您可以使用 jsxInject
(Vite 專有的選項) 注入 JSX 輔助程式,以避免手動匯入
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxInject: `import React from 'react'`,
},
})
CSS
匯入 .css
檔案會透過具有 HMR 支援的 <style>
標籤將其內容注入頁面。
@import
內聯和重定基底
Vite 預先設定為透過 postcss-import
支援 CSS @import
內聯。Vite 別名也適用於 CSS @import
。此外,所有 CSS url()
參考,即使匯入的檔案位於不同的目錄中,也總是會自動重定基底以確保正確性。
@import
別名和 URL 重定基底也支援 Sass 和 Less 檔案(請參閱CSS 預處理器)。
PostCSS
如果專案包含有效的 PostCSS 設定(postcss-load-config 支援的任何格式,例如 postcss.config.js
),則會自動將其套用至所有匯入的 CSS。
請注意,CSS 縮小會在 PostCSS 之後執行,並使用 build.cssTarget
選項。
CSS 模組
任何以 .module.css
結尾的 CSS 檔案都被視為CSS 模組檔案。匯入此類檔案會傳回對應的模組物件
.red {
color: red;
}
import classes from './example.module.css'
document.getElementById('foo').className = classes.red
CSS 模組行為可以透過 css.modules
選項進行設定。
如果 css.modules.localsConvention
設定為啟用駝峰式大小寫的局部變數(例如 localsConvention: 'camelCaseOnly'
),您也可以使用具名匯入
// .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColor
CSS 預處理器
由於 Vite 僅以現代瀏覽器為目標,因此建議使用具有實作 CSSWG 草案的 PostCSS 外掛程式(例如 postcss-nesting)的原生 CSS 變數,並撰寫簡單、符合未來標準的 CSS。
也就是說,Vite 為 .scss
、.sass
、.less
、.styl
和 .stylus
檔案提供內建支援。無需為它們安裝 Vite 特定的外掛程式,但必須安裝對應的預處理器本身
# .scss and .sass
npm add -D sass-embedded # or sass
# .less
npm add -D less
# .styl and .stylus
npm add -D stylus
如果使用 Vue 單一檔案元件,這也會自動啟用 <style lang="sass">
等。
Vite 改善了 Sass 和 Less 的 @import
解析,因此也會遵守 Vite 別名。此外,匯入的 Sass/Less 檔案中與根檔案位於不同目錄的相對 url()
參考也會自動重定基底,以確保正確性。
由於其 API 限制,Stylus 不支援 @import
別名和 URL 重定基底。
您也可以透過在檔案副檔名前面加上 .module
,將 CSS 模組與預處理器結合使用,例如 style.module.scss
。
停用 CSS 注入到頁面
可以透過 ?inline
查詢參數關閉 CSS 內容的自動注入。在這種情況下,處理後的 CSS 字串會像往常一樣作為模組的預設匯出傳回,但樣式不會注入到頁面。
import './foo.css' // will be injected into the page
import otherStyles from './bar.css?inline' // will not be injected
注意
從 Vite 5 開始,已移除 CSS 檔案的預設和具名匯入 (例如 import style from './foo.css'
)。請改用 ?inline
查詢。
Lightning CSS
從 Vite 4.4 開始,實驗性地支援 Lightning CSS。您可以透過在設定檔中新增 css.transformer: 'lightningcss'
並安裝選用的 lightningcss
相依性來選擇使用它
npm add -D lightningcss
如果啟用,CSS 檔案會由 Lightning CSS 而不是 PostCSS 處理。若要設定它,您可以將 Lightning CSS 選項傳遞至 css.lightningcss
設定選項。
若要設定 CSS 模組,您將使用 css.lightningcss.cssModules
,而不是 css.modules
(其設定 PostCSS 處理 CSS 模組的方式)。
依預設,Vite 使用 esbuild 來縮小 CSS。Lightning CSS 也可以用作 CSS 縮小工具,使用 build.cssMinify: 'lightningcss'
。
注意
使用 Lightning CSS 時,不支援CSS 預處理器。
靜態資源
匯入靜態資源會傳回其服務時解析的公開 URL
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
特殊的查詢可以修改資源的載入方式
// Explicitly load assets as URL
import assetAsURL from './asset.js?url'
// Load assets as strings
import assetAsString from './shader.glsl?raw'
// Load Web Workers
import Worker from './worker.js?worker'
// Web Workers inlined as base64 strings at build time
import InlineWorker from './worker.js?worker&inline'
更多詳細資訊請參閱靜態資源處理。
JSON
可以直接匯入 JSON 檔案 - 也支援具名匯入
// import the entire object
import json from './example.json'
// import a root field as named exports - helps with tree-shaking!
import { field } from './example.json'
Glob 匯入
Vite 支援透過特殊的 import.meta.glob
函數,從檔案系統匯入多個模組
const modules = import.meta.glob('./dir/*.js')
以上程式碼會轉換為下列程式碼
// code produced by vite
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
'./dir/bar.js': () => import('./dir/bar.js'),
}
然後,您可以迭代 modules
物件的鍵以存取對應的模組
for (const path in modules) {
modules[path]().then((mod) => {
console.log(path, mod)
})
}
預設情況下,比對的檔案會透過動態匯入進行延遲載入,並在建置期間分割成單獨的區塊。如果您寧願直接匯入所有模組(例如,依靠這些模組中的副作用來先套用),您可以將 { eager: true }
作為第二個引數傳遞
const modules = import.meta.glob('./dir/*.js', { eager: true })
以上程式碼會轉換為下列程式碼
// code produced by vite
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
'./dir/foo.js': __glob__0_0,
'./dir/bar.js': __glob__0_1,
}
多個模式
第一個引數可以是 glob 的陣列,例如
const modules = import.meta.glob(['./dir/*.js', './another/*.js'])
否定模式
也支援否定 glob 模式(以 !
作為前綴)。若要從結果中忽略某些檔案,您可以將排除 glob 模式新增至第一個引數
const modules = import.meta.glob(['./dir/*.js', '!**/bar.js'])
// code produced by vite
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
}
具名匯入
可以使用 import
選項僅匯入模組的部分。
const modules = import.meta.glob('./dir/*.js', { import: 'setup' })
// code produced by vite
const modules = {
'./dir/foo.js': () => import('./dir/foo.js').then((m) => m.setup),
'./dir/bar.js': () => import('./dir/bar.js').then((m) => m.setup),
}
當與 eager
結合使用時,甚至可以為這些模組啟用 tree-shaking。
const modules = import.meta.glob('./dir/*.js', {
import: 'setup',
eager: true,
})
// code produced by vite:
import { setup as __glob__0_0 } from './dir/foo.js'
import { setup as __glob__0_1 } from './dir/bar.js'
const modules = {
'./dir/foo.js': __glob__0_0,
'./dir/bar.js': __glob__0_1,
}
將 import
設定為 default
可匯入預設匯出。
const modules = import.meta.glob('./dir/*.js', {
import: 'default',
eager: true,
})
// code produced by vite:
import __glob__0_0 from './dir/foo.js'
import __glob__0_1 from './dir/bar.js'
const modules = {
'./dir/foo.js': __glob__0_0,
'./dir/bar.js': __glob__0_1,
}
自訂查詢
您也可以使用 query
選項為匯入提供查詢,例如,將資源作為字串或作為 URL匯入
const moduleStrings = import.meta.glob('./dir/*.svg', {
query: '?raw',
import: 'default',
})
const moduleUrls = import.meta.glob('./dir/*.svg', {
query: '?url',
import: 'default',
})
// code produced by vite:
const moduleStrings = {
'./dir/foo.svg': () => import('./dir/foo.js?raw').then((m) => m['default']),
'./dir/bar.svg': () => import('./dir/bar.js?raw').then((m) => m['default']),
}
const moduleUrls = {
'./dir/foo.svg': () => import('./dir/foo.js?url').then((m) => m['default']),
'./dir/bar.svg': () => import('./dir/bar.js?url').then((m) => m['default']),
}
您也可以為其他外掛程式提供自訂查詢以供使用
const modules = import.meta.glob('./dir/*.js', {
query: { foo: 'bar', bar: true },
})
Glob 匯入注意事項
請注意
- 這是 Vite 專有的功能,並非網頁或 ES 標準。
- glob 模式會被視為匯入規範:它們必須是相對的(以
./
開頭)或絕對的(以/
開頭,相對於專案根目錄解析)或別名路徑(請參閱resolve.alias
選項)。 - glob 比對是透過
tinyglobby
完成。 - 您也應該注意,
import.meta.glob
中的所有引數都必須以字面值形式傳遞。您不能在其中使用變數或運算式。
動態匯入
與glob 匯入類似,Vite 也支援使用變數的動態匯入。
const module = await import(`./dir/${file}.js`)
請注意,變數僅表示檔案名稱的深度為一層。如果 file
是 'foo/bar'
,匯入將會失敗。如需更進階的用法,您可以使用glob 匯入功能。
WebAssembly
可以使用 ?init
匯入預先編譯的 .wasm
檔案。預設匯出將是一個初始化函數,該函數會傳回 WebAssembly.Instance
的 Promise
import init from './example.wasm?init'
init().then((instance) => {
instance.exports.test()
})
init
函式也可以接受一個 importObject
,它會作為第二個參數傳遞給 WebAssembly.instantiate
。
init({
imports: {
someFunc: () => {
/* ... */
},
},
}).then(() => {
/* ... */
})
在生產環境建置中,小於 assetInlineLimit
的 .wasm
檔案將會被內嵌為 base64 字串。否則,它們將會被視為靜態資源並按需載入。
注意
目前不支援 WebAssembly 的 ES 模組整合提案。請使用 vite-plugin-wasm
或其他社群外掛來處理。
存取 WebAssembly 模組
如果您需要存取 Module
物件,例如為了多次實例化它,請使用明確的 URL 匯入來解析資源,然後執行實例化。
import wasmUrl from 'foo.wasm?url'
const main = async () => {
const responsePromise = fetch(wasmUrl)
const { module, instance } =
await WebAssembly.instantiateStreaming(responsePromise)
/* ... */
}
main()
在 Node.js 中載入模組
在 SSR 中,作為 ?init
匯入一部分發生的 fetch()
可能會失敗,並顯示 TypeError: Invalid URL
。請參閱問題 Support wasm in SSR。
這裡有一個替代方案,假設專案的基礎目錄為目前目錄
import wasmUrl from 'foo.wasm?url'
import { readFile } from 'node:fs/promises'
const main = async () => {
const resolvedUrl = (await import('./test/boot.test.wasm?url')).default
const buffer = await readFile('.' + resolvedUrl)
const { instance } = await WebAssembly.instantiate(buffer, {
/* ... */
})
/* ... */
}
main()
Web Workers
使用建構函式匯入
可以使用 new Worker()
和 new SharedWorker()
匯入 web worker 腳本。與 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',
})
只有在 new URL()
建構函式直接在 new Worker()
宣告中使用時,worker 偵測才會起作用。此外,所有 options 參數必須為靜態值(即字串字面值)。
使用查詢後綴匯入
可以直接在匯入請求中附加 ?worker
或 ?sharedworker
來匯入 web worker 腳本。預設匯出將會是一個自訂 worker 建構函式。
import MyWorker from './worker?worker'
const worker = new MyWorker()
worker 腳本也可以使用 ESM import
語句,而不是 importScripts()
。注意:在開發期間,這依賴於瀏覽器原生支援,但在生產環境建置中,它會被編譯掉。
預設情況下,worker 腳本會在生產環境建置中作為單獨的區塊發出。如果您希望將 worker 內嵌為 base64 字串,請新增 inline
查詢。
import MyWorker from './worker?worker&inline'
如果您希望以 URL 形式檢索 worker,請新增 url
查詢。
import MyWorker from './worker?worker&url'
有關配置所有 worker 綁定的詳細資訊,請參閱 Worker 選項。
內容安全策略 (CSP)
為了部署 CSP,由於 Vite 的內部機制,必須設定某些指令或配置。
'nonce-{RANDOM}'
當 html.cspNonce
設定時,Vite 會將具有指定值的 nonce 屬性新增至任何 <script>
和 <style>
標籤,以及用於樣式表和模組預載的 <link>
標籤。此外,當設定此選項時,Vite 會注入 meta 標籤 (<meta property="csp-nonce" nonce="PLACEHOLDER" />
)。
在開發和建置後,Vite 會在必要時使用具有 property="csp-nonce"
的 meta 標籤的 nonce 值。
警告
請確保您為每個請求將預留位置取代為唯一值。這對於防止繞過資源的策略非常重要,否則很容易做到。
data:
預設情況下,在建置期間,Vite 會將小型資源內嵌為資料 URI。允許相關指令 (例如 img-src
、font-src
) 使用 data:
,或透過設定 build.assetsInlineLimit: 0
來停用它是必要的。
警告
請勿允許 script-src
使用 data:
。這將允許注入任意腳本。
建置最佳化
以下列出的功能會自動作為建置流程的一部分套用,除非您想要停用它們,否則無需明確設定。
CSS 程式碼分割
Vite 會自動提取非同步區塊中模組使用的 CSS,並為其產生一個單獨的檔案。當載入相關的非同步區塊時,CSS 檔案會透過 <link>
標籤自動載入,並且保證非同步區塊只會在 CSS 載入後才評估,以避免 FOUC。
如果您希望將所有 CSS 提取到單個檔案中,您可以將 build.cssCodeSplit
設定為 false
來停用 CSS 程式碼分割。
預載指令產生
Vite 會在已建置的 HTML 中,自動為入口區塊及其直接匯入產生 <link rel="modulepreload">
指令。
非同步區塊載入最佳化
在真實世界的應用程式中,Rollup 通常會產生「共用」區塊 - 在兩個或多個其他區塊之間共用的程式碼。結合動態匯入,以下情況很常見
在未最佳化的情況下,當匯入非同步區塊 A
時,瀏覽器必須先請求和剖析 A
,才能判斷它也需要共用區塊 C
。這會導致額外的網路往返。
Entry ---> A ---> C
Vite 會自動使用預載步驟來重寫程式碼分割的動態匯入呼叫,以便在請求 A
時,平行擷取 C
。
Entry ---> (A + C)
C
有可能會有進一步的匯入,這會在未最佳化的情況下導致更多的往返。Vite 的最佳化將追蹤所有直接匯入,以完全消除往返,而與匯入深度無關。