動機
現在、日本語学習者向けの漢字学習アプリを開発しています。
バックエンドに Bun & Elysia を採用することは決定しているので、
フロントエンドはできれば React, Vue, Svelte あたりから選びたいと考えています。
本記事は Svelte を触って何かやってみるための準備の記録です。
教材
Svelte チュートリアル
本記事は Part 1: Basic Svelte の前半部分 (Introduction, Reactivity, Props) のノートです。
Svelteを使うと何がうれしいのか
- コンポーネントベースでWebアプリが構築できる
- JavaScriptにコンパイルされるため、オーバーヘッドがない
- 1つのコンポーネント (
.svelte
ファイル) にHTML, CSS, JavaScript を記述でき、自己完結させやすい
基本的な書き方
{} を使ってJSコードを埋め込む
JSX/TSXと同様です。
<script>
let src = '/image.gif';
let name = 'Rick Astley';
</script>
<img src={src} alt="{name} dances." />
<!-- <img src="/image.gif" alt="Rick Astley dances." /> -->
なお、src={src}
のように属性名と変数名が同じ場合、シンプルに {src}
とも書けます。
import
文でコンポーネントを読み込む
<script>
import AnotherComponent from './AnotherComponent.svelte';
</script>
{@html ...}
で文字列をHTMLとしてレンダリングする
<script>
let string = `this string contains some <strong>HTML!!!</strong>`;
</script>
<p>{@html string}</p>
this string contains some HTML!!!
Reactivity システム (DOM を更新する)
イベントハンドラを定義する
<script>
let count = 0;
function increment() {
count++;
}
</script>
<button on:click={increment}>
Clicked {count}
{count === 1 ? 'time' : 'times'}
</button>
on:click={...}
部分がイベントハンドラにあたります。
このケースでは、ボタンをクリックしたときに increment()
が発火し、count が更新され再レンダリングされます。
$:
を使って、関連するDOMを更新させる (Reactive declaration: リアクティブ宣言)
<script>
let count = 0;
$: doubled = count * 2;
function increment() {
count += 1;
}
</script>
<button on:click={increment}>
Clicked {count}
{count === 1 ? 'time' : 'times'}
</button>
<p>{count} doubled is {doubled}.</p>
$: doubled = count * 2;
という宣言をすることで、count
が更新された時に、 doubled
の再代入と再レンダリングが行われます。
「 doubled
の再代入」と書きましたが、より正しくは、 $:
以降の処理の再実行 (re-action) が行われるので、値の設定だけでなく、関数の実行もできます。制御構文も書けます。
$: console.log(`Someone seems to have pressed the button.`);
$: if (age === 18) {
alert('Congratulations! You are now ready to vote.');
}
リアクティビティは代入によってトリガーされますが、push()
, pop()
などのいわゆる破壊的メソッドによってはトリガーされません。
愚直ですが、自身を再代入することでトリガーできます。
function addNumber() {
numbers.push(numbers.length + 1);
numbers = numbers; // この文がないと、push だけでは numbers に対するリアクションが発火しない
}
export
で子コンポーネントへのデータ受け渡しを行う (いわゆる props)
<script>
export let place;
</script>
<p>Stairway to {place}</p>
<script>
import Nested from './Nested.svelte';
let place = "Heaven"
</script>
<Nested place={place} />
結果
Stairway to Heaven
props のデフォルト値を設定する
値を代入しておけばよいです。
<script>
export let place = 'Paradise';
</script>
<p>Stairway to {place}</p>
<script>
import Nested from './Nested.svelte';
let place = "Heaven"
</script>
<Nested />
結果
Stairway to Paradise
props の受け渡しにはスプレッド構文が使える
さもありなん、というところです。
<script>
export let title;
export let author;
export let nationality;
export let publishedYear;
</script>
<p>
"{title}" is a novel by {nationality} author {author}, first published in {publishedYear}.
</p>
<script>
import NovelInfo from './NovelInfo.svelte';
const book = {
title: 'Confessions of a Mask',
author: 'Yukio Mishima',
nationality: 'Japanese',
publishedYear: '1949'
};
</script>
<PackageInfo {...book} />
結果
"Confessions of a Mask" is a novel by Japanese author Yukio Mishima, first published in 1949.
雑感
useState
や useEffect
などの hooks ががっちりと用意されている React.js と比べて、学習・実装コストとも低いと感じました。
この調子でチュートリアルを進めていきたいです。