はじめに
Svelte は、ReduxやVueのようなJavascriptフレームワークですが、以下のような点が異なるようです。いろいろ記事はあるようなので簡単に。
- コードの記述量が減る: その分バグが少なくなる
- 仮想DOMを使わない、ただのJavascriptとして動作する: ファイルサイズが小さくなり、構文解析や実行が速い
- フレームワーク自体にストアのサポートがある
Svelteのチュートリアル
公式のチュートリアル にいろいろと書いてあります。
今回は、チュートリアル内<svelte:self>
でのフォルダ階層表示をTypeScriptにしてみようと思います。
ただ、そもそもTypescriptに慣れていないためいろいろおかしいところがあると思います。
環境
Node.js: v14.15.3 (nvmで入れています)
プロジェクトの準備
公式のブログ Svelte <3 TypeScript に従って準備します。
npx
は(恒久的な)インストールをせずに実行するnpm
みたいなものっぽいです。npm v5.2.0以降で増えたらしい。そうなのか。
新しめのNode.jsをインストール済みであれば、ターミナルでプロジェクトを作成したいディレクトリを表示して、以下を実行すればSvelte+TypeScriptのプロジェクト作成は完了です。
npx degit sveltejs/template svelte-typescript-app
cd svelte-typescript-app
node scripts/setupTypeScript.js
npm install時になんか表示されるのが嫌なので、package.json
を編集しておきます。
{
"name": "svelte-app",
"version": "1.0.0",
"private": true, // <- この行を追加
(略)
}
次に、もろもろインストールします。ここまでで、Svelte+TypeScriptが動作できるようになっています。
npm install
せっかくなので、Sassの準備もしておきます。今回の範囲では最終的に不要でしたが...
(こちらを参考にさせていただきました: Svelteとは?Reactの比較 / TypeScriptと Sassの導入方法 )
ちなみに、TypeScriptの準備でいいかんじになっているようなので、rollup.config.js
の編集は不要でした。
npm install -D sass
最後に、VSCodeであればこちらの拡張機能を入れておくと便利そうです: Svelte for VS Code
ここまででもろもろインストールされたバージョン
- Svelte: 3.31.1
- Typescript: 3.9.7
- Sass: 1.32.0
コーディング
これ以降、パスはsvelte-typescript-app/src
をルート扱いとします。
公式のチュートリアル のIntroductionのセクション(現時点で7ページ分)と、LogicのセクションのうちIf blocks
、Else blocks
、Each blocks
、を先に読んでおくとよいと思います。
1. ファイルのデータ型を準備
ただの型定義TypeScriptファイルです。
// ファイル種類の列挙型
export enum FileType {
// フォルダ
folder,
// ファイル
file,
};
// ファイル情報1件
export type FileItem = {
// ファイル名
name: string;
// ファイル種類
type: FileType;
// 子階層(フォルダの時用)
children?: Array<FileItem>;
};
2. ファイルアイコン(コンポーネント)の準備
SVGファイルは、マテリアルデザインのアイコンをいただきました。
folder, folder_open, text_snippetです。
<script lang="ts">
export let size: number = 24; // アイコンのサイズ
</script>
<style lang="scss">
svg {
vertical-align: text-bottom;
}
</style>
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="{size}" viewBox="0 0 24 24" width="{size}">
<g>
<rect fill="none" height="24" width="24"/>
<path d="M20.41,8.41l-4.83-4.83C15.21,3.21,14.7,3,14.17,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9.83 C21,9.3,20.79,8.79,20.41,8.41z M7,7h7v2H7V7z M17,17H7v-2h10V17z M17,13H7v-2h10V13z"/>
</g>
</svg>
<script lang="ts">
export let size: number = 24; // アイコンのサイズ
export let open: boolean = false; // 開閉(true=開いている)
</script>
<style lang="scss">
svg {
vertical-align: text-bottom;
}
</style>
<svg xmlns="http://www.w3.org/2000/svg" height={size} viewBox="0 0 24 24" width={size}>
<path d="M0 0h24v24H0z" fill="none"/>
{#if open}
<path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/>
{:else}
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
{/if}
</svg>
3. ファイル表示のコンポーネント作成
<script lang="ts">
import FileIcon from '../icons/FileIcon.svelte';
export let name: string; // ファイル名
</script>
<FileIcon size={18}/>
<span>{name}</span>
4. フォルダ表示のコンポーネント作成
開閉の処理があるのでやや長いです。
<script lang="ts">
import FolderIcon from "../icons/FolderIcon.svelte";
import File from "./File.svelte";
import type { FileItem } from "../../repositories/Files";
import { FileType } from "../../repositories/Files";
import { slide } from 'svelte/transition';
export let folder: FileItem; // ファイル情報
export let expanded: boolean = false; // 開閉の状態(true=開いている)
// 開閉の切り替え
function toggle() {
expanded = !expanded;
}
</script>
<style lang="scss">
div {
cursor: pointer;
display: inline-block;
}
ul {
list-style-type: none;
padding-inline-start: 20px;
}
li {
margin-block-start: 5px;
}
li:first-child {
margin-block-start: 0;
}
p {
margin-block: 0;
padding-inline-start: 20px;
}
</style>
<div on:click={toggle}>
<FolderIcon size={18} open={expanded} />
<span class:expanded>{folder.name}</span>
</div>
{#if expanded}
{#if folder.children && folder.children.length > 0}
<ul transition:slide>
{#each folder.children as item}
<li>
{#if item.type == FileType.folder}
<svelte:self folder={item} />
{:else}
<File {...item}/>
{/if}
</li>
{/each}
</ul>
{:else}
<p transition:slide>空っぽだよ</p>
{/if}
{/if}
5. App.svelte作成
<script lang="ts">
import Folder from './files/Folder.svelte';
import type { FileItem } from '../repositories/Files';
import { FileType } from '../repositories/Files';
// 表示するフォルダ階層の内容
const root: FileItem = {
name: 'root',
type: FileType.folder,
children: [
{
name: 'file1',
type: FileType.file,
},
{
name: 'folder2',
type: FileType.folder,
},
{
name: 'folder3',
type: FileType.folder,
children: [
{
name: 'file2',
type: FileType.file,
},
{
name: 'folder4',
type: FileType.folder,
children: [],
},
],
},
],
};
</script>
<h1>Explorer</h1>
<Folder folder={root} />
6. main.ts修正
//import App from './App.svelte'; // 元のApp.svelte
import App from './components/App.svelte'; // 新しいApp.svelte
const app = new App({
target: document.body,
props: {
name: 'world'
}
});
export default app;
最終的なみため
ターミナルから起動します。Svelte+TypeScript+SassがJavascriptとCSSにコンパイルされ、ブラウザで表示可能となります。
cd svelte-typescript-app
npm run dev
localhostの5000番ポートで起動するので、ブラウザで http://localhost:5000 にアクセスすると、以下のように表示されます(フォルダを開閉できます)。
また、ソースコードの変更を保存すると自動で表示が変わります。変わらなくなった場合はブラウザでリロードしてみてください。
npm run build
すれば、難読化されたJSファイルが出力されます。
出力先は public/build です。npm run dev
と同じ場所なので、互いに上書きされます。(rollup.config.jsを編集すればbuldとdevで出力先を変えられるとは思います)
はまったところ
TypeScript 3.8以降で、import type
が増えたみたいです。
ただのimport
だとコンパイルのエラーになりました。
今回は以上です。続くかも。
参考まとめ
Svelte: Svelte公式トップ
Svelte <3 TypeScript: Svelte公式のTypescript導入方法
Svelteとは?Reactの比較 / TypeScriptと Sassの導入方法: Sass導入参考
Svelte for VS Code: VSCodeのSvelte拡張機能
Icons - Material Design: マテリアルデザインのアイコン一覧