SvelteとIonicで始めるモバイルアプリケーション開発入門 Part1. 導入編
開発環境の構築とプロジェクトの作成
はじめに
ネクストビートでは、2022年の4月から、Svelte / SvelteKitを採用し、基幹事業である「保育士バンク!」の技術移行プロジェクトを推進してきました。(詳しくはこちらの記事を読んでみてください。)
もともと、バックエンドをScala + Play Frameworkで作っていたこともあり、フロントエンドも、ScalaベースのテンプレートエンジンであるTwirlを採用していました。また一部のページはAngularやVue、jQueryなどで書かれていました。
これらの言語、フレームワークからSvelte / SvelteKitに切り替えたことにより、開発生産性が向上し、キャッチアップコストも少なくなったと、多くのメンバーが感じています。
これを受け、モバイルアプリの開発にもSvelteを採用できないかと考え、試行錯誤しながらベースプロジェクトを作成し、「保育士バンク!」のアプリの書き換えをおこなってきました。(もうすぐリリース予定!)
今後、何本かに渡り、モバイルアプリをSvelteで開発する方法についての記事をお届けできればと思います。
プロジェクトの作成
まずは、フロントエンドのビルドツールであるViteを使って、プロジェクトを作成します。
プロジェクトのテンプレートにはSvelteとTypeScriptの組み合わせであるsvelte-tsを選択します。(※ 詳しくは公式ドキュメントを参照)
# Viteのプロジェクトを作成する
npm create vite@latest svelte-ionic -- --template svelte-ts
Need to install the following packages:
create-vite@latest
Ok to proceed? (y) y
Scaffolding project in /current-path/svelte-ionic...
Done. Now run:
cd svelte-ionic
npm install
npm run dev
コマンドの実行が完了すると、以下のような構造のプロジェクトが作成されます。
tree svelte-ionic -L 1
svelte-ionic
├── README.md
├── index.html
├── package.json
├── public
├── src
├── svelte.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
以下のコマンドを実行してアプリケーションを起動します。
# 作成したプロジェクトのtディレクトリに移動
cd svelte-ionic
# npmの依存パッケージをインストールする
npm install
# プロジェクトを実行する
npm run dev
> svelte-ionic@0.0.0 dev
> vite
Forced re-optimization of dependencies
VITE v4.2.0 ready in 2752 ms
➜ Local: http://127.0.0.1:5173/
➜ Network: use --host to expose
➜ press h to show help
http://127.0.0.1:5173/にアクセスすると以下のようなページが表示されます。
プロジェクトの説明
では、作成されたソースコードを見ていきましょう。
重要なところにはコメントを追記しています。
index.html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
</head>
<body>
<!-- App.svelteを埋め込むためのコンテナ -->
<div id="app"></div>
<!-- main.tsをmoduleとして読み込んで実行している -->
<script type="module" src="/src/main.ts"></script>
</body>
</html>
アプリケーションのエントリーポイントです。
main.tsを読み込み、export defaultされているapp関数の処理を実行します。
/src/main.ts
import './app.css'
import App from './App.svelte'
/** App.svelte(SvelteのComponent)をid="app"のDOMの内部に埋め込む */
const app = new App({
target: document.getElementById('app'),
})
/** デフォルトで実行される処理として上記のapp関数を設定する */
export default app
index.htmlから呼び出されているファイルです。
export defaultされているapp関数によって、SvelteのComponentが作成され、targetの要素をrootとして、ComponentがDOMに埋め込まれます。(※ 詳しくはSvelteの公式ドキュメントを参照)
これにより、DOMが以下のような構造に変更されます。
<body>
<div id="app">
<!-- ここにApp.svelteのHTMLテンプレート部分が埋め込まれる -->
<main>
<div>
...
</div>
</main>
</div>
</body>
/src/App.svelte
<script lang="ts">
import svelteLogo from './assets/svelte.svg'
import viteLogo from '/vite.svg'
import Counter from './lib/Counter.svelte'
</script>
<main>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} class="logo" alt="Vite Logo" />
</a>
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
</a>
</div>
<h1>Vite + Svelte</h1>
<div class="card">
<Counter />
</div>
<p>
Check out <a href="https://github.com/sveltejs/kit#readme" target="_blank" rel="noreferrer">SvelteKit</a>, the official Svelte app framework powered by Vite!
</p>
<p class="read-the-docs">
Click on the Vite and Svelte logos to learn more
</p>
</main>
<style>
// 省略
</style>
先ほどDOMに埋め込まれたSvelteのComponentです。
Chromeなどのブラウザの開発ツールで実際のDOMを見てみましょう。
上の画像のように、想定通りのDOMの構造になっていますね。
普段はあまり使う機会がないかもしれませんが、このようにして、SvelteのComponentをスクリプトからDOMに埋め込むことができます。
サンプルプロジェクトの作成と、初期コードの説明は以上となります。
Ionicの導入
次に、Ionic Framework(以下Ionic)を導入します。
Ionicは、Web標準(HTML, CSS, JavaScript)の技術を使って、モバイルアプリに最適なUIを構築できるSDKです。
モバイルアプリ開発に特化した様々なエコシステムを持ち、後述するCapacitorと組み合わせることで、簡単にモバイルアプリを開発することができます。
また、Angular、React、Vueなどのモダンなフロントエンドのフレームワークに最適化されたパッケージが配布されており、チュートリアルやドキュメントも豊富に用意されています。フレームワークを使わないVanilla JSのプロジェクトにも組み込み可能です。
(※ 詳しくはIonicの公式ドキュメントを参照)
今回は、IonicをSvelteのプロジェクトに導入する方法について解説します。IonicにはSvelteの公式のパッケージがないため、Vanilla JSのプロジェクトに導入するのと同じイメージで進めていきます。
インストール
Ionic CLIをインストールし、ionicコマンドを使うことで、簡単にプロジェクトを作成することができます。今回は、既存のプロジェクトにIonicを導入するため、initコマンドを使います。
Project nameにはsvelte-ionicを、Project typeにはcustomを設定します。
# @ionic/cliをグローバルインストール
npm install -g @ionic/cli
# 既存プロジェクトにionicを導入する
ionic init
? Project name: svelte-ionic
[WARN] Could not determine project type.
Please choose a project type from the list.
? Project type: Custom (custom)
[OK] Your Ionic project has been initialized!
初期化処理が完了し、ionic.config.jsonファイルが作成されました。
このファイルにIonicの設定を書いていくことになります。
{
"name": "svelte-ionic",
"integrations": {},
"type": "custom"
}
次に、以下のコマンドで、@Ionic/coreをインストールします。
npm install @ionic/core
これで、開発に必要な準備が整いました。
Ionicの初期化処理
続いて、IonicのComponentを使うために必要な初期化処理を書いていきます。
/src/main.ts
import { initialize } from "@ionic/core/components"
import { defineCustomElement as defineCustomElementIonApp } from '@ionic/core/components/ion-app'
import { defineCustomElement as defineCustomElementIonNav } from '@ionic/core/components/ion-nav'
import { defineCustomElement as defineCustomElementIonTabs } from '@ionic/core/components/ion-tabs'
import { defineCustomElement as defineCustomElementIonTab } from '@ionic/core/components/ion-tab'
import { defineCustomElement as defineCustomElementIonTabBar } from '@ionic/core/components/ion-tab-bar'
import { defineCustomElement as defineCustomElementIonTabButton } from '@ionic/core/components/ion-tab-button'
import { defineCustomElement as defineCustomElementIonHeader } from '@ionic/core/components/ion-header'
import { defineCustomElement as defineCustomElementIonToolbar } from '@ionic/core/components/ion-toolbar'
import { defineCustomElement as defineCustomElementIonContent } from '@ionic/core/components/ion-content'
import { defineCustomElement as defineCustomElementIonBackButton } from '@ionic/core/components/ion-back-button'
import { defineCustomElement as defineCustomElementIonInfiniteScroll } from '@ionic/core/components/ion-infinite-scroll'
import { defineCustomElement as defineCustomElementIonInfiniteScrollContent } from '@ionic/core/components/ion-infinite-scroll-content'
import { defineCustomElement as defineCustomElementIonList } from '@ionic/core/components/ion-list'
import { defineCustomElement as defineCustomElementIonRefresher } from '@ionic/core/components/ion-refresher'
import { defineCustomElement as defineCustomElementIonRefresherContnet } from '@ionic/core/components/ion-refresher-content'
import App from './App.svelte'
/** CSSのインポート */
import "@ionic/core/css/ionic.bundle.css"
/**
* 初期化処理
*/
const init = (): void => {
/** Ionicの初期化処理 */
initialize()
/** Ionicの各種コンポーネントをカスタムエレメントとして定義 */
defineCustomElements()
/** body直下にApp.svelteを埋め込む */
new App({
target: document.body
})
/** htmlタグにion-ceクラスを追加 (bodyをdisplay:block;に) */
document.documentElement.classList.add('ion-ce')
}
/**
* Ionicのコンポーネントをカスタムエレメントとして定義
* 使いたいコンポーネントだけdefineCustomElementをimportして実行する
*/
const defineCustomElements = (): void => {
defineCustomElementIonApp()
defineCustomElementIonNav()
defineCustomElementIonTabs()
defineCustomElementIonTab()
defineCustomElementIonTabBar()
defineCustomElementIonTabButton()
defineCustomElementIonHeader()
defineCustomElementIonToolbar()
defineCustomElementIonContent()
defineCustomElementIonBackButton()
defineCustomElementIonInfiniteScroll()
defineCustomElementIonInfiniteScrollContent()
defineCustomElementIonList()
defineCustomElementIonRefresher()
defineCustomElementIonRefresherContnet()
}
/** 初期化処理をdefaultとしてexport */
export default init()
まずは、IonicのCSSのimportを行います。公式ドキュメントには、ReactとVueの例が載っています。必要最低限のstyleでよければ、core.cssを選べば良いみたいですね。ionic.bundle.cssには、すべてのIonic Global Stylesheetsが含まれているそうです。今回は、ionic.bundle.cssをimportするようにしています。
次に、initialize関数で、IonicのComponentを使うための初期化処理を実行します。
その後、各ComponentのdefineCustomElement関数を実行しています。これで、<ion-xxx />のように、HTML内でIonicのComponentを呼び出すことができるようになります。
また、body直下にApp.svelteを埋め込むように変更しています。
最後に、htmlタグにion-ceというクラスを付与しています。
これがないと、bodyブロックがdisplay: none;のままになるため、bodyが描画されないみたいです。Angularなどのパッケージでは、画面のちらつき防止策として、初期化処理などが終わった後に.hydratedクラスが付与されるようになっているのかもしれません。
// ionic-framework/core/src/css/structure.scss
html:not(.hydrated) body {
display: none;
}
html.ion-ce body {
display: block;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS + Ionic</title>
</head>
<body>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
合わせて、index.htmlも少しだけ修正しています。
以上で、一通りの初期化処理が完了しました。
あとは、App.svelteの中身、つまり、アプリケーション部分を自由に書き換えていくだけです。
IonicのComponentの使い方や、ルーティング処理の実装に関しては、長くなるので、別の記事で紹介できればと思います。
続きはこちら
We are hiring!
「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」
を理念に掲げ一緒に働く仲間を募集しております。