🧊

Svelte 3.46.0で追加された新機能

https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md#3460

機能追加と言えるものは割と久しぶりな気がしたので、こうして筆を執ってみた次第。

この2つの機能が追加された。

  • `@const`タグ
  • `style`ディレクティブ

Constants in markup

rfcs/0007-markup-constants.md at master · sveltejs/rfcs · GitHub

Svelteには、テンプレの部分で使えるタグっていう特別な構文がある。

既存のタグとして、`{@html}`と`{@debug}`があって(詳細はDocsをみてね)、そこに今回`{@const}`というのが追加された。

コレは何かというと、主に`{#each}`みたいなループのコンテキスト直下でのみ使える、ローカルな変数入れ。

ほかにも`{:then}`や`Component`や`svelte:fragment`の直下でも使えるらしいけど、基本的なモチベーションとしてはやはり`{#each}`ブロックかと。

RFCからコードを持ってくると、こんな時に嬉しい。

{#each boxes as box}
  <div
    class="box"
    class:large={box.width * box.height >= 10000}
    style="width: {box.width}px; height: {box.height}px"
  >
    {box.width} * {box.height} = {box.width * box.height}
  </div>
{/each}

このように、ループ内で同じ計算を何度もしたくないな〜みたいな時。

もちろん関数にすればええやんって話でもあるけど、ループごとに1度で済ませたいはず。構造化されたアプリならStore側で整形しろって話もあるかもしれんけど、ちょっとしたものを書きたいケースもあるはず。

そこで、`{@const}`が使える!

{#each boxes as box}
  {@const area = box.width * box.height}

  <div
    class="box"
    class:large={area >= 10000}
    style="width: {box.width}px; height: {box.height}px"
  >
    {box.width} * {box.height} = {area}
  </div>
{/each}

というように書けて便利ねっていう。

SvelteのテンプレはJSXではないので、こういうところが不便って感じる人もいてたかもしれない。

個人的には、テンプレ部にロジックを書かせるスキを与えてる感じがしてそこまでポジティブに見てなかった。JSXの`map`のループ内でずらずら変数定義されてるのすごいよく見るし。

けどまぁSvelteらしい機能といえばそうなので、別にいいかって感じ。あったら使うと思うし。

Style Directive

rfcs/0008-style-directives.md at master · sveltejs/rfcs · GitHub

ディレクティブは、要素につけて使う属性のこと。`class:`とか、`use:`とかコロンをつけるアレ。

今回追加されたのは、インラインでスタイルを定義するときに便利なディレクティブ。

またもRFCからコードを持ってくると。

<div
  style="
    position: {position};
    {position === 'absolute' ? 'top: 20px;' : ''}
    {pointerEvents === false ? 'pointer-events:none;' : ''}
  "
/>

みたく、インラインでスタイルを書いてたとする。(SvelteはJSXじゃないので、雑に文字列を展開できるんよね)

まぁわからんでもないけど、それなりに冗長だった。それがこう書けるようになる。

<div
  style:position
  style:top={position === "absolute" ? "20px" : null}
  style:pointer-events={pointerEvents ? null : "none"}
/>

`class:`ディレクティブで`boolean`を渡してた代わりに、`style:`ディレクティブには`string`を渡すみたいな。

ちなみに、`style=`属性と衝突する場合は、ディレクティブが優先される。

もうひとつちなむと、`style:marginLeft`みたいにキャメルケースでも書ける。

さらにもうひとつちなむと、Reactの`style`オブジェクトのアレとは違って、勝手に`px`を付けてくれたりはしない。

まとめ

Svelteらしい機能追加で良かったです👏