首頁  >  文章  >  web前端  >  Svelte igration 的經驗與注意事項

Svelte igration 的經驗與注意事項

Barbara Streisand
Barbara Streisand原創
2024-11-04 10:46:30832瀏覽

Experiences and Caveats of Svelte igration

我最近更新了一個相當複雜的網路應用程式。該應用程式具有 auth、Stripe、i18n、dark/light 模式、PWA 等功能。總體而言,它有大約 30 個頁面和元件,幾乎沒有第三方 npm 套件。

我想指出在將應用程式移轉到 Svelte 5 時我發現非常具有挑戰性的事情。

自動遷移腳本錘

Svelte 提供的自動遷移腳本可以在終端npx sv migrate svelte-5 中使用這個「one-liner」指令為您完成這項工作(在完成所有必要的更新並安裝之後:「@sveltejs/vite - plugin-svelte」:「^4.0.0」和「svelte」:「^5」)。但我並不推薦這種「錘子」的做法。

使用Ctrl Shift P (Windows/Linux) / Shift Command P (Mac) 逐一檔案、逐個元件地進行操作,並使用Migrate Component to Svelte 5 Syntax 指令而是使用VS Code 指令面板。這樣你就會有更多的控制權。

已棄用的 run() 驚喜

腳本無法創造奇蹟。將反應式變數宣告升級到 $state() 通常就可以了。但是,腳本可能很難偵測 $: 是否要轉換為 $driven()/$衍生.by(() => {}) 或 $effect(() => {})。

那麼,你猜怎麼著?使用自動遷移腳本,您最終可能會得到大量 run(() => {})。

例如,想像一個使用以下內容的簡化範例:

<script>
...
   let notext = false;
   $: if (data.completeDoc == 'NoLangVersion') {
      notext = true;
   }
   $: if (data.completeDoc !== 'NoLangVersion') {
      notext = false;
   }
</script>

...
{#if notext}
   {data.userPrefferedLang.noTextWarning}
{:else}
...
{/if}
...

自動遷移腳本將為您提供:

<script>
    import { run } from 'svelte/legacy';
...
    let notext = $state(false);
    run(() => {
        if (data.completeDoc == 'NoLangVersion') {
            notext = true;
        }
    });
    run(() => {
        if (data.completeDoc !== 'NoLangVersion') {
            notext = false;
        }
    });
</script>

有一個很好的小警告,run 函數已被棄用

我猜更好的 Svelte 5 程式碼是這樣的:

<script>
...
    let notext = $derived.by(() => {
        if (data.completeDoc == 'NoLangVersion') {
            return  true;
        }
        if (data.completeDoc !== 'NoLangVersion') {
            return false;
        }
    });
...
</script>

或如果你的程式碼並不複雜,即使是這樣:

<script>
...
    let notext = $derived(
        data.completeDoc == 'NoLangVersion' 
        ? 
        true
        :
        false
        ) 
...
</script>

原因是腳本無法輕鬆地將程式碼轉換為 $categories.by(() => {}),因此它希望使用更髒的方法 $effect()。但 $effect() 僅在客戶端運行,因此腳本使用已棄用的 run 函數。

如果可以的話避免$效應

現在我們得到了最重要的結論。 $effect() 僅在客戶端運行。所以伺服器上沒有 $effect(),用於預渲染頁面和 SSR。

$effect() 不在伺服器上運作!

Svelte 5 文件中應該要強調這一點。

看這兩個例子:

<script>
let a = 1
let b = 2

$: c = a + b
</script>

{c}  // server responds with c == 3
<script>
let a = $state(1)
let b = $state(2)
let c = $state(0)

$effect(() => {
  c = a + b
})
</script>

{c}  // server responds with c == 0

它們不一樣。這帶來了很多挑戰。客戶端在安裝頁面時需要重新評估 c 變數。從伺服器發送的頁面和最終在客戶端進行 DOM 渲染時的頁面看起來會有所不同(SSR、SEO、閃爍問題等)。

因此,請務必嘗試使用 $衍生或 $衍生.by(() => {}) 而不是 $effect()。這會幫你省去很多麻煩。

這與我們被勸阻不要在 SvelteKit 和 SSR 中使用商店時的情況完全相同。

SvelteKit 中的 $effect 與 onMount()

由於 Svelte 5 到來期間給出的範例,您可能會想用 $effect() 取代 SvelteKit 中的 onMount() 。由於已經提到的原因,我暫時不鼓勵這樣做。 onMount 仍然是核心 Svelte 生命週期掛鉤。

$bindable $props 驚喜

另一個令人驚訝的地方是 Svelte 5 非常注意變數值的一致性。如果您將變數作為 prop 傳遞給元件,並稍後在元件中變更此變量,腳本將嘗試使用 $bindable $prop 解決此不一致問題。應通知家長,以便您的應用程式狀態保持一致。

看這個例子:

<script>
...
   let notext = false;
   $: if (data.completeDoc == 'NoLangVersion') {
      notext = true;
   }
   $: if (data.completeDoc !== 'NoLangVersion') {
      notext = false;
   }
</script>

...
{#if notext}
   {data.userPrefferedLang.noTextWarning}
{:else}
...
{/if}
...

自動遷移腳本將要求您使用具有綁定值的元件,以確保父級可以取回更新的值:

<script>
    import { run } from 'svelte/legacy';
...
    let notext = $state(false);
    run(() => {
        if (data.completeDoc == 'NoLangVersion') {
            notext = true;
        }
    });
    run(() => {
        if (data.completeDoc !== 'NoLangVersion') {
            notext = false;
        }
    });
</script>

但也許我們也可以使用更簡單的方法,你猜對了,使用 $衍生():

<script>
...
    let notext = $derived.by(() => {
        if (data.completeDoc == 'NoLangVersion') {
            return  true;
        }
        if (data.completeDoc !== 'NoLangVersion') {
            return false;
        }
    });
...
</script>

:global { } 塊

我在遷移過程中發現的一個非常好的功能是我們現在可以使用 CSS :global 和 block。例如,如果您想在 @html 中設定 HTML 元素的樣式,則使用 :global 進行樣式設定是非常必要的。

所以代替這個:

<script>
...
    let notext = $derived(
        data.completeDoc == 'NoLangVersion' 
        ? 
        true
        :
        false
        ) 
...
</script>

你可以用這個:

<script>
let a = 1
let b = 2

$: c = a + b
</script>

{c}  // server responds with c == 3

樣式作為組件中的道具

在 Svelte 4 中,如果您想要提供 CSS 類別作為元件的 prop,您可以使用 {$$props.class}:

<script>
let a = $state(1)
let b = $state(2)
let c = $state(0)

$effect(() => {
  c = a + b
})
</script>

{c}  // server responds with c == 0

在 Svelte 5 中你可以使用 class={className}:

// parent svelte file
<script>
   import ComponentBinded from './ComponentBinded.svelte';
   import ComponentWithDerived from './ComponentWithDerived.svelte';
   let name = $state('John Wick');
</script>

<p>Name value in parent: {name}</p>

<ComponentBinded bind:name={name} />

<ComponentWithDerived {name} />

燈塔性能可能下降

當我使用自動合併腳本時,我對應用程式的效能下降感到震驚。有了 Svelte 4,我幾乎獲得了 100% 的成績。直到我手動遷移並仔細考慮如何(主要是如果可能的話如何避免 $effect())後,我的 Lighthouse 分數才再次回到綠色。

最後的話

遷移到 Svelte 5 的時間比我預期的要長。不過,我還沒有將這個新版本投入生產。 Svelte 5 的更新頻率仍然相當高。

希望我的經驗對其他人有用。

以上是Svelte igration 的經驗與注意事項的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn