ホームページ  >  記事  >  ウェブフロントエンド  >  新しい Svelte veactivity システムを学ぶ

新しい Svelte veactivity システムを学ぶ

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-10-26 08:24:30138ブラウズ

それで、Svelte v5 は、おそらく現存する中で最高のフロントエンド フレームワークの最新版がリリースされましたが、以前のバージョンとは大きく異なります。主な違いはその核心、つまり変数の反応性がどのように実装されるかにあります。これらの変更により、Svelte は簡単になりましたが、同時に少し難しくなりました。

私は v5@next.155 以来、実際のマイクロフロントエンド プロジェクトで Svelte v5 を熱心に使ってきたため、私が得た知識を皆さんに役立てるためにこのシリーズの記事を書くことにしました。コードを理解し、受け入れ、潜在的に Svelte v5 に移行してください。

簡単な紹介: Svelte v4 の反応性

Svelte v4 の反応性システムはまさに芸術作品です。Svelte はコンポーネント内のコードを静的に分析し、通常の JavaScript 変数が変更されるたびに DOM を強制的に変更するコードを生成します。シンプルでエレガント、そして非常に高性能です。簡単な例:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document on:click={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

この単純なコンポーネントは、「クリック」イベント リスナーをドキュメント オブジェクトに追加し、クリックをカウントします。上記のコードだけでクリック数がリアルタイムに表示されます。すごいですよね?

しかし、人生のすべてのことと同様、これも完璧ではありません。注意点については Rich Harris (Svelte の作成者) が説明していますが、この記事では説明しません。 1 つだけ触れておきます: コードのリファクタリング

コードのリファクタリングは不可能です

より重要な注意点の 1 つは、この反応性システムをコンポーネントの外部に持ち運ぶことができないことです。たとえば、この例の countClicks 関数の実装をカプセル化する再利用可能なモジュールを作成することはできません。

反応性は静的解析に依存するため、関数を取り出してモジュールに取り込むと、変数の突然変異が静的コード アナライザーに隠蔽され、反応性が失われます。

Svelte v5: Runes の反応性

ルーンという用語は「魔法のシンボル」を指し、スベルトが次の機能に見える「魔法」用語を命名するために採用した用語です。

  • $state

  • $props

  • $バインド可能

  • $派生

  • $effect

Svelte v5 の反応性は、これらのルーンの使用によって制御されます。

R 値のように見えますが、生成されるコードは実際には L 値であることに注意することが重要です。言い換えれば、変数から変数へ状態を渡すことができるとは考えないでください。これについては、以下の $state rune セクションで詳しく説明します。

この新しい反応性システムの主な利点は次のとおりです:

  • コンポーネントの外部でリアクティブ コードをリファクタリングする機能

  • きめ細かい反応性

前者は、コンポーネントの外側にリアクティブな変数を持つことができることを意味します。後者は、状態の変化に反応する際に、コンポーネントの再レンダリングがよりターゲットを絞ったものであることを意味します。

コンポーネントの外部に状態を保持する機能については、この記事では説明しませんが、これについては記事が掲載される予定なので、このシリーズに従ってください。

一方、きめ細かい反応性とは、Svelte が状態オブジェクトで変更されたプロパティを認識できるようになり、その特定のプロパティによって影響を受けるもののみを再レンダリング (エフェクトの再実行と派生値の再計算) できることを意味します。のみ。これにより、パフォーマンスが大幅に向上する場合があります。簡単な例として、大きなテーブル コンポーネントのデータに新しい行が追加された場合、Svelte は新しい行のみをレンダリングします。 3 行目の 1 つのセルの値が変更された場合、その値を表示するセルのみが再レンダリングされます。もう明らかですか?そうであれば幸いですが、そうでない場合は、コメントセクションで私に連絡してください。

$state ルーン

このルーンは反応状態を作成するために使用されます。上記の Svelte v4 コード サンプルを書き直してみましょう:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document on:click={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

Svelte v4 で行ったのと同じ結果を達成するには、0 の代わりに $state(0) を使用しました。

このルーンを管理する主なルールは、変数またはクラス フィールドの初期化にのみ使用できるということです。これは、先ほど読んだ重要な注意事項と関係があります。つまり、ルーンは構文的には関数のように見えますが、実際は違います。コンパイラは、値を計算して返すという関数の動作の概念と互換性のないコードでルーン文字を置き換えます。これは、以下が 2 番目のリアクティブ変数を作成しないことを意味します:

<script lang="ts">
    let clickCount = $state(0);

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document onclick={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

clickCount の反応的な性質は、代入演算子を使用することによって SecondClickCount に転送またはコピーされません。ルーン文字が関数である場合、上記は機能するでしょう。しかし、それらは機能しません

$state について言及すべき重要な点がもう 1 つあります。それは、その値を非常にリアクティブにするということです。これは、値がオブジェクトのプロパティにオブジェクトが含まれている場合、含まれているオブジェクトのプロパティもリアクティブであることを意味します。このパターンは再帰的に適用されるため、オブジェクト グラフ全体が最終的にリアクティブになります。

$props ルーン

コンポーネントのプロパティはリアクティブであることが期待されており、Svelte v5 は $props ルーンを使用してこれを実現します。

<script lang="ts">
    let clickCount = $state(0);
    let secondClickCount = clickCount;

    function countClicks() {
        ++clickCount;
    }
</script>

プロジェクトでは TypeScript を使用することがすべてであるため、型を使用してコンポーネントのプロパティを宣言することから始めます。オプションのプロパティは、? を追加することでマークされます。その名前に。

次に、構造を解除するステートメントであるルーンの使用が始まります。この例では、デフォルト値を割り当てる方法と、その他のプロパティである「rest」プロパティを許可する方法を示します。コンポーネントは、この「残りの」プロパティを、span HTML 要素のプロパティ (属性) として展開 (適用) します。

let props を実行することもできます: Props = $props();プロパティを定義すると機能しますが、さまざまなプロパティのデフォルトを指定できないため、常に示されているようにプロパティを宣言することをお勧めします。ちなみに、構造を解除しない場合は、restProperties を宣言する方法もわかりません。

注意していただければ、上記のコードでは TypeScript エラーが発生します。結局のところ、Props タイプには「残り」プロパティについての言及はありません。 restProps を入力するにはどうすればよいですか?

一般的に言えば、次のようなことを行うと、あらゆる種類のものを許可できます。それはあなたの TypeScript スキル次第だと思います。

以下は、任意の data-* 属性を許可する Props タイプを開きます:

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document on:click={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

これは何でも許可します:

<script lang="ts">
    let clickCount = $state(0);

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document onclick={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

しかし、多くの場合、restProps を受け取る HTML 要素の属性を許可する必要があります。この例では、それは Span HTML 要素でした。

この一般的なシナリオのために、Svelte v5 は HTML 要素のほとんどをカバーする型を提供します。

<script lang="ts">
    let clickCount = $state(0);
    let secondClickCount = clickCount;

    function countClicks() {
        ++clickCount;
    }
</script>

後者を使用すると、VS Code のような GUI が、span HTML 要素の可能な props (属性) に対して正確な Intellisense を提供します。いいですね?

HTMLAttributesインターフェースは、プロパティのリストに特徴のない HTML 要素に使用されます。ただし、多くの要素にはそれがあります。たとえば、HTMLAttributes を実行するのではなく、'svelte/elements' から HTMLButtonAttributes インターフェイスをインポートします。

最後の詳細はデフォルト値です。言うべきことはあまりありません。この例がすべてを物語っています。操作 prop のデフォルト値は 'sum' です。コンポーネントの使用時にプロパティが指定されていない場合、それがプロパティの値となります。

必要なデフォルトが未定義の場合は、何も指定しないでください。

$bindable ルーン

これは、コンポーネントのプロパティでのみ使用できる非常に特殊なルーンです。プロパティをバインド可能としてマークします。

知らない、または思い出せない場合は、Svelte ではプロパティの双方向バインドが可能です。 Vue にもこの機能がありますが、対照的に、React にはありません。

使用方法は非常に簡単です:

<script lang="ts">
    type Props = {
        data: number[];
        operation?: 'sum', 'avg';
    };

    let {
        data,
        operation = 'sum',
        ...restProps,
    }: Props = $props();

    function sum() {
        return data.reduce((p, c) => p + c);
    }

    function avg() {
        return sum() / data.length
    }
</script>

<span class="amount" {...restProps}>{operation === 'sum' ? sum() : avg()}</span>

<style>
    .amount {
        font-family: monospace;
    }
</style>

値が変更されるプロパティは常にバインド可能にします。そうしないと、Svelte がコンソールに警告を表示します。この警告には、コンポーネントがそのコンポーネントに属さない状態を変更してはならないこと、およびこれが意図されている場合はバインディングを使用する必要があることが記載されています。

例に示すように、$bindable rune を通じてプロパティのデフォルトを指定できます。この例では、プロパティのデフォルトを 5 に設定します。

しかし、ここでデフォルトは意味があるのでしょうか?そうですね。プロパティをバインド可能として宣言しても、必須になるわけではありません。

$派生ルーン

プロパティまたはその他の反応状態 (時間の経過とともに変化する可能性がある) からの値を使用して値を計算する必要がある場合は、$derived rune を使用します。

合計と平均を計算するサンプル コンポーネントを元に戻し、次のルーンを使用して書き直すことができます。

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document on:click={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

これで、入力と同様に反応性があり、データ配列内のデータが変更されるたびに自動的に再計算される、result という名前の新しい変数ができました。これ自体はリアクティブ変数であるため、それを使用するテンプレート (コンポーネントの HTML 部分) も更新されます。

$effect ルーン

このルーンを使用すると、リアクティブなデータが変更されるたびに実行される任意のコードを指定できます。このルーンが魔法を発揮するために、実行中に読み取られる反応データを追跡します。このリアクティブ データのインベントリは、インベントリ内の値が変更されるたびにエフェクトを再トリガーするために使用されます。

おそらく最も一般的なシナリオは、値の変更に基づいてデータ フェッチ操作を再トリガーすることです。

<script lang="ts">
    let clickCount = $state(0);

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document onclick={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

$derived 変数に約束を持たせたくない場合は、通常、非同期操作が内部エフェクトの標準となります。ただし、個人的には、Svelte で Promise を操作するのは非常に簡単なので、単純に $derived 値を使用します。次に示すバリアントは、データを約束を保持するリアクティブな計算値にします:

<script lang="ts">
    let clickCount = $state(0);
    let secondClickCount = clickCount;

    function countClicks() {
        ++clickCount;
    }
</script>

一般的に、$state と $effect を組み合わせて実行する場合は、$derived を使用した方がよいでしょう。ただし、この規則には例外もあるので、聖書の言葉ではなく、経験則として捉えてください。

データのフェッチが $effect の良い例ではないとしたら、それは何でしょうか?これを見てみましょう:

<script lang="ts">
    type Props = {
        data: number[];
        operation?: 'sum', 'avg';
    };

    let {
        data,
        operation = 'sum',
        ...restProps,
    }: Props = $props();

    function sum() {
        return data.reduce((p, c) => p + c);
    }

    function avg() {
        return sum() / data.length
    }
</script>

<span class="amount" {...restProps}>{operation === 'sum' ? sum() : avg()}</span>

<style>
    .amount {
        font-family: monospace;
    }
</style>

これは、ステータス プロパティを介して制御される単純なタイマー コンポーネントです。 $effect ルーンは、タイマーの操作を強制するためにここで使用されます。これを $derived にリファクタリングすることを想像できますか?ちなみに、elapsed は prop なので、$derived と prop を同時に行うことはできないため、試さないでください。

結論

Svelte v5 には、再レンダリングのパフォーマンス向上とコード リファクタリングの向上を目的とした、まったく新しいリアクティビティ エンジンが付属しています。新しい反応性システムの使用は、単純であると同時に複雑でもあります。一般的なシナリオがシステムの設計で十分にカバーされているため単純ですが、v4 と比較するとコードが少し複雑になっているため、少し難しくなります。

とにかく、新しいシステムは強力で、ほとんどのシナリオに適切かつ効果的に対応し、最初は少し奇妙ではあるものの、簡単に使用できるあらゆる可能性のルーンを提供します。

次は何だろう

この記事では、ルーンの入門部分と、ルーンを使用した個人的な経験についてのみ説明しました。読者の皆さんが、この Svelte の新しい表現をより迅速に始めるのに役立つトピックがさらにあります。

  • $effect の仕組みに関する深い知識

  • 高度なルーン ($state.raw、$derived.by、$effect.pre など)

  • ストアをリアクティブ状態に置き換えます

  • 常識外のシナリオ

ボーナス: パフォーマンス チャート

次のベンチマーク結果をご覧ください: Interactive Results (krausest.github.io)

フレームワークのリストは驚くべきものなので、次の JSON をコピーし、[貼り付け] ボタンを使用して Web ページに貼り付けます (スクリーンショットを参照)。

<script lang="ts">
    let clickCount = 0;

    function countClicks() {
        ++clickCount;
    }
</script>

<svelte:document on:click={() => countClicks()} />
<pre class="brush:php;toolbar:false">Clicks inside document: {clickCount}

Learning the new Svelte veactivity System

ちなみに、ウィンドウをフォーカスしてキーボードから貼り付けるだけでも機能すると思います。

これにより、フレームワークのリストがより人気のあるもの、または少なくとも私が人気があると考えるものに絞り込まれます。もしかしたら、あなたは私よりも詳しいかもしれません。

Svelte v4 がチャートで利用できなくなったのは残念ですが、ご覧のとおり、選択したフレームワークのうち、トップ 3 は議論の余地のないものです: Vanilla JS、Solid、Svelte。

その対極では、React v19 のパフォーマンスが非常に悪いのを見るのは本当に悲しいことです。コンパイラはそれをもっと良くするはずではなかったのでしょうか?結局無駄な努力だったようです。確かに、React v18 よりもパフォーマンスが優れているように見えますが、それだけです。 Meta がなぜ React に資金を投資し続けるのかは私にはわかりません。誰か考えてみませんか?

以上が新しい Svelte veactivity システムを学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。