morishitaです。
今回は JavaScript の UI フレームワーク Svelte を紹介します。
アクトインディでは今の所、フロントエンドフレームワークとしては Vue.js を使っていますが、これもなかなか良さそうだと思ったのでちょっと触ってみました。
プロジェクトの初期化から、次を使えるようにするまでを書きたいと思います。
- typescript
- pug
- sass
- jest
- eslint
Svelte とは
Web フロントエンド開発のための UI フレームワークです。
2019 年にリリースされた Svelte3 で大幅にアップデートされたようです。Vue の SFC によく似たマークアップ、スクリプト、スタイルを1ファイルに記述する Svelte コンポーネントとしてアプリケーションを実装します。
ライブラリではなくコンパイラとして働き、ビルドするとブラウザで実行できる JavaScirpt と CSS を出力します。
なので、ランタイムライブラリを明示的に組み込む必要はありません。コードの中で import
しなくてもビルド時に必要なコードが組み込まれます。
コンパイラとしてフレームワークの機能を注入してくれるおかげで、記述するコードは他のフレームワークより少ないと思います。そこが売りになっています。
しかも必要な機能のみ組み込まれるので、ビルド後のフットプリントも小さいです。
また、ガベージコレクターに負荷をかける Virtual DOM の差分更新は使わず、インタラクティブに DOM 更新します。
ドキュメントも必要十分に整っていると思います。
特にチュートリアルがよくできており、インタラクティブに Svelte の様々な機能を一通り学ぶことができます。
興味を持たれた方はぜひ一度やってみることを強くお勧めします。
プロジェクトの初期化
Svelte 公式のテンプレートリポジトリ sveltejs/template から degit1 を使ってプロジェクトの雛形を作ります。
次のコマンドを実行します。
$ npx degit sveltejs/template <プロジェクト名> $ cd <プロジェクト名> $ yarn install
次のファイル群が生成されます。
ファイルツリーを見る
❯ tree -I node_modules
.
├── README.md
├── package.json
├── public
│ ├── favicon.png
│ ├── global.css
│ └── index.html
├── rollup.config.js
├── scripts
│ └── setupTypeScript.js
├── src
│ ├── App.svelte
│ └── main.js
└── yarn.lock
Svelte コンポーネント
テンプレートに含まれる Svelte コンポーネント src/App.svelte を見てみましょう。
<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>
基本構造は HTML になっています。
Vue の SFC の様にマークアップ部分を <template>
タグで囲う必要もないです。
<script>
の export let name;
で定義されている変数はコンポーネントの引数に入力引数となります。
で、このコンポーネントを初期化するコードが src/main.js で中身は次のとおりです。name
の値がプロパティして与えられているのがわかりますね。
import App from './App.svelte'; const app = new App({ target: document.body, props: { name: 'world' } }); export default app;
動作確認
まずはこの状態で動作確認しておきましょう。
次のコマンドで開発用のサーバ起動します。
$ yarn dev
起動後、http://localhost:5000 にアクセスします。次のページが表示されれば OK です。
public/build ディレクトリ内にコンパイル結果の次のファイル群ができています。
- bundle.css
- bundle.js
- bundle.js.map
bundle.js にはマークアップとスクリプトの中身がコンパイルされています。コンポーネントのコンパイル結果と思われる箇所を見てみると、マークアップも JavaScript コードに変換されているのがわかります。よく見ると svelte-<hash値>
という CSS クラスがつけられています。
bundle.css を見ると各定義に先程の CSS クラスが対応するように付与されています。このことからコンポーネント内の <style>
の影響範囲がコンポーネント内に限定される仕組みになっていることがわかります2。
これで Svelte で一応、開発はできる状態ではあるのですが JavaScript より TypeScript で書きたいし、 HTML と CSS より Pug と Sass の方が楽です。
そして、本格的に開発するなら Linter やテストフレームワークも欲しいところです。
ということで以降は、それらを順に導入していきます。
Typescript を有効にする
デフォルトは JavaScript での実装となっていますが、TypeScript で実装できるようにセットアップするスクリプトが用意されています。
実行すると、src ディレクトリ内の JavaScript が TypeScript に書き換えられます。
$ node scripts/setupTypeScript.js $ yarn install
rollup.config.js 以外の .js ファイルは .ts に変換されますし、App.svelte 内も TypeScript に書き換えられています。実行後のファイル一覧を見る
❯ tree -I node_modules
.
├── README.md
├── package.json
├── public
│ ├── build
│ │ ├── bundle.css
│ │ ├── bundle.js
│ │ └── bundle.js.map
│ ├── favicon.png
│ ├── global.css
│ └── index.html
├── rollup.config.js
├── src
│ ├── App.svelte
│ └── main.ts
├── tsconfig.json
└── yarn.lock
yarn dev
で開発用サーバを起動して http://localhost:5000 にアクセスして表示できれば OK です。表示されるものは TypeScript を有効にする前と特に変わりません。
Pug と Sass を導入する
TypeScript を有効にすると svelte-preprocess が devDependencies に追加されています。
これが、Pug や Sass で書かれたコードの変換をサポートしているのでほとんど設定なく導入できます。
Pug
Pug は Ruby の Slim に似たシンタックスで HTML を記述できるテンプレートエンジンです。
次のコマンドで Pug をインストールします。
yarn add -D pug
src/App.svelte を開くと次のような HTML 部分があります。
<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>
これを Pug で次の様に書き換えます。
Pug を利用す場合には、マークアップは <template>
タグで囲う必要があります。
<template lang='pug'> main h1 Hello {name}! p | Visit the a(href="https://svelte.dev/tutorial") Svelte tutorial | to learn how to build Svelte apps. </template>
これで閉じタグ無しのらくらくマークアップ記述ができるようになります。 開発サーバを起動して表示できれば OK です。
Sass
Sass も Pug と同様、モジュールを追加するだけで使えるようになります。
$ yarn add -D sass
src/App.svelte を開いて、<style>
の部分を次の様に書き換えます。
<style lang='sass'> 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>
開発サーバを起動して表示できれば OK です。
Jest の導入
プロダクトを実装する上でテストコードがあるとないとでは、変更やライブラリのアップデート時の安心感が雲泥の差です。
ということでテスティングフレームワーク Jest を導入していきましょう。
まずは必要なモジュールをインストールします。
$ yarn add -D jest ts-jest ts-node @types/jest @testing-library/svelte svelte-jester @testing-library/jest-dom
Jest の設定ファイル jest.config.ts を作成します。最低限だとこんな感じです。
export default { clearMocks: true, coverageDirectory: 'coverage', moduleFileExtensions: ['js', 'ts', 'svelte'], testEnvironment: 'jsdom', transform: { '^.+\\.svelte$': ['svelte-jester', { preprocess: true }], '^.+\\.ts$': 'ts-jest', '^.+\\.js$': 'babel-jest', }, };
次に svelte.config.js を作成します。
rollup.config.js に同様の設定がありますが、Jest の実行には rollup は使わないので作る必要があります。
const sveltePreprocess = require("svelte-preprocess"); module.exports = { preprocess: sveltePreprocess({ sourceMap: true }), };
テストコードで使う関数などの型定義を読み込ませるために tsconfig.json に次の様に設定を追加します。
{ "extends": "@tsconfig/svelte/tsconfig.json", "include": ["src/**/*"], "exclude": ["node_modules/*", "__sapper__/*", "public/*"], "compilerOptions": { // <= 追加 "types": ["svelte", "jest", "node"], // <= 追加 } // <= 追加 }
そして、App.svelte をテストするコード test/App.test.ts はこんな感じになります。
import { render } from '@testing-library/svelte'; import App from '../src/App.svelte'; test('should render', () => { const results = render(App, { props: { name: 'world' } }); expect(() => results.getByText('Hello world!')).not.toThrow(); });
実行しやすいように package.json の scripts
に test
も追加しておきましょう。
{ // 〜 略 〜 "scripts": { "build": "rollup -c", "dev": "rollup -c -w", "start": "sirv public", "validate": "svelte-check", "test": "jest" // <== 追加 }, // 〜 略 〜 }
最後に yarn test
でエラーなく実行できれば OK です。
ESlint の導入
Svelte3 で ESLint を使うには eslint-plugin-svelte3 を使います3。
次のコマンドでインストールします。
$ yarn add -D eslint eslint-plugin-svelte3 eslint-plugin-jest $ ./node_modules/.bin/eslint --init ✔ How would you like to use ESLint? · style ✔ What type of modules does your project use? · esm ✔ Which framework does your project use? · none ✔ Does your project use TypeScript? · No / Yes ✔ Where does your code run? · browser, node ✔ How would you like to define a style for your project? · guide ✔ Which style guide do you want to follow? · airbnb ✔ What format do you want your config file to be in? · JavaScript Checking peerDependencies of eslint-config-airbnb-base@latest The config that you've selected requires the following dependencies: @typescript-eslint/eslint-plugin@latest eslint-config-airbnb-base@latest eslint@^5.16.0 || ^6.8.0 || ^7.2.0 eslint-plugin-import@^2.22.1 @typescript-eslint/parser@latest ✔ Would you like to install them now with npm? · No / Yes # 〜 略 〜 Successfully created .eslintrc.yml file in /Users/hero/Develop/javascript/svelte3-ts-pug-sass-template $ yarn install $ rm package-lock.json
上記を実行すると .eslintrc.js ができていますが、Svelte3 に必要な設定が含まれていないので追加します。
追加したものは次の通りです。svelte
とコメントした部分が Svelte3 のための設定となります。
jest
のコメントは Jest
のための設定です。
module.exports = { env: { browser: true, es2021: true, node: true, 'jest/globals': true, // <== svelte }, extends: [ 'airbnb-base', ], parser: '@typescript-eslint/parser', parserOptions: { ecmaVersion: 12, sourceType: 'module', }, plugins: [ 'svelte3', //<== svelte '@typescript-eslint', 'jest', // <== jest ], overrides: [ // <== svelte { // <== svelte files: ['*.svelte'], // <== svelte processor: 'svelte3/svelte3', // <== svelte }, // <== svelte ], // <== svelte ignorePatterns: [ 'public/build/*', ], settings: { // <== svelte 'svelte3/typescript': require('typescript'), // eslint-disable-line global-require 'svelte3/ignore-styles': (attributes) => attributes.lang && attributes.lang === 'sass', }, rules: { // これらは最小限必要と思う。その他ルールの追加はお好みで。 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], 'import/no-mutable-exports': 'off', 'import/prefer-default-export': 'off', }, };
私は YAML 形式の .eslintrc.yml を好みますが、 settings
で関数を設定する必要があり、JavaScript 形式が必須となります。
さらに次の様に packasge.json の script を追加しておくと便利です。
"scripts": { "eslint": "eslint --ext svelte,js,ts ./" },
で、次のコマンドで、自動で修正できるところを修正しておきましょう。
$ yarn elint --fix
自動修正できない箇所は手で直しましょう。
開発をサポートしてくれるツール
開発をサポートする周辺ツールも紹介します。
REPL
Web ブラウザで気軽に試すことができます。ちょっと機能を試すのに便利そうです。 - Hello world • REPL • Svelte
エディタ拡張
VSCode のエクステンションが公式に提供されています。 - Svelte for VS Code - Visual Studio Marketplace
ブラウザ拡張
今のところ、公式のものはなさそうですが、いくつか存在します。
Svelte Devtools
Vue のVue.js devtoolsに相当する拡張。
Svelte Sight
Svelte Devtools と類似の用途の拡張だと思われますが、コンポーネント階層がグラフィカルに表示できます。 - Chrome
これで一通りの開発できる環境が整いました。
参考
- svelte-preprocess Pug
- svelte-preprocess scss, sass
- Svelte with TypeScript and Jest (Starter Project)
おまけ
この手順を実施したあとの状態を次のリポジトリに置いておきます。
まとめ
Svelte はコード量を少なく高機能なコンポーネントが記述できる UI フレームワークです。
チュートリアルは本当によくできており、試しながら様々な機能を学べます。ざっと見ると、データストアやイベントハンドリングや非同期関数の扱い、<input>
などの入力要素とのデータバインディング、トランジションなど他のフレームワークにある機能は一通り揃っていると思います。
Vue.js の Nuxt.js に相当する Sapperというフレームワークもあるようです。
Rails way を踏み外さない Rails のプロダクトの場合、JS のビルドには Webpacker 、つまり Webpack を使うことになるかと思います。
Rollup の代わりに Webpack を使うテンプレート sveltejs/template-webpack も用意されています。試してはないですが、それを参考にすればできるかなと思います4。
さて、結構長くなりましたが、今回のエントリは前フリです。
Svelte コンポーネントは簡単に Web Components としてビルドもできます。
次回は Web Component を作ってみようと思います。
最後に
アクトインディではエンジニアを募集しています。 actindi.net
-
Git リポジトリをローカルにコピーするコマンドラインツール。 https://github.com/Rich-Harris/degit↩
-
スタイルのスコープはコンポーネントだとチュートリアルにも記述されています。コンポーネント内で別のコンポーネントを使ってもネストされた側に影響しません。また、グローバルスコープで定義する方法も用意されています。↩
-
v3.1.0 で TypeScript の Svelte モジュールにも対応しました。↩
-
いっそ Rails のプロジェクトとは別のリポジトリで Web Components としてコンポーネントを実装すると依存のしがらみ少なく UI を実装できるのではと思ったり…。↩