43
35

More than 1 year has passed since last update.

すぐに使えるSvelteハンドブック

Last updated at Posted at 2022-03-21

はじめに

今後爆発的人気になるかもしれないし、ならないかもしれないSvelteの基本的な記述のやり方についてまとめてみました。
フロント開発に関わっている方や、フロント開発を勉強中の方に読んでいただけたらと思います。
ReactVueでの開発経験があったほうが、よりわかりやすい内容となっています。

注意
この記事はSvelteの基本的な記述のみをまとめているため、Svelteの概要や、アニメーションの使い方などは割愛していますのでご了承ください。

「やりたいことから」リンク集

■基本情報
プロジェクト作成
HTML、CSS、JSの記述

■データ
reactive値を扱う
再計算、再実行させる

■コンポーネント
コンポーネントの作成、呼び出し(通常、slot)
propsを扱う

■イベント
DOMイベントと関数を紐づける
コンポーネントからイベントを発信させる

■ロジック
表示を分岐させる(表示、非表示)
表示を繰り返す
非同期処理を扱う

■バインド
テキスト入力欄を使う
チェックボックスを使う
ラジオボタンを使う
セレクトボックスを使う
子コンポーネントの入力要素にバインドさせる

■ライフサイクル
mount時に処理を実行させる
unMount時に処理を実行させる
DOM更新前に処理を実行させる
DOM更新後に処理を実行させる

■Store
Storeを作成する
Storeの値を取得する
Storeの値を変更する

プロジェクト作成

test-projectの部分を任意のプロジェクト名に変えてください。
※この記事はTypeScriptでの記述を前提に書いています。

$ npx degit sveltejs/template test-project
$ cd test-project
$ node scripts/setupTypeScript.js
$ npm install
$ npm run dev

VSCodeのパッケージを追加

VSCodeを使用する方は、任意で入れてみてください。

HTML、CSS、JSの記述

svelteは、JSHTMLCSSの順番で記述する。

  • script内に宣言した変数は{}を使用することでHTML内に反映できる。
  • CSSscopedとなっている。
App.svelte
<script lang="ts">
  let text = 'Hello World'
</script>

<h1 class="title">{text}</h1>

<style>
  .title {
    color: red;
  }
</style>

属性名変数名が同じ場合は省略できる。

App.svelte
<script lang="ts">
  let src = './○○.png'
</script>

<img src={src} alt="image"> <!-- ↓と同じ -->
<img {src} alt="image">

reactive値を扱う

svelteではletで変数宣言をすると、自動的にreactiveな値となる。

App.svelte
<script lang="ts">
  let count = 0
</script>

<h1>{count}</h1>

注意
svelteのリアクティビティは代入によってトリガーされるため、pushspliceのような配列メソッドを使っても自動的には更新されない。

再計算、再実行させる

変数や関数の前に$:を記述することで、再計算、再実行の対象となる。

<script lang="ts">
  let count = 0

  // countの値が変更されると再計算される
  $: doubled = count * 2

  // 任意の処理を再実行することもできる
  $: console.log('the count is ' + count);

  // if文の前にも記述できる
  $: if (count >= 10) {
    alert('count is dangerously high!');
    count = 9;
  }
</script>

コンポーネントの作成、呼び出し(通常、slot)

通常のコンポーネントの場合

App.svelte
<script lang="ts">
  import Nested from './Nested.svelte';
</script>

<Nested />
Nested.svelte
<script lang="ts">
  let text = 'Hello World'
</script>

<p>{text}</p>

slotの場合

App.svelte
<script lang="ts">
  import Box from './Box.svelte';
</script>

<Box>
  <p>Hello World</p>
</Box>
Box.svelte
<div>
  <slot></slot>
</div>

propsを扱う

propsとして渡される値の前にexportを記述することで、「propsとして渡される値」と認識される。

App.svelte
<script lang="ts">
  import Nested from './Nested.svelte';
</script>

<Nested answer={42} />
Nested.svelte
<script lang="ts">
  export let answer;
  export let name = 'Taro'; // あらかじめ値を代入しておくとデフォルト値にできる
</script>

<p>name: {name}</p>
<p>The answer is {answer}</p>

プロパティを持っているオブジェクトを渡す場合、...で簡略化できる

App.svelte
<script lang="ts">
  import Nested from './Nested.svelte';

  const user = {
    name: 'Taro',
    age: 20
  }
</script>

<Nested {...user} />
Nested.svelte
<script lang="ts">
  export let name;
  export let age;
</script>

<p>名前: {name}</p>
<p>年齢: {age}</p>

DOMイベントと関数を紐づける

イベント名の前にon:を記述する。

App.svelte
<script lang="ts">
  let count = 0;

  const countUp = () => {
	count += 1
  }
</script>

<h2>{count}</h2>
<button on:click={countUp}>
 Count Up
</button>

子、孫コンポーネントのDOMイベントの場合イベントフォワーディングをする。

App.svelte
<script lang="ts">
  import CustomButton from './CustomButton.svelte';

  const handleClick = () => {
	alert('Button Clicked');
  }
</script>

<CustomButton on:click={handleClick}/>
CustomButton.svelte
<button on:click>
  Click me
</button>

|を使用し、イベントに修飾子をつけられる。複数指定可能。

<script lang="ts">
  const handleClick = () => {
	alert('alerts')
  }
</script>

<!-- once の場合、1回のみ実行される -->
<button on:click|once={handleClick}>
  Click me
</button>

<!-- 複数指定する場合 -->
<button on:click|once|self={handleClick}>
  Click me
</button>

イベント修飾子の一覧

  • preventDefault・・・実行する前にevent.preventDefault()を呼び出す。
  • stopPropagation・・・event.stopPropagation()を呼び出す。
  • passive・・・タッチ/ホイールイベントでスクロールのパフォーマンスを向上させる。(svelteが自動的に追加)
  • nonpassive・・・passive: falseを明示的に設定。
  • capture・・・バブリングフェーズではなく、キャプチャフェーズ中に実行する。
  • once・・・ハンドラを最初に実行した後に削除する。
  • self・・・設定した要素がevent.targetの場合にのみ、実行する。
  • trusted・・・event.isTrustedtrueの場合にのみ、実行する。

コンポーネントからイベントを発信させる

Vueのemitみたいなもの。
createEventDispatcherを使用し、イベントを発信。

App.svelte
<script lang="ts">
  import Child from './Child.svelte';

  const handleMessage = (event) => {
	alert(event.detail.text);
  }
</script>

<Child on:message={handleMessage}/>
Child.svelte
<script lang="ts">
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  const sayHello = () => {
	dispatch('message', {
	  text: 'Hello!'
	});
  }
</script>

<button on:click={sayHello}>
	Click to say hello
</button>

中間コンポーネントがある場合はフォワーディングする必要がある。

App.svelte
<script lang="ts">
  import Child from './Child.svelte';

  const handleMessage = (event) => {
	alert(event.detail.text);
  }
</script>

<Child on:message={handleMessage}/>
Child.svelte
<script lang="ts">
  import Grandchild from './Grandchild.svelte';
</script>

<Grandchild on:message/>
Grandchild.svelte
<script lang="ts">
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  const sayHello = () => {
	dispatch('message', {
	  text: 'Hello!'
	});
  }
</script>

<button on:click={sayHello}>
  Click to say hello
</button>

表示を分岐させる(表示、非表示)

# の文字は常にブロックの開始タグを示す。
: の文字は {:else} のようにブロックの継続タグを示す。
/ の文字は常にブロックの終了タグを示す。

App.svelte
<script lang="ts">
  let isLogin = false;

  const toggle = () => {
	isLogin = !isLogin;
  }
</script>

{#if isLogin}
  <button on:click={toggle}>
    Log out
  </button>
{:else}
  <button on:click={toggle}>
	Log in
  </button>
{/if}

else ifももちろん使える

App.svelte
<script lang="ts">
  let x = 7;
</script>

{#if x > 10}
  <p>{x} is greater than 10</p>
{:else if 5 > x}
  <p>{x} is less than 5</p>
{:else}
  <p>{x} is between 5 and 10</p>
{/if}

表示を繰り返す

svelteで表示の繰り返しをする場合はeachを使用する。

App.svelte
<script lang="ts">
  let users = [
	{ name: 'Taro', age: 20 },
	{ name: 'Hana', age: 23 },
	{ name: 'Ken', age: 25 }
  ];
</script>

<ul>
  {#each users as user}
	<li>
      <p>name: {user.name}</p>
      <p>age: {user.age}</p>
    </li>
  {/each}
</ul>

第2引数でindexを指定できる。

{#each users as user, i}
  <li>
    <p>number: {i}</p>
    <p>name: {user.name}</p>
    <p>age: {user.age}</p>
  </li>
{/each}

一意な識別子(key)

{#each users as user (user.name)}
{/each}

非同期処理を扱う

App.svelte
<script lang="ts">
  const fetchData = async() => {
	const res = await fetch('https://○○○');

	if (res) {
	  return 'OK';
	} else {
	  throw new Error('Error');
	}
  }
	
  let result = fetchData();
  
  const promiseTest = () => {
    result = fetchData();
  }
</script>

<button on:click={promiseTest}>
  fetch
</button>

{#await result}
  <!-- 処理中の表示を記述 -->
{:then string}
  <!-- resolved時の表示を記述 -->
{:catch error}
  <!-- rejected時の表示を記述 -->
{/await}

rejectできないことがわかっている場合は、catch ブロックを省略することが可能。

{#await result}
  <p>...waiting</p>
{:then string}
  <p>The text is {string}</p>
{/await}

resolveするまで何も表示したくない場合は、最初のブロックを省略することもできる。

{#await result then value}
  <p>The text is {value}</p>
{/await}

テキスト入力欄を使う

bind:valueを使用することで、reactive値input値を紐づけることができる。
vueのv-modelと同じ。
※bindの名前と変数名が一致する場合は省略できる。
textareaタグでも同様。

App.svelte
<script lang="ts">
  let value = '世界';
</script>

<input bind:value={value}> <!-- ↓と同じ -->
<input bind:value>

<h1>Hello {value}!</h1>

type="number"type="range"を使用したときは、数値として解釈してくれる。

App.svelte
<script lang="ts">
  let num = 2;
</script>

<input type=number bind:value={num}>

<h1>{num * num}</h1>

チェックボックスを使う

チェックボックスの場合は、bind:checkedを使用する。

App.svelte
<script lang="ts">
 let isChecked = false;
</script>

<label>
  <input type=checkbox bind:checked={isChecked}>
  check box
</label>

<button disabled={!isChecked}>
  Subscribe
</button>

ラジオボタンを使う

ラジオボタンのようにグループ化が必要な場合は、bind:groupを使用する。

App.svelte
<script lang="ts">
  let radioValue = 1
</script>

<label>
  <input type=radio bind:group={radioValue} name="radioValue" value={1}>
  No.1
</label>

<label>
  <input type=radio bind:group={radioValue} name="radioValue" value={2}>
  No.2
</label>

<label>
  <input type=radio bind:group={radioValue} name="radioValue" value={3}>
  No.3
</label>

セレクトボックスを使う

セレクトボックスもbind:valueを使用する。

App.svelte
<script lang="ts">
  let questions = [
	{ id: 1, text: 'question1' },
	{ id: 2, text: 'question2' },
	{ id: 3, text: 'question3' }
  ];

  let selected;
</script>

<select bind:value={selected}>
  {#each questions as question}
	<option value={question}>
	  {question.text}
	</option>
  {/each}
</select>

子コンポーネントの入力要素にバインドさせる

DOM要素のプロパティにバインドできるのと同様に、コンポーネントのpropsにもバインドできる。

App.svelte
<script lang="ts">
  import Child from "./Child.svelte";

  let name = '';
</script>

<Child bind:name={name} />
<h1>{name}</h1>
Child.svelte
<script lang="ts">
  export let name: string
</script>

<input type="text" bind:value={name}>

mount時に処理を実行させる

onMountを使用することで、コンポーネントが最初にDOMにレンダリングされた後に処理を実行できる。

App.svelte
<script lang="ts">
  import { onMount } from 'svelte';

  onMount(() => {
	console.log('Mounted!!')
  });
</script>

<h1>onMount</h1>

unMount時に処理を実行させる

onDestroyを使用することで、コンポーネントが破棄されるときに処理を実行できる。

App.svelte
<script lang="ts">
  import { onDestroy } from 'svelte';

  onDestroy(() => {
	console.log('Destroy!!')
  });
</script>

<h1>onDestroy</h1>

DOM更新前に処理を実行させる

beforeUpdateを使用することで、コンポーネントが最初にマウントされる前と、DOMが更新される直前に処理を実行できる。

App.svelte
<script lang="ts">
  import { beforeUpdate } from 'svelte';

  beforeUpdate(() => {
	console.log('Update!!')
  });
</script>

<h1>beforeUpdate</h1>

DOM更新後に処理を実行させる

afterUpdateを使用することで、DOMがデータと同期した後に処理を実行できる。

App.svelte
<script lang="ts">
  import { afterUpdate } from 'svelte';

  afterUpdate(() => {
	console.log('Updated!!')
  });
</script>

<h1>afterUpdate</h1>

Storeを作成する

外部から読み書き可能なwritableと、外部からは読み取り専用のreadableがある。
readableは第1引数に初期値、第2引数に値を変更するための関数が必要。

stores.ts
import { writable, readable } from 'svelte/store';

export const count = writable(0); // 読み書き可能

// 読み取り専用
export const time = readable(new Date(), function start(set) {
  const interval = setInterval(() => {
	set(new Date());
  }, 1000);

  return function stop() {
	clearInterval(interval);
  };
});

Storeの値を取得する

Storeから読み取る値には$の記述が必要。

App.svelte
<script lang="ts">
  import { count } from './stores';
</script>

<h1>The count is {$count}</h1>
stores.ts
import { writable } from 'svelte/store';

export const count = writable(0);

Storeの値を変更する

現在のStoreの値を利用するupdateと、直接変更するsetがある。

App.svelte
<script lang="ts">
  import { count } from './stores';
  import Counter from './Counter.svelte'
</script>

<h1>The count is {$count}</h1>
<Counter />
Counter.svelte
<script lang="ts">
  import { count } from './stores';

  const increment = () => {
	count.update(n => n + 1); // storeの現在の値を引数に取り更新
  }

  const reset = () => {
	count.set(0); // storeの値を直接変更
  }
</script>

<button on:click={increment}>increment</button>
<button on:click={reset}>reset</button>
stores.ts
import { writable } from 'svelte/store';

export const count = writable(0);

おわりに

ここまでお読みいただきありがとうございました。
以上がSvelteの基本的な記述のやり方です。
Svelteはとてもシンプルで使いやすいので、これからどんどん需要が高まっていくことを期待しています!
記述ミスやわかりにくい点などございましたら、お手数ですが、コメントや編集リクエストなどで送っていただけたら幸いです。
これからも少しずつSvelteをキャッチアップしていきたいと思います。

43
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
35