l12a

白ウサギを追え

Svelte を試す

Svelte

React / Vue / Angular がメインストリームであるフロントエンド界に現れた新たなプロジェクトとして、ちょっと話題になったやつです。
「スヴェルト」と読むそうです。
リッチハリスさんという人が作りました。

Vue.js と nuxt.js のように、Svelte にも Sapper というフレームワークがあり、ルーティングやSSRなどを提供しています。
今日は Svelte を使ってアプリケーションの立ち上げからコンポーネントの作成、テンプレートロジックの実装までを試しました。

svelte.dev

Svelte を試す

クイックスタートを探す

Github リポジトリを見ても、正しい始め方が書いてなく、クイックスタートのブログポストを探すことになります。

The easiest way to get started with Svelte

とりあえず動かすまで

degit というスキャフォールディングツール(作者同じ)を使います。
簡単に言うと git リポジトリをコピーしてくるツールみたいです。

github.com

$ npm i degit

次に、 npx コマンドで degit を動かしてテンプレートを作ります。

$ npx degit sveltejs/template my-svelte-project

できたら、

$ cd my-svelte-project
$ npm install
$ npm run dev

とすると、 localhost:5000 で立ち上がります。

f:id:lnly:20200122140317p:plain

package.json を見る

{
  "name": "svelte-app",
  "version": "1.0.0",
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public"
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^11.0.0",
    "@rollup/plugin-node-resolve": "^6.0.0",
    "rollup": "^1.20.0",
    "rollup-plugin-livereload": "^1.0.0",
    "rollup-plugin-svelte": "^5.0.3",
    "rollup-plugin-terser": "^5.1.2",
    "svelte": "^3.0.0"
  },
  "dependencies": {
    "sirv-cli": "^0.4.4"
  }
}

Rollup (これの開発にもリッチ氏がコントリビュートしています) でモジュールバンドルしています。

github.com

Svelte の説明によれば、

Rollupに限定されないし、Webpackも使えるし、CLIを使うこともできるので、好きなものを使ってくれ

みたいなことが書いてあります。

テンプレートを編集しながら、使用感を探る

ソースコードの種類

コンポーネント

Vue.js の Single File Component によく似たファイルを使います。拡張子は .svelte
Intellij IDEA はプラグインがあり、コードハイライトなど IDE からのコーディング補助の恩恵を受けられます。

<script>
    export let name;
</script>

<main>
    <h1>Hello {name}!</h1>
    <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>

<style>
    main {
        text-align: center;
        padding: 1em;
        max-width: 240px;
        margin: 0 auto;
    }

    h1 {
        color: #ff3e00;
        text-transform: uppercase;
        font-size: 4em;
        font-weight: 100;
    }

    @media (min-width: 640px) {
        main {
            max-width: none;
        }
    }
</style>

具体的な違いは、

です。

エントリポイント

通常の Javascript です。

import App from './App.svelte';

const app = new App({
    target: document.body,
    props: {
        name: 'world'
    }
});

export default app;

コンポーネントの利用

コンポーネントは import するだけで使えます。

<!-- FooBar.svelte -->
<p>this is foo bar</p>
<!-- App.svelte -->
<script>
    import FooBar from './components/FooBar.svelte'
</script>

<main>
    <h1>Hello World!</h1>
    <FooBar />
</main>

HTML の展開

変数は通常、プレーンテキストとして展開されますが。 以下のようにHTML展開もできます。

<script>
    const hoge = 'this is <b>html</b>'
</script>

<p>{@html hoge}</p>

属性への埋め込み

HTMLコンテンツ同様、属性へも展開できます。

<script>
    const src = 'image.gif';
</script>

<img src={src}>

イベントとデータバインディング

DOM上のイベントと、ハンドラおよびデータとのバインディングは以下。

<script>
    let count = 0;

    function handleClick() {
        count += 1;
    }
</script>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

リアクティブデータ

以下の方法では、 doubled はリアクティブになりません。

<script>
    let count = 0;
    let doubled = count * 2;

    function handleClick() {
        count += 1;
    }
</script>

<p>{count} doubled is {doubled}</p>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

珍しいシンタックスを使って、以下のように書きます。

<script>
    let count = 0;
    $: doubled = count * 2;

    function handleClick() {
        count += 1;
    }
</script>

<p>{count} doubled is {doubled}</p>

<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

「独自シンタックス」は好まれないことが多いですがこの点については

これが異質に見えても、心配しないでください。型にはまらなければこれは Valid な Javascript です

とのことです。確かに、 console でこのように書いてもエラーにはなりませんでした。

値以外のリアクティビティ

$:シンタックスで表現されるリアクティブデータは、プリミティブな値に限らず以下のようなこともできます。

$: console.log(`the count is ${count}`);

Propsの受け渡し

コンポーネントからは子コンポーネントにデータを渡せます。

<!-- App.svelte -->
<script>
    import FooBar from './FooBar.svelte';
</script>

<FooBar baz={'hogehoge'}/>

コンポーネントでは同名の変数を export することで親コンポーネントからデータを受け取れます。

<!-- FooBar.svelte -->
<script>
    export let baz;
</script>

<p>{baz}</p>

テンプレートロジック

HTMLテンプレート内でのロジックは以下のように実装します。

if ブロック

{#if hoge === 123}
    <p>yes!</p>
{/if}

each ブロック

<ul>
    {#each items as item}
        <li>{item.label}</li>
    {/each}
</ul>

おわり

今日勉強できたのはここまでです。