Open12

Deep Dive into Svelte

山とコード山とコード

Svelte の特徴

  1. Write less code ... 少ないコード量で記述できる
  2. No Virtual DOM ... 仮想DOMを使用しない
  3. Truly reactive ... 真にリアクティブ
山とコード山とコード

Getting started

npm create svelte@latest myapp-svelte
cd myapp
npm install
npm run dev

4つほど質問があった。
ここでTypeScript で実装するかも聞かれる。

Need to install the following packages:
  create-svelte@5.0.2
Ok to proceed? (y) y

create-svelte version 5.0.2

┌  Welcome to SvelteKit!
│
◇  Which Svelte app template?
│  SvelteKit demo app
│
◇  Add type checking with TypeScript?
│  Yes, using TypeScript syntax
│
◇  Select additional options (use arrow keys/space bar)
│  Add ESLint for code linting, Add Prettier for code formatting, Add Playwright for browser testing, Add Vitest for unit testing
│
└  Your project is ready!

✔ Typescript
  Inside Svelte components, use <script lang="ts">

✔ ESLint
  https://github.com/sveltejs/eslint-plugin-svelte

✔ Prettier
  https://prettier.io/docs/en/options.html
  https://github.com/sveltejs/prettier-plugin-svelte#options

✔ Playwright
  https://playwright.dev

✔ Vitest
  https://vitest.dev

Install community-maintained integrations:
  https://github.com/svelte-add/svelte-add

Next steps:
  1: cd myapp-svelte
  2: npm install (or pnpm install, etc)
  3: git init && git add -A && git commit -m "Initial commit" (optional)
  4: npm run dev -- --open
山とコード山とコード

Shorthand attributes

<img src={src} alt="A man dances.">

↓ 属性の名前と値の変数が一致している場合、省略できる

<img {src} alt="A man dances.">
山とコード山とコード

コンポーネント名は大文字始まり

コンポーネント名 Nested が大文字で始まっていることにも注目してください。
この命名規則は、ユーザーが定義したコンポーネントと、通常のHTMLタグを区別するために採用されました。

UpperCamelCase なのかな?

山とコード山とコード

アロー関数ではないのが、慣習なのかな?

<script>
	let count = 0;

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

<button on:click={incrementCount}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
山とコード山とコード

リアクティブ宣言 - reactive declarations

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

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

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

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

リアクティブな値は、複数回参照する必要がある場合や、他の リアクティブな値に依存する値がある場合に特に価値があります。

こういう場合かな

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

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

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

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

$: ではないから、意図した動作にならないコード

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

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

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

<p>{count} doubled is {doubled}</p>
<p>{count} doubled is {doubled}</p>
<p>{count} doubled is {doubled}</p>
<p>{count} doubled is {doubled}</p>
山とコード山とコード

これなら意図した動きにはなる。

$: の方が良い理由があるのかな?
たしかに冗長な書き方には見える。

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

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

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

<p>{count} doubled is {doubled}</p>
<p>{count} doubled is {doubled}</p>
<p>{count} doubled is {doubled}</p>
<p>{count} doubled is {doubled}</p>
山とコード山とコード

ロジックを表現する記法

  • {} を使う
  • 開始は #
  • 中間は :
  • 終了は /
<script>
	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}
山とコード山とコード

非同期処理

ロジックの記法と同じ

  • {} を使う
  • 開始は #
  • 中間は :
  • 終了は /
<script>
	async function getRandomNumber() {
		const res = await fetch(`/tutorial/random-number`);
		const text = await res.text();

		if (res.ok) {
			return text;
		} else {
			throw new Error(text);
		}
	}

	let promise = getRandomNumber();

	function handleClick() {
		promise = getRandomNumber();
	}
</script>

<button on:click={handleClick}>
	generate random number
</button>

{#await promise}
	<p>...waiting</p>
{:then number}
	<p>The number is {number}</p>
{:catch error}
	<p style="color: red">{error.message}</p>
{/await}
山とコード山とコード

イベント修飾子

once で 1 回のみ実行

<script>
	function handleClick() {
		alert('clicked')
	}
</script>

<button on:click|once={handleClick}>
	Click me
</button>

イベント修飾子一覧

preventDefault

ハンドラを実行する前に event.preventDefault() を呼び出します。
たとえば、クライアントのフォーム処理に役立ちます。

stopPropagation

次の要素にイベントが伝播しないように event.stopPropagation() を呼び出します。

passive

タッチ/ホイールイベントでスクロールのパフォーマンスを向上させます。
(Svelte が安全な場所に自動的に追加します)

nonpassive

passive: false を明示的に設定します。

capture

バブリング フェーズではなく、キャプチャ フェーズ中にハンドラを起動します。
MDN docs

once

ハンドラを最初に実行した後に削除します。

self

設定した要素が event.target の場合にのみ、ハンドラをトリガします。

trusted

event.isTrustedtrue の場合にのみハンドラをトリガします。
つまり、ユーザーのアクションによってイベントがトリガされた場合です。