主打一個“小巧靈動”:Vite + Svelte
一、背景
為了統一技術標準、提升協作效率,通常在前端團隊內部只會保留一套通用的研發框架。尤其是在團隊初創時期,團隊成員會考慮易用性、社區活躍程度、學習成本等因素,選擇一個合適的研發框架并一直推行和使用下去。
國內的前端團隊比較青睞 Vue和 React,我們團隊內部的主要研發框架也是 Vue,包括組件庫、工具庫、腳手架等等,都是圍繞 Vue 展開來做研發。
堅持使用一個技術棧雖然讓團隊協作變得高效,也不用重復“造輪子”,同時提升了人員“流通性”,有它不可忽略的優勢。但沒有任何一款框架是“銀彈”,例如 Vue,它的通用性很好,但在某些特殊場景下,我們會有更好的選擇。
例如,當我們在開發一些小型項目時,會發現至少有兩個明顯的問題:
- 框架太“重”了:通常一個小型項目只由少數幾個簡單頁面構成,如果使用 Vue 或者 React 這些框架來研發的話,有點“大材小用”了。構建的產物中包含了不少框架運行時代碼(虛擬 DOM、響應式、狀態管理等),這些代碼對于小型項目而言是冗余的,它們影響了包體大小,進而影響頁面的啟動速度和執行性能。
- 打包太慢了:以 Vue CLI 為例,它的底層基于 Webpack,雖然 Webpack 具備更強大的功能和靈活性,但相比于 Vite、Esbuild 這些以速度為標桿的構建工具來說,它的速度確實慢了一些,影響了研發效率。
面對這兩個問題,我們似乎有更好的技術方案可選:使用更輕量的 Vite + Svelte。本文就是針對開發小型項目的場景,談談 Vite+Svelte 是如何讓項目變得“小巧靈動”。
注意:本篇所有針對 Svelte 的性能觀點,都是基于小型項目這個前提下提出。事實上,隨著項目規模的增長, Svelte 的性能、包體大小優勢會逐漸減小,甚至不如 Vue 或 React。
理論上在普通 CSR 項目中,組件數量超過19個時, Svelte 就失去了它的包體大小優勢。
二、Vite 和 Svelte 簡介
先了解下 Vite 和 Svelte。
2.1 Vite
Vite 是尤雨溪尤大寫的一款高效的前端構建工具,相比于 Webpack,它最大的優勢就是“快”。
在開發環境下,Vite 使用高性能的 Esbuild 來進行預構建,并利用現代瀏覽器對 ESM 的支持,直接將預構建好的ES模塊丟給瀏覽器進行解析執行,無需在每次變更代碼時都重新編譯代碼,具有更快的冷啟動速度和熱更新效率。
在生產環境下,Vite 基于 Rollup 進行打包,Rollup 同樣支持 ESM 語法,并且具有更快速高效的 Treeshaking,一般情況下,Rollup 具有更小的包體大小和更快的構建速度。
參考 Github 上的構建工具橫向對比 benchmark。
(圖片來源:https://github.com)
可以看到無論是冷啟動、熱更新還是生產環境打包,Vite 都是優于 Webpack 的。尤其是在開發過程中的熱更新很快,大大優化了開發體驗。
2.2 Svelte
Svelte 是由 Rollup 的作者 Rich Harris(前端輪子哥) 寫的一款前端框架。在語法上,Svelte 和 Vue 類似。它和傳統框架(如 Vue、React)的最大差異就在于:在構建階段,Svelte 就將代碼編譯為“純粹”的 JavaScript 代碼,幾乎沒有運行時。
這意味著在小型項目中,它打出來的包更小,在運行時,它也不需要復雜的狀態管理和虛擬 DOM,在性能上的表現也更好。
在前端大佬 Jacek Schae 的前端框架橫向測評中(測評簡述:使用各個前端框架來編寫同一個標準 App - RealWorld,并橫向對比它們的表現),可以看到,Svelte 無論是在首屏渲染速度、包體大小還是代碼行數上都展現了較大的優勢,全部躋身前三。
首屏渲染速度:
(圖片來源:https://medium.com)
包體大小:
代碼行數:
(圖片來源:https://medium.com)
三、搭建 Vite+Svelte 項目
在基本了解了 Vite 和 Svelte 之后,來看看如何著手去搭建一個 Vite+Svelte 的項目。
3.1 全局安裝 Vite
通過 npm 全局安裝 Vite:
npm install vite -g
3.2 創建 Svelte 項目
Vite 原生支持直接通過腳手架創建 Svelte 項目,執行以下命令:
npm create vite@latest
命令行中會出現引導,按照提示輸入項目的名稱并直接選擇 Svelte 框架即可。
? Project name: vite-svelte-demo
? Select a framework: ? - Use arrow-keys. Return to submit.
Vanilla
Vue
React
Preact
Lit
? Svelte
Solid
Qwik
Others
? Select a variant: ? - Use arrow-keys. Return to submit.
TypeScript
? JavaScript
SvelteKit ↗
3.3 運行項目
通過以下命令運行項目:
cd vite-svelte-demo
npm install
npm run dev
這樣一來,整個 Vite + Svelte 的項目結構就搭建好了,開箱即用。整個項目的目錄結構如下:
|-node_modules -- 項目依賴
|-public -- 公共文件
|--vite.svg
|-src -- 源文件
|-assets
|-lib
|--App.svelte -- 項目根組件
|--app.css -- 項目根樣式
|--main.js -- 項目入口文件
|--.gitignore
|-- index.html -- 首頁index.html
|-- package-lock.json
|-- package.json
|-- tsconfig.json
|-- svelte.config.js -- svelte配置文件
|-- vite.config.js -- vite配置文件
可以看到,整個項目結構基本和 Vue 類似,只是組件的后綴名改為了.svelte,新增了 vite.config.js 和 svelte.config.js 兩個配置文件。
3.4 配置
Vite+Svelte 支持開箱即用,項目的初始配置也很簡單,沒有任何額外配置。
vite.config.js:用于配置 Vite 構建工具的行為,例如入口文件、輸出目錄、插件、devServer 等。
// vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
})
svelte.config.js:用于配置 Svelte 項目的各種選項,例如別名、預處理器、插件等。
// svelte.config.js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
preprocess: vitePreprocess(),
}
在工程配置這里,唯一需要注意的是,Vite 默認情況打出的包體僅支持現代瀏覽器(支持 ESM ),如果要兼容低版本瀏覽器,可以使用官方提供的 @vitejs/plugin-legacy插件:
// vite.config.js
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import legacy from "@vitejs/plugin-legacy";
export default defineConfig({
plugins: [
svelte(),
legacy({
// 設置需要兼容的目標瀏覽器版本
targets: [
"Android >= 4.4",
"iOS >= 9",
"ie >= 11",
"not Android < 4.4",
"not iOS < 9",
"not ie < 11",
],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
renderLegacyChunks: true,
}),
});
更多配置項可參考官網的 Vite 配置文檔和 Svelte 配置文檔。
四、開發體驗優化
項目搭建完成后,后續就是根據業務需求來開發 Svelte 組件和完善業務邏輯。整個開發過程中,體驗感還是不錯的。
詳細的開發流程不再贅述,想要了解更多關于 Vite 和 Svelte 內容,可以參考 Vite 官方文檔和 Svelte 官方文檔。這里我們主要來看看 Vite 和 Svelte 分別在開發體驗上帶來的一些優化。
4.1 Vite 的開發體驗太棒了
首先,Vite 的開發體驗太棒了。它的構建速度極快,對開發效率有很明顯的提升。
在開發環境下,Webpack 每次都需要對改動的部分進行重新編譯打包,耗時幾秒鐘,而 Vite 則不需要重新打包,只需要把更新后的 ESM 代碼交付給瀏覽器就ok,幾乎是即時更新。
同時在構建生產環境包時,也明顯比原先的 Vue 項目要快了近50%。別小看這一點速度的提升,讓整個開發體驗好了不少。
4.2 Svelte 的語法更優雅?
如果你跟隨上面步驟搭建了項目,那你可以打開項目中的 Counter.svelte 文件看看,會發現 Svelte 組件的基本結構和 Vue 幾乎一致,也是由 script、template 和 style 三部分組成,唯一的差異在于 Svelte 中不需要像 Vue 一樣額外使用來作為 DOM 結構的根標簽。
<script>
let count = 0
const increment = () => {
count += 1
}
</script>
<button on:click={increment}>
count is {count}
</button>
<style>
...
</style>
在語法上,Svelte 和 Vue 也非常相似,但個人更喜歡 Svelte 的一些簡潔的語法設計,舉一些例子:
插值表達式:Svelte 的插值表達式只需要單個{},而 Vue 則需要兩層{{}},并且 Svelte 還有一些簡寫語法,例如 src={src} 可以簡寫為 {src}。
// Svelte的插值表達式
<div {src}>
count is {count}
</<div>
// Vue的插值表達式
<template>
<div :src="src">
count is {{ count }}
</div>
</template>
響應式:Svelte 聲明的變量直接支持響應式,而在 Vue 中則需要使用 ref 來聲明。
// Svelte
let message = 'Svelte';
// Vue
import { ref } from 'vue';
const message = ref('Vue');
computed 計算屬性 / watch 監聽:Svelte 的$: 代碼塊類似于 Vue 的 computed 計算屬性和 watch 狀態監聽的結合體。
// Svelte的計算屬性
$: info = name + age
// Svelte的監聽
let val
$: {
console.log(`你輸入的內容是: ${val}`);
}
// Vue的計算屬性
import { computed } from 'vue';
const info = computed(() => {
return name + age
});
// Vue的監聽
import { watch } from 'vue';
let val = ref('');
watch(val, (newValue, oldValue) => {
console.log(`你輸入的內容是: ${val}`);
});
雖然差異沒有很大,但這些簡潔的語法,至少讓我個人感覺寫項目時更流暢,體驗感很不錯。
這些就是 Vite 和 Svelte 帶來的開發體驗上的一些優化。
五、Svelte 和 Vue 性能對比
上面我們了解了如何搭建 Vite+Svelte 項目,并感受到 Vite 和 Svelte 帶來的開發體驗優化。那接下來看看這套方案在實際項目中的性能表現。
為了對比 Svelte 和 Vue 之間的性能差異,我特地找了一個小型項目進行改造,用 Svelte 對它重寫,并通過 ABTest 進行線上的性能數據對比。
5.1 首先,來看打包之后的 bundle 大小對比
Vue 的包體:
Svelte 的包體:
是否壓縮 | Vue | Svelte | 差值 |
gzip 前 | 360K | 207K | 降低 42.5% |
gzip 后 | 124K | 77K | 降低 38% |
可以看到 Svelte 相比于 Vue,在包體大小上確實優化了不少,基本在40%左右,效果很明顯。
5.2 其次,看看本地的啟動速度(FCP)對比
在本地進行10次平均性能測試,發現 Svelte 的 FCP 指標比 Vue 要快了約46%,說明 Svelte 的首屏渲染速度比 Vue 快了不少,提升很明顯。
_ | Vue | Svelte |
FCP(ms) | 1128 | 738 |
FCP(ms) | 1897 | 545 |
FCP(ms) | 990 | 701 |
FCP(ms) | 1016 | 608 |
FCP(ms) | 1002 | 620 |
FCP(ms) | 1391 | 587 |
FCP(ms) | 1294 | 662 |
FCP(ms) | 983 | 803 |
FCP(ms) | 1231 | 618 |
FCP(ms) | 1279 | 757 |
FCP 均值(ms) | 1234.7 | 663.9 |
優化程度(%) | - | 46.2% ↑ |
下面這個錄屏是對比兩個頁面首次打開時的效果,左側為 Svelte,右側為 Vue。
5.3 最后,看看線上的啟動速度數據對比
我們通過 ABTest 進行線上的啟動速度數據對比,在投放了1周后,數據結論如下:
平均啟動速度 | |
vue | 2493 ms |
svelte | 2132 ms |
差值 | 361 ms |
優化程度 | 14.5% ↑ |
以看到 Svelte 在線上的啟動速度表現也比 Vue 要快一些,優化程度約為14.5%。
注意:這里的平均啟動速度指標是我們團隊內部定義的頁面啟動速度指標,計算方式:業務啟動速度 = 頁面加載完成時間 - 頁面入口點擊時間。由于我們是在客戶端 App 中進行測試,因此該時間包含了客戶端 webview 的啟動時間。所以實際上 Svelte 對前端部分的啟動速度優化程度可能會更高,參考前面的 FCP 指標的本地測試結果。
總之,對于小型項目,Svelte 在包體大小和啟動速度上還是有不少提升的,如果你的項目對啟動速度有強要求,也可以嘗試使用 Svelte 來開發或改造,應該會有不錯的效果。
六、Svelte 比 Vue 快在哪?
通過上面的數據分析,雖然我們了解到 Svelte 在構建小型項目時確實有更快的頁面啟動速度,但具體快在哪,還需要進一步深入分析。
我們可以通過 Chrome 的 devtool 來觀察 Svelte 和 Vue 兩者打包后的頁面在加載流程上的差異。
Vue 頁面加載流程:
Svelte 頁面加載流程:
對比二者的加載流程,可以看到耗時上有兩個主要的差異點:
耗時差異 | Vue | Svelte |
JS Bundle 加載時間 | 627 ms | 274 ms |
Html/JS 解析耗時 | 292 + 148 + 85 = 525ms | 290 ms |
因此,在小型項目中,Svelte 相比于 Vue,有兩個明顯的優勢:
- 資源加載更快:由于包體較小,所以加載首屏 JS Bundle 時網絡耗時更短。
- Html/JS解析執行更快:由于 Svelte 幾乎無運行時,因此在執行相同業務邏輯時,解析執行耗時更短。并且在解析執行過程中,沒有二次加載更多的首屏 JS、CSS 資源,因此整體的解析執行速度更快。
七、總結
總之,對于小型項目而言,Vite + Svelte 這對組合還是很不錯的,既提升了開發效率,又優化了頁面性能,更加“小巧靈動”。而且這兩者的學習曲線都比較緩和,基本在2-3天內就可以完成學習加一個簡單項目的改造。
一點題外話,很多時候大家會低估“技術嘗新”的重要性,我們被困在團隊圈定的“技術穹頂”中,永遠用一個“套路”去實現所有業務,這會讓我們感到疲乏、缺乏價值感和動力,甚至影響工作的投入程度,久而久之,整個團隊也會在技術上變得遲滯。
所以在有機會時,要盡量合理地發掘和嘗試新的技術方案,一方面是讓自己保持技術活力,另一方面也是在加強團隊的技術沉淀。