【原理揭秘】Vite 是怎麼相容老舊瀏覽器的?你以為僅僅依靠 Babel?

京東雲開發者發表於2023-04-03

作者:京東科技 孫凱

一、前言

對前端開發者來說,Vite 應該不算陌生了,它是一款基於 nobundle 和 bundleless 思想誕生的前端開發與構建工具,官網對它的概括和期待只有一句話:“下一代的前端工具鏈”。

Vite 最早的版本由尤雨溪釋出於3年前,經歷了3年多的發展,Vite 也已逐漸迭代成熟,它的穩定性、擴充套件性、周邊生態足以在生產環境中支撐各種業務場景的落地。但是關於Vite的優劣勢分析我們就戛然而止,不在深入展開了,這不是本文的重點。

本文的重點在於探究 Vite 如何實現相容低版本瀏覽器,這一切還得從那個陽光明媚的午後說起?。

二、那個午後

本著嚐鮮的態度,我在某一個專案中用了 Vite,當時還是3.x.x的版本,跟著文件配置,從專案啟動到專案構建,一路都很“德芙”(縱享絲滑),在經歷了 Vite 帶來的短暫新鮮感後,就一直沉浸在業務模組的開發中了,因為在 Vite 剛釋出後的那段時間曾看過相關原理解析,是基於瀏覽器原生的模組化能力按需構建BALABALA等,所以後來 Vite 的這種新鮮感對我而言並沒有保持多久。

但直到有天下午我開始打包提測,審查頁面元素後發現構建產物居然跟以往 webpack 的產物竟然有點不一樣,在好奇心的驅使下,於是我開始嘗試解謎。

三、跟webpack構建產物到底哪裡不一樣?

1. 準備工作

為了能更好的對比兩者產物究竟有什麼區別,我們首先要確保我們的業務程式碼基本一致,不一致的地方僅僅是使用不同工具( vite 和 webpack)進行構建,這樣才能排除最大干擾因素。

於是我們分別使用最新版的 Vite 和 webpack 初始化了兩個頁面,為了做作區分,兩個頁面的僅標題和標題背景不一致,他們在瀏覽器中渲染後的分別長這個樣子:

2. 構建工具版本說明

• Vite:v4.1.4

• webpack:v5.75.0

3. 構建工具配置項說明

• Vite (非常簡單,啥也沒有)

// vite.config.js
import { defineConfig } from 'vite'
import legacy from '@vitejs/plugin-legacy'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
    plugins: [
        vue(),
        legacy({
            targets: ['ios >= 9', 'android >= 4.2', '> 1%']
        })
    ],
    server: {
        host: '127.0.0.1'
    },
    build: {
        minify: false
    }
})



• webpack(太多了,也比較常規,就不在這裡貼出來全部配置項了,僅在這裡配置好跟 Vite 一樣的需要相容到最低的瀏覽器版本)

// .browserslistrc
ios >= 9
android >= 4.2
> 1%

至此,準備工作完畢,讓我們看看兩者的構建產物吧。

4. 構建產物

從產物的命名中,我們就能多少看出些許區別,webpack的產物比較簡單,中規中矩,而 Vite 的 JS 檔案不但比 webpack 多,而且部分檔案命名中還多了一個單詞:legacy,百度翻譯對它的解釋是:遺產;遺贈財物;遺留;後遺症;(計算機系統或產品)已停產的,透過翻譯,或許你可以猜出來,名字中帶 legacy 的檔案大機率就是瀏覽器的相容檔案,那麼事實到底是不是這樣呢?

如果你足夠細心,其實你應該可以從上面 Vite 的配置項程式碼中嗅到一絲端倪,在 Vite 的配置檔案中,有一個名為 @vitejs/plugin-legacy 的外掛,它的名字也包含 legacy,Vite 官網中對這個外掛的解釋是這樣的:

傳統瀏覽器可以透過外掛 @vitejs/plugin-legacy 來支援,它將自動生成傳統版本的 chunk 及與其相對應 ES 語言特性方面的 polyfill。相容版的 chunk 只會在不支援原生 ESM 的瀏覽器中進行按需載入。

也就是說,這個外掛它不但提供了低版本瀏覽器的相容能力,還提供了檢測是否支援原生 ESM 的能力。那麼這個外掛都做了哪些事?

主要是以下三點:

  1. 為最每個生成的 ESM 模組化方式的 chunk 也對應生成一個 legacy chunk,同時使用 @babel/preset-env 轉換(沒錯,Vite 的內部整合了 Babel),生成一個 SystemJS 模組,關於 SystemJS 可以看點選這裡檢視,它在瀏覽器中實現了模組化,用來載入有依賴關係的各個 chunk。

  2. 生成 polyfill 包,包含 SystemJS 的執行時,同時包含由要相容的目標瀏覽器版本和程式碼中的高階語法產生的 polyfill。

  3. 生成

相關文章