Vite 功能

2022-03-29 16:06 更新

對(duì)非?;A(chǔ)的使用來說,使用 Vite 開發(fā)和使用一個(gè)靜態(tài)文件服務(wù)器并沒有太大區(qū)別。然而,Vite 還通過原生 ESM 導(dǎo)入提供了許多主要用于打包場(chǎng)景的增強(qiáng)功能。

NPM依賴解析和預(yù)構(gòu)建

原生 ES 導(dǎo)入不支持下面這樣的裸模塊導(dǎo)入:

import { someMethod } from 'my-dep'

上面的代碼會(huì)在瀏覽器中拋出一個(gè)錯(cuò)誤。Vite 將會(huì)檢測(cè)到所有被加載的源文件中的此類裸模塊導(dǎo)入,并執(zhí)行以下操作:

  1. 預(yù)構(gòu)建 它們可以提高頁(yè)面加載速度,并將 CommonJS / UMD 轉(zhuǎn)換為 ESM 格式。預(yù)構(gòu)建這一步由 esbuild 執(zhí)行,這使得 Vite 的冷啟動(dòng)時(shí)間比任何基于 JavaScript 的打包器都要快得多。
  2. 重寫導(dǎo)入為合法的 URL,例如 /node_modules/.vite/my-dep.js?v=f3sf2ebd 以便瀏覽器能夠正確導(dǎo)入它們。

依賴是強(qiáng)緩存的

Vite 通過 HTTP 頭來緩存請(qǐng)求得到的依賴

模塊熱重載

Vite 提供了一套原生 ESM 的 HMR API。 具有 HMR 功能的框架可以利用該 API 提供即時(shí)、準(zhǔn)確的更新,而無需重新加載頁(yè)面或清除應(yīng)用程序狀態(tài)。Vite 內(nèi)置了 HMR 到 Vue 單文件組件(SFC) 和 React Fast Refresh 中。也通過 @prefresh/vite 對(duì) Preact 實(shí)現(xiàn)了官方集成。

注意,你不需要手動(dòng)設(shè)置這些 —— 當(dāng)你通過 create-vite 創(chuàng)建應(yīng)用程序時(shí),所選模板已經(jīng)為你預(yù)先配置了這些。

TypeScript

Vite 天然支持引入 ?.ts? 文件。
Vite 僅執(zhí)行? .ts? 文件的轉(zhuǎn)譯工作,并 不 執(zhí)行任何類型檢查。并假設(shè)類型檢查已經(jīng)被你的 IDE 或構(gòu)建過程接管了(你可以在構(gòu)建腳本中運(yùn)行 ?tsc --noEmit? 或者安裝 ?vue-tsc? 然后運(yùn)行 ?vue-tsc --noEmit? 來對(duì)你的 ?*.vue? 文件做類型檢查)。
Vite 使用 esbuild 將 TypeScript 轉(zhuǎn)譯到 JavaScript,約是 tsc 速度的 20~30 倍,同時(shí) HMR 更新反映到瀏覽器的時(shí)間小于 50ms。

TypeScript編譯器選項(xiàng)

tsconfig.json 中 compilerOptions 下的一些配置項(xiàng)需要特別注意。

?isolatedModules ?

?isolatedModules應(yīng)該設(shè)置為 ?true?

這是因?yàn)?nbsp;?esbuild ?只執(zhí)行沒有類型信息的轉(zhuǎn)譯,它并不支持某些特性,如 ?const enum? 和隱式類型導(dǎo)入。

你必須在 ?tsconfig.json? 中的 ?compilerOptions ?下設(shè)置 ?"isolatedModules": true?。如此做,TS 會(huì)警告你不要使用隔離(isolated)轉(zhuǎn)譯的功能。

? useDefineForClassFields?

從 Vite v2.5.0 開始,如果 TypeScript 的 target 是 ESNext,此選項(xiàng)默認(rèn)值則為 true。這與 tsc v4.3.2 及以后版本的行為 一致。這也是標(biāo)準(zhǔn)的 ECMAScript 的運(yùn)行時(shí)行為。

但對(duì)于那些習(xí)慣其他編程語言或舊版本 TypeScript 的開發(fā)者來說,這可能是違反直覺的。 你可以參閱 TypeScript 3.7 發(fā)布日志 中了解更多關(guān)于如何兼容的信息。

如果你正在使用一個(gè)嚴(yán)重依賴 class fields 的庫(kù),請(qǐng)注意該庫(kù)對(duì)此選項(xiàng)的預(yù)期設(shè)置。

大多數(shù)庫(kù)都希望 "useDefineForClassFields": true,如 MobX,Vue Class Components 8.x 等。

但是有幾個(gè)庫(kù)還沒有兼容這個(gè)新的默認(rèn)值,其中包括 lit-element。如果遇到這種情況,請(qǐng)將 useDefineForClassFields 設(shè)置為 false。

影響構(gòu)建結(jié)果的其他編譯器選項(xiàng)

如果你的代碼庫(kù)很難遷移到 "isolatedModules": true,或許你可以嘗試通過第三方插件來解決,比如 rollup-plugin-friendly-type-imports。但是,這種方式不被 Vite 官方支持。

客戶端類型

Vite 默認(rèn)的類型定義是寫給它的 Node.js API 的。要將其補(bǔ)充到一個(gè) Vite 應(yīng)用的客戶端代碼環(huán)境中,請(qǐng)?zhí)砑右粋€(gè) d.ts 聲明文件:

/// <reference types="vite/client" />

同時(shí),你也可以將 vite/client 添加到 tsconfig 中的 compilerOptions.types 下:

{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

這將會(huì)提供以下類型定義補(bǔ)充:

  • 資源導(dǎo)入 (例如:導(dǎo)入一個(gè) .svg 文件)
  • import.meta.env 上 Vite 注入的環(huán)境變量的類型定義
  • import.meta.hot 上的 HMR API 類型定義

Vue

Vite 為 Vue 提供第一優(yōu)先級(jí)支持:

JSX

.jsx 和 .tsx 文件同樣開箱即用。JSX 的轉(zhuǎn)譯同樣是通過 esbuild,默認(rèn)為 React 16 風(fēng)格。期望在 esbuild 中支持 React 17 風(fēng)格的 JSX 請(qǐng)看 這里。

Vue 用戶應(yīng)使用官方提供的 @vitejs/plugin-vue-jsx 插件,它提供了 Vue 3 特性的支持,包括 HMR,全局組件解析,指令和插槽。

如果不是在 React 或 Vue 中使用 JSX,自定義的 jsxFactory 和 jsxFragment 可以使用 esbuild 選項(xiàng) 進(jìn)行配置。例如對(duì) Preact:

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  esbuild: {
    jsxFactory: 'h',
    jsxFragment: 'Fragment'
  }
})

更多細(xì)節(jié)詳見 esbuild 文檔.

你可以使用 jsxInject(這是一個(gè)僅在 Vite 中使用的選項(xiàng))為 JSX 注入 helper,以避免手動(dòng)導(dǎo)入:

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  esbuild: {
    jsxInject: `import React from 'react'`
  }
})

CSS

導(dǎo)入 .css 文件將會(huì)把內(nèi)容插入到 <style> 標(biāo)簽中,同時(shí)也帶有 HMR 支持。也能夠以字符串的形式檢索處理后的、作為其模塊默認(rèn)導(dǎo)出的 CSS。

?@import? 內(nèi)聯(lián)和變基

Vite 通過 ?postcss-import? 預(yù)配置支持了 CSS ?@import? 內(nèi)聯(lián),Vite 的路徑別名也遵從 CSS ?@import?。換句話說,所有 CSS ?url()? 引用,即使導(dǎo)入的文件在不同的目錄中,也總是自動(dòng)變基,以確保正確性。

Sass 和 Less 文件也支持 ?@import? 別名和 URL 變基

PostCSS

如果項(xiàng)目包含有效的 PostCSS 配置 (任何受 postcss-load-config 支持的格式,例如 postcss.config.js),它將會(huì)自動(dòng)應(yīng)用于所有已導(dǎo)入的 CSS。

CSS Modules

任何以 .module.css 為后綴名的 CSS 文件都被認(rèn)為是一個(gè) CSS modules 文件。導(dǎo)入這樣的文件會(huì)返回一個(gè)相應(yīng)的模塊對(duì)象:

/* example.module.css */
.red {
  color: red;
}
import classes from './example.module.css'
document.getElementById('foo').className = classes.red

CSS modules 行為可以通過 ?css.modules? 選項(xiàng) 進(jìn)行配置。

如果 ?css.modules.localsConvention? 設(shè)置開啟了 camelCase 格式變量名轉(zhuǎn)換(例如 ?localsConvention: 'camelCaseOnly'?),你還可以使用按名導(dǎo)入。

// .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColor

CSS預(yù)處理器

由于 Vite 的目標(biāo)僅為現(xiàn)代瀏覽器,因此建議使用原生 CSS 變量和實(shí)現(xiàn) CSSWG 草案的 PostCSS 插件(例如 postcss-nesting)來編寫簡(jiǎn)單的、符合未來標(biāo)準(zhǔn)的 CSS。

話雖如此,但 Vite 也同時(shí)提供了對(duì) ?.scss?, ?.sass?, ?.less?, ?.styl? 和 ?.stylus? 文件的內(nèi)置支持。沒有必要為它們安裝特定的 Vite 插件,但必須安裝相應(yīng)的預(yù)處理器依賴:

# .scss and .sass
npm install -D sass

# .less
npm install -D less

# .styl and .stylus
npm install -D stylus

如果是用的是單文件組件,可以通過 ?<style lang="sass">?(或其他預(yù)處理器)自動(dòng)開啟。

Vite 為 Sass 和 Less 改進(jìn)了 ?@import? 解析,以保證 Vite 別名也能被使用。另外,?url()? 中的相對(duì)路徑引用的,與根文件不同目錄中的 Sass/Less 文件會(huì)自動(dòng)變基以保證正確性。

由于 Stylus API 限制,?@import? 別名和 URL 變基不支持 Stylus。

你還可以通過在文件擴(kuò)展名前加上 ?.module? 來結(jié)合使用 CSS modules 和預(yù)處理器,例如 ?style.module.scss?。

靜態(tài)資源處理

導(dǎo)入一個(gè)靜態(tài)資源會(huì)返回解析后的 URL:

import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl

添加一些特殊的查詢參數(shù)可以更改資源被引入的方式:

// 顯式加載資源為一個(gè) URL
import assetAsURL from './asset.js?url'
// 以字符串形式加載資源
import assetAsString from './shader.glsl?raw'
// 加載為 Web Worker
import Worker from './worker.js?worker'
// 在構(gòu)建時(shí) Web Worker 內(nèi)聯(lián)為 base64 字符串
import InlineWorker from './worker.js?worker&inline'

JSON

JSON 可以被直接導(dǎo)入 —— 同樣支持具名導(dǎo)入:

// 導(dǎo)入整個(gè)對(duì)象
import json from './example.json'
// 對(duì)一個(gè)根字段使用具名導(dǎo)入 —— 有效幫助 treeshaking!
import { field } from './example.json'

Glob導(dǎo)入

Vite 支持使用特殊的 import.meta.glob 函數(shù)從文件系統(tǒng)導(dǎo)入多個(gè)模塊:

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

以上將會(huì)被轉(zhuǎn)譯為下面的樣子:

// vite 生成的代碼
const modules = {
  './dir/foo.js': () => import('./dir/foo.js'),
  './dir/bar.js': () => import('./dir/bar.js')
}

你可以遍歷 modules 對(duì)象的 key 值來訪問相應(yīng)的模塊:

for (const path in modules) {
  modules[path]().then((mod) => {
    console.log(path, mod)
  })
}

匹配到的文件默認(rèn)是懶加載的,通過動(dòng)態(tài)導(dǎo)入實(shí)現(xiàn),并會(huì)在構(gòu)建時(shí)分離為獨(dú)立的 chunk。如果你傾向于直接引入所有的模塊(例如依賴于這些模塊中的副作用首先被應(yīng)用),你可以使用 import.meta.globEager 代替:

const modules = import.meta.globEager('./dir/*.js')

以上會(huì)被轉(zhuǎn)譯為下面的樣子:

// 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
}

請(qǐng)注意:

  • 這只是一個(gè) Vite 獨(dú)有的功能而不是一個(gè) Web 或 ES 標(biāo)準(zhǔn)
  • 該 Glob 模式會(huì)被當(dāng)成導(dǎo)入標(biāo)識(shí)符:必須是相對(duì)路徑(以 ./ 開頭)或絕對(duì)路徑(以 / 開頭,相對(duì)于項(xiàng)目根目錄解析)。
  • Glob 匹配是使用 fast-glob 來實(shí)現(xiàn)的 —— 閱讀它的文檔來查閱 支持的 Glob 模式
  • 你還需注意,glob 的導(dǎo)入不接受變量,你應(yīng)直接傳遞字符串模式。

WebAssembly

預(yù)編譯的 .wasm 文件可以直接被導(dǎo)入 —— 默認(rèn)導(dǎo)出一個(gè)函數(shù),返回值為所導(dǎo)出 wasm 實(shí)例對(duì)象的 Promise:

import init from './example.wasm'

init().then((exports) => {
  exports.test()
})

init 函數(shù)還可以將傳遞給 WebAssembly.instantiate 的導(dǎo)入對(duì)象作為其第二個(gè)參數(shù):

init({
  imports: {
    someFunc: () => {
      /* ... */
    }
  }
}).then(() => {
  /* ... */
})

在生產(chǎn)構(gòu)建當(dāng)中,體積小于 assetInlineLimit 的 .wasm 文件將會(huì)被內(nèi)聯(lián)為 base64 字符串。否則,它們將作為資源復(fù)制到 dist 目錄中,并按需獲取。

Web Worker

一個(gè) web worker 腳本可以直接通過添加一個(gè) ?worker 或 ?sharedworker 查詢參數(shù)來導(dǎo)入。默認(rèn)導(dǎo)出一個(gè)自定義的 worker 構(gòu)造器:

import MyWorker from './worker?worker'

const worker = new MyWorker()

Worker 腳本也可以使用 ?import ?語句來替代 ?importScripts()? —— 注意,在開發(fā)過程中,這依賴于瀏覽器原生支持,目前只在 Chrome 中適用,而在生產(chǎn)版本中,它已經(jīng)被編譯掉了。

默認(rèn)情況下,worker 腳本將在生產(chǎn)構(gòu)建中編譯成單獨(dú)的 chunk。如果你想將 worker 內(nèi)聯(lián)為 base64 字符串,請(qǐng)?zhí)砑?nbsp;?inline ?查詢參數(shù):

import MyWorker from './worker?worker&inline'

構(gòu)建優(yōu)化

下面所羅列的功能會(huì)自動(dòng)應(yīng)用為構(gòu)建過程的一部分,除非你想禁用它們,否則沒有必要顯式配置。

CSS代碼分割

Vite 會(huì)自動(dòng)地將一個(gè)異步 chunk 模塊中使用到的 CSS 代碼抽取出來并為其生成一個(gè)單獨(dú)的文件。這個(gè) CSS 文件將在該異步 chunk 加載完成時(shí)自動(dòng)通過一個(gè) ?<link>? 標(biāo)簽載入,該異步 chunk 會(huì)保證只在 CSS 加載完畢后再執(zhí)行,避免發(fā)生 FOUC 。

如果你更傾向于將所有的 CSS 抽取到一個(gè)文件中,你可以通過設(shè)置 ?build.cssCodeSplit? 為 ?false ?來禁用 CSS 代碼分割。

預(yù)加載指令生成

Vite 會(huì)為入口 chunk 和它們?cè)诖虬龅?HTML 中的直接引入自動(dòng)生成 <link rel="modulepreload"> 指令。

異步 Chunk 加載優(yōu)化

在實(shí)際項(xiàng)目中,Rollup 通常會(huì)生成 “共用” chunk —— 被兩個(gè)或以上的其他 chunk 共享的 chunk。與動(dòng)態(tài)導(dǎo)入相結(jié)合,會(huì)很容易出現(xiàn)下面這種場(chǎng)景:graph

在無優(yōu)化的情境下,當(dāng)異步 chunk A 被導(dǎo)入時(shí),瀏覽器將必須請(qǐng)求和解析 A,然后它才能弄清楚它也需要共用 chunk C。這會(huì)導(dǎo)致額外的網(wǎng)絡(luò)往返:

Entry ---> A ---> C

Vite 將使用一個(gè)預(yù)加載步驟自動(dòng)重寫代碼,來分割動(dòng)態(tài)導(dǎo)入調(diào)用,以實(shí)現(xiàn)當(dāng) ?A ?被請(qǐng)求時(shí),?C ?也將 同時(shí) 被請(qǐng)求:

Entry ---> (A + C)

C 也可能有更深的導(dǎo)入,在未優(yōu)化的場(chǎng)景中,這會(huì)導(dǎo)致更多的網(wǎng)絡(luò)往返。Vite 的優(yōu)化會(huì)跟蹤所有的直接導(dǎo)入,無論導(dǎo)入的深度如何,都能夠完全消除不必要的往返。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)