主打一個(gè)“小巧靈動(dòng)”:Vite + Svelte

作者:來(lái)自 vivo 互聯(lián)網(wǎng)大前端團(tuán)隊(duì)- Wei Xing

在研發(fā)小型項(xiàng)目時(shí),傳統(tǒng)的 Vue、React 顯得太“笨重”。本文主要針對(duì)開(kāi)發(fā)小型項(xiàng)目的場(chǎng)景,談?wù)?Vite+Svelte 是如何讓項(xiàng)目變得“小巧靈動(dòng)”,并橫向?qū)Ρ?Svelte 和 Vue 的性能表現(xiàn),對(duì)二者的加載流程做詳細(xì)分析。

一、背景

為了統(tǒng)一技術(shù)標(biāo)準(zhǔn)、提升協(xié)作效率,通常在前端團(tuán)隊(duì)內(nèi)部只會(huì)保留一套通用的研發(fā)框架。尤其是在團(tuán)隊(duì)初創(chuàng)時(shí)期,團(tuán)隊(duì)成員會(huì)考慮易用性、社區(qū)活躍程度、學(xué)習(xí)成本等因素,選擇一個(gè)合適的研發(fā)框架并一直推行和使用下去。

國(guó)內(nèi)的前端團(tuán)隊(duì)比較青睞 Vue和 React,我們團(tuán)隊(duì)內(nèi)部的主要研發(fā)框架也是 Vue,包括組件庫(kù)、工具庫(kù)、腳手架等等,都是圍繞 Vue 展開(kāi)來(lái)做研發(fā)。

堅(jiān)持使用一個(gè)技術(shù)棧雖然讓團(tuán)隊(duì)協(xié)作變得高效,也不用重復(fù)“造輪子”,同時(shí)提升了人員“流通性”,有它不可忽略的優(yōu)勢(shì)。但沒(méi)有任何一款框架是“銀彈”,例如 Vue,它的通用性很好,但在某些特殊場(chǎng)景下,我們會(huì)有更好的選擇。

例如,當(dāng)我們?cè)陂_(kāi)發(fā)一些小型項(xiàng)目時(shí),會(huì)發(fā)現(xiàn)至少有兩個(gè)明顯的問(wèn)題:

  • 框架太“重”了:通常一個(gè)小型項(xiàng)目只由少數(shù)幾個(gè)簡(jiǎn)單頁(yè)面構(gòu)成,如果使用 Vue 或者 React 這些框架來(lái)研發(fā)的話,有點(diǎn)“大材小用”了。構(gòu)建的產(chǎn)物中包含了不少框架運(yùn)行時(shí)代碼(虛擬 DOM、響應(yīng)式、狀態(tài)管理等),這些代碼對(duì)于小型項(xiàng)目而言是冗余的,它們影響了包體大小,進(jìn)而影響頁(yè)面的啟動(dòng)速度和執(zhí)行性能。

  • 打包太慢了:以 Vue CLI 為例,它的底層基于 Webpack,雖然 Webpack 具備更強(qiáng)大的功能和靈活性,但相比于 Vite、Esbuild 這些以速度為標(biāo)桿的構(gòu)建工具來(lái)說(shuō),它的速度確實(shí)慢了一些,影響了研發(fā)效率。

面對(duì)這兩個(gè)問(wèn)題,我們似乎有更好的技術(shù)方案可選:使用更輕量的 Vite + Svelte。本文就是針對(duì)開(kāi)發(fā)小型項(xiàng)目的場(chǎng)景,談?wù)?Vite+Svelte 是如何讓項(xiàng)目變得“小巧靈動(dòng)”。

注意:本篇所有針對(duì) Svelte 的性能觀點(diǎn),都是基于小型項(xiàng)目這個(gè)前提下提出。事實(shí)上,隨著項(xiàng)目規(guī)模的增長(zhǎng), Svelte 的性能、包體大小優(yōu)勢(shì)會(huì)逐漸減小,甚至不如 Vue 或 React,詳情可參考尤雨溪尤大本人針對(duì) Svelte 和 Vue3的包體大小問(wèn)題的分析。

理論上在普通 CSR 項(xiàng)目中,組件數(shù)量超過(guò)19個(gè)時(shí), Svelte 就失去了它的包體大小優(yōu)勢(shì)。

二、Vite 和 Svelte 簡(jiǎn)介

先了解下 Vite 和 Svelte。

2.1 Vite

Vite 是尤雨溪尤大寫(xiě)的一款高效的前端構(gòu)建工具,相比于 Webpack,它最大的優(yōu)勢(shì)就是“”。

在開(kāi)發(fā)環(huán)境下,Vite 使用高性能的 Esbuild 來(lái)進(jìn)行預(yù)構(gòu)建,并利用現(xiàn)代瀏覽器對(duì) ESM 的支持,直接將預(yù)構(gòu)建好的ES模塊丟給瀏覽器進(jìn)行解析執(zhí)行,無(wú)需在每次變更代碼時(shí)都重新編譯代碼,具有更快的冷啟動(dòng)速度和熱更新效率。

在生產(chǎn)環(huán)境下,Vite 基于 Rollup 進(jìn)行打包,Rollup 同樣支持 ESM 語(yǔ)法,并且具有更快速高效的 Treeshaking,一般情況下,Rollup 具有更小的包體大小和更快的構(gòu)建速度。

參考 github 上的構(gòu)建工具橫向?qū)Ρ?benchmark。

圖片來(lái)源:https://github.com

可以看到無(wú)論是冷啟動(dòng)、熱更新還是生產(chǎn)環(huán)境打包,Vite 都是優(yōu)于 Webpack 的。尤其是在開(kāi)發(fā)過(guò)程中的熱更新很快,大大優(yōu)化了開(kāi)發(fā)體驗(yàn)。

2.2 Svelte

Svelte 是由 Rollup 的作者 Rich Harris(前端輪子哥) 寫(xiě)的一款前端框架。在語(yǔ)法上,Svelte 和 Vue 類似。它和傳統(tǒng)框架(如 Vue、React)的最大差異就在于:在構(gòu)建階段,Svelte 就將代碼編譯為“純粹”的 JavaScript 代碼,幾乎沒(méi)有運(yùn)行時(shí)。

這意味著在小型項(xiàng)目中,它打出來(lái)的包更小,在運(yùn)行時(shí),它也不需要復(fù)雜的狀態(tài)管理和虛擬 DOM,在性能上的表現(xiàn)也更好。

在前端大佬 Jacek Schae 的前端框架橫向測(cè)評(píng)中(測(cè)評(píng)簡(jiǎn)述:使用各個(gè)前端框架來(lái)編寫(xiě)同一個(gè)標(biāo)準(zhǔn) App - RealWorld,并橫向?qū)Ρ人鼈兊谋憩F(xiàn)),可以看到,Svelte 無(wú)論是在首屏渲染速度、包體大小還是代碼行數(shù)上都展現(xiàn)了較大的優(yōu)勢(shì),全部躋身前三。

首屏渲染速度

圖片來(lái)源:https://medium.com

包體大小:

圖片來(lái)源:https://medium.com

代碼行數(shù):

圖片來(lái)源:https://medium.com

三、搭建 Vite+Svelte 項(xiàng)目

在基本了解了 Vite 和 Svelte 之后,來(lái)看看如何著手去搭建一個(gè) Vite+Svelte 的項(xiàng)目。

3.1 全局安裝 Vite

通過(guò) npm 全局安裝 Vite:

npm install vite -g

3.2 創(chuàng)建 Svelte 項(xiàng)目

Vite 原生支持直接通過(guò)腳手架創(chuàng)建 Svelte 項(xiàng)目,執(zhí)行以下命令:

npm create vite@latest

命令行中會(huì)出現(xiàn)引導(dǎo),按照提示輸入項(xiàng)目的名稱并直接選擇 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 運(yùn)行項(xiàng)目

通過(guò)以下命令運(yùn)行項(xiàng)目:

cd vite-svelte-demo
npm install
npm run dev

這樣一來(lái),整個(gè) Vite + Svelte 的項(xiàng)目結(jié)構(gòu)就搭建好了,開(kāi)箱即用。整個(gè)項(xiàng)目的目錄結(jié)構(gòu)如下:

|-node_modules        -- 項(xiàng)目依賴
|-public              -- 公共文件
  |--vite.svg        
|-src                 -- 源文件
  |-assets           
  |-lib               
  |--App.svelte       -- 項(xiàng)目根組件
  |--app.css          -- 項(xiàng)目根樣式
  |--main.js          -- 項(xiàng)目入口文件
|--.gitignore         
|-- index.html        -- 首頁(yè)index.html
|-- package-lock.json 
|-- package.json      
|-- tsconfig.json 
|-- svelte.config.js  -- svelte配置文件
|-- vite.config.js    -- vite配置文件

可以看到,整個(gè)項(xiàng)目結(jié)構(gòu)基本和 Vue 類似,只是組件的后綴名改為了.svelte,新增了 vite.config.js 和 svelte.config.js 兩個(gè)配置文件。

3.4 配置

Vite+Svelte 支持開(kāi)箱即用,項(xiàng)目的初始配置也很簡(jiǎn)單,沒(méi)有任何額外配置。

vite.config.js:用于配置 Vite 構(gòu)建工具的行為,例如入口文件、輸出目錄、插件、devServer 等。

// vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

export default defineConfig({
  plugins: [svelte()],
})

svelte.config.js:用于配置 Svelte 項(xiàng)目的各種選項(xiàng),例如別名、預(yù)處理器、插件等。

// svelte.config.js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'

export default {
  preprocess: vitePreprocess(),
}

在工程配置這里,唯一需要注意的是,Vite 默認(rèn)情況打出的包體僅支持現(xiàn)代瀏覽器(支持 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({
      // 設(shè)置需要兼容的目標(biāo)瀏覽器版本
      targets: [
        "Android >= 4.4",
        "iOS >= 9",
        "ie >= 11",
        "not Android < 4.4",
        "not iOS < 9",
        "not ie < 11",
      ],
      additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
      renderLegacyChunks: true,
    }),
});

更多配置項(xiàng)可參考官網(wǎng)的 Vite 配置文檔和 Svelte 配置文檔。

四、開(kāi)發(fā)體驗(yàn)優(yōu)化

項(xiàng)目搭建完成后,后續(xù)就是根據(jù)業(yè)務(wù)需求來(lái)開(kāi)發(fā) Svelte 組件和完善業(yè)務(wù)邏輯。整個(gè)開(kāi)發(fā)過(guò)程中,體驗(yàn)感還是不錯(cuò)的。

詳細(xì)的開(kāi)發(fā)流程不再贅述,想要了解更多關(guān)于 Vite 和 Svelte 內(nèi)容,可以參考 Vite 官方文檔和 Svelte 官方文檔。這里我們主要來(lái)看看 Vite 和 Svelte 分別在開(kāi)發(fā)體驗(yàn)上帶來(lái)的一些優(yōu)化。

4.1 Vite 的開(kāi)發(fā)體驗(yàn)太棒了

首先,Vite 的開(kāi)發(fā)體驗(yàn)太棒了。它的構(gòu)建速度極快,對(duì)開(kāi)發(fā)效率有很明顯的提升。

在開(kāi)發(fā)環(huán)境下,Webpack 每次都需要對(duì)改動(dòng)的部分進(jìn)行重新編譯打包,耗時(shí)幾秒鐘,而 Vite 則不需要重新打包,只需要把更新后的 ESM 代碼交付給瀏覽器就ok,幾乎是即時(shí)更新。

同時(shí)在構(gòu)建生產(chǎn)環(huán)境包時(shí),也明顯比原先的 Vue 項(xiàng)目要快了近50%。別小看這一點(diǎn)速度的提升,讓整個(gè)開(kāi)發(fā)體驗(yàn)好了不少。

4.2 Svelte 的語(yǔ)法更優(yōu)雅?

如果你跟隨上面步驟搭建了項(xiàng)目,那你可以打開(kāi)項(xiàng)目中的 Counter.svelte 文件看看,會(huì)發(fā)現(xiàn) Svelte 組件的基本結(jié)構(gòu)和 Vue 幾乎一致,也是由 script、template 和 style 三部分組成,唯一的差異在于 Svelte 中不需要像 Vue 一樣額外使用來(lái)作為 DOM 結(jié)構(gòu)的根標(biāo)簽。

<script>
  let count = 0
  const increment = () => {
    count += 1
  }
</script>

<button on:click={increment}>
  count is {count}
</button>

<style>
  ...
</style>

在語(yǔ)法上,Svelte 和 Vue 也非常相似,但個(gè)人更喜歡 Svelte 的一些簡(jiǎn)潔的語(yǔ)法設(shè)計(jì),舉一些例子:

插值表達(dá)式:Svelte 的插值表達(dá)式只需要單個(gè){},而 Vue 則需要兩層{{}},并且 Svelte 還有一些簡(jiǎn)寫(xiě)語(yǔ)法,例如 src={src} 可以簡(jiǎn)寫(xiě)為 {src}。

// Svelte的插值表達(dá)式
<div {src}>
  count is {count}
</<div>

// Vue的插值表達(dá)式
<template>
  <div :src="src">
    count is {{ count }}
  </div>
</template>

響應(yīng)式:Svelte 聲明的變量直接支持響應(yīng)式,而在 Vue 中則需要使用 ref 來(lái)聲明。

// Svelte
let message = 'Svelte';

// Vue
import { ref } from 'vue';
const message = ref('Vue');

computed 計(jì)算屬性 / watch 監(jiān)聽(tīng):Svelte 的$: 代碼塊類似于 Vue 的 computed 計(jì)算屬性和 watch 狀態(tài)監(jiān)聽(tīng)的結(jié)合體。

// Svelte的計(jì)算屬性
$: info = name + age

// Svelte的監(jiān)聽(tīng)
let val
$: {
  console.log(`你輸入的內(nèi)容是: ${val}`);
}

// Vue的計(jì)算屬性
import { computed } from 'vue';
const info = computed(() => {
  return name + age
});

// Vue的監(jiān)聽(tīng)
import { watch } from 'vue';

let val = ref(''); 
watch(val, (newValue, oldValue) => { 
  console.log(`你輸入的內(nèi)容是: ${val}`);
});

雖然差異沒(méi)有很大,但這些簡(jiǎn)潔的語(yǔ)法,至少讓我個(gè)人感覺(jué)寫(xiě)項(xiàng)目時(shí)更流暢,體驗(yàn)感很不錯(cuò)。

這些就是 Vite 和 Svelte 帶來(lái)的開(kāi)發(fā)體驗(yàn)上的一些優(yōu)化。

五、Svelte 和 Vue 性能對(duì)比

上面我們了解了如何搭建 Vite+Svelte 項(xiàng)目,并感受到 Vite 和 Svelte 帶來(lái)的開(kāi)發(fā)體驗(yàn)優(yōu)化。那接下來(lái)看看這套方案在實(shí)際項(xiàng)目中的性能表現(xiàn)。

為了對(duì)比 Svelte 和 Vue 之間的性能差異,我特地找了一個(gè)小型項(xiàng)目進(jìn)行改造,用 Svelte 對(duì)它重寫(xiě),并通過(guò) ABTest 進(jìn)行線上的性能數(shù)據(jù)對(duì)比。

5.1 首先,來(lái)看打包之后的 bundle 大小對(duì)比

Vue 的包體

Svelte 的包體


可以看到 Svelte 相比于 Vue,在包體大小上確實(shí)優(yōu)化了不少,基本在40%左右,效果很明顯。

5.2 其次,看看本地的啟動(dòng)速度(FCP)對(duì)比

在本地進(jìn)行10次平均性能測(cè)試,發(fā)現(xiàn) Svelte 的 FCP 指標(biāo)比 Vue 要快了約46%,說(shuō)明 Svelte 的首屏渲染速度比 Vue 快了不少,提升很明顯。

下面這個(gè)錄屏是對(duì)比兩個(gè)頁(yè)面首次打開(kāi)時(shí)的效果,左側(cè)為 Svelte,右側(cè)為 Vue。

5.3 最后,看看線上的啟動(dòng)速度數(shù)據(jù)對(duì)比

我們通過(guò) ABTest 進(jìn)行線上的啟動(dòng)速度數(shù)據(jù)對(duì)比,在投放了1周后,數(shù)據(jù)結(jié)論如下:

可以看到 Svelte 在線上的啟動(dòng)速度表現(xiàn)也比 Vue 要快一些,優(yōu)化程度約為14.5%。

注意:這里的平均啟動(dòng)速度指標(biāo)是我們團(tuán)隊(duì)內(nèi)部定義的頁(yè)面啟動(dòng)速度指標(biāo),計(jì)算方式:業(yè)務(wù)啟動(dòng)速度 = 頁(yè)面加載完成時(shí)間 - 頁(yè)面入口點(diǎn)擊時(shí)間。由于我們是在客戶端 App 中進(jìn)行測(cè)試,因此該時(shí)間包含了客戶端 webview 的啟動(dòng)時(shí)間。所以實(shí)際上 Svelte 對(duì)前端部分的啟動(dòng)速度優(yōu)化程度可能會(huì)更高,參考前面的 FCP 指標(biāo)的本地測(cè)試結(jié)果。

總之,對(duì)于小型項(xiàng)目,Svelte 在包體大小和啟動(dòng)速度上還是有不少提升的,如果你的項(xiàng)目對(duì)啟動(dòng)速度有強(qiáng)要求,也可以嘗試使用 Svelte 來(lái)開(kāi)發(fā)或改造,應(yīng)該會(huì)有不錯(cuò)的效果。

六、Svelte 比 Vue 快在哪?

通過(guò)上面的數(shù)據(jù)分析,雖然我們了解到 Svelte 在構(gòu)建小型項(xiàng)目時(shí)確實(shí)有更快的頁(yè)面啟動(dòng)速度,但具體快在哪,還需要進(jìn)一步深入分析。

我們可以通過(guò) Chrome 的 devtool 來(lái)觀察 Svelte 和 Vue 兩者打包后的頁(yè)面在加載流程上的差異。

Vue 頁(yè)面加載流程

Svelte 頁(yè)面加載流程

對(duì)比二者的加載流程,可以看到耗時(shí)上有兩個(gè)主要的差異點(diǎn):


因此,在小型項(xiàng)目中,Svelte 相比于 Vue,有兩個(gè)明顯的優(yōu)勢(shì):

  • 資源加載更快:由于包體較小,所以加載首屏 JS Bundle 時(shí)網(wǎng)絡(luò)耗時(shí)更短。

  • Html/JS解析執(zhí)行更快:由于 Svelte 幾乎無(wú)運(yùn)行時(shí),因此在執(zhí)行相同業(yè)務(wù)邏輯時(shí),解析執(zhí)行耗時(shí)更短。并且在解析執(zhí)行過(guò)程中,沒(méi)有二次加載更多的首屏 JS、CSS 資源,因此整體的解析執(zhí)行速度更快。

七、總結(jié)

總之,對(duì)于小型項(xiàng)目而言,Vite + Svelte 這對(duì)組合還是很不錯(cuò)的,既提升了開(kāi)發(fā)效率,又優(yōu)化了頁(yè)面性能,更加“小巧靈動(dòng)”。而且這兩者的學(xué)習(xí)曲線都比較緩和,基本在2-3天內(nèi)就可以完成學(xué)習(xí)加一個(gè)簡(jiǎn)單項(xiàng)目的改造。

一點(diǎn)題外話,很多時(shí)候大家會(huì)低估“技術(shù)嘗新”的重要性,我們被困在團(tuán)隊(duì)圈定的“技術(shù)穹頂”中,永遠(yuǎn)用一個(gè)“套路”去實(shí)現(xiàn)所有業(yè)務(wù),這會(huì)讓我們感到疲乏、缺乏價(jià)值感和動(dòng)力,甚至影響工作的投入程度,久而久之,整個(gè)團(tuán)隊(duì)也會(huì)在技術(shù)上變得遲滯。

所以在有機(jī)會(huì)時(shí),要盡量合理地發(fā)掘和嘗試新的技術(shù)方案,一方面是讓自己保持技術(shù)活力,另一方面也是在加強(qiáng)團(tuán)隊(duì)的技術(shù)沉淀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容