ハイブリッドなモバイルアプリを開発する際には、UIフレームワークの導入が欠かせません。
Monacaでは公式にOnsenUIを提供していますが、他にも選択肢があります。バージョンアップが頻繁に行われており、高機能なUIフレームワークとして注目されているのがFramework7です。
Framework7を使ってUIを作るのは簡単です。まず基本となるルーティングやイベントなどを押さえておく必要があるでしょう。この記事では、Framework7の基礎について解説します。
記事では、Framework7のバージョン6を対象としています。以前、またはバージョン7以降では使い方が変わる可能性がありますのでご注意ください。
また、今回は素のJavaScript(Vanilla JS)を対象としています。VueやReactなどのフレームワークは利用していません。
Framework7の導入
Framework7は 、「framework7.min.css」 と 「framework7.min.js」 を読み込んで利用します。このファイルは、Framework7 CLIにて新規でアプリケーションを作成し、作成完了後に 「npm run postinstall」 を実行すると 「www/framework7」 以下に作成されます。
<link rel="stylesheet" href="framework7/css/framework7.min.css">
<script src="framework7/js/framework7.min.js"></script>
ファイルを直接アプリ内に配置しないでCDNを利用する場合は、次のように設定することでFramework7を利用できます。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/framework7@6.3.16/framework7.min.css">
<script src="https://cdn.jsdelivr.net/npm/framework7@6.3.16/framework7.min.js"></script>
そして、HTMLにFramework7用のDOMを用意します。一般的に #app
と設定します。
<div id="app">
</div>
HTMLのDOMの操作は、Framework7で提供されているDom7というDOM操作用のライブラリをローカル変数に展開するのが一般的です。
Dom7は、jQueryライクにクラスを追加したり、要素を変更したりできます。簡単な使い方は後述しますが、詳しくはDom7 - カスタムDOMライブラリ | Framework7 Documentationを参照してください。
const $$ = Dom7;
そして、JavaScriptにてFramework7を次のように初期化します。
const app = new Framework7({
el: '#app', // 展開するDOM
name: 'Framework7', // アプリ名
theme: 'auto', // テーマ
// アプリ全体で利用するデータ(オプション)
data: function () {
return {
user: {
firstName: 'John',
lastName: 'Doe',
},
};
},
// アプリ全体の共通メソッド(オプション)
methods: {
helloWorld: function () {
app.dialog.alert('Hello World!');
},
},
// ルーティング
routes,
});
ここまでで初期化が終了します。
後は index.html
の #app
の中にframework7のルールに合わせてHTMLを記述していきます。
ルーティング
※ ルーターの日本語公式ドキュメントは、ルーター | Framework7 Documentationを参照してください。
Framework7を使う際に、少し難しくなる概念として「ルーティング」があります。
先ほど初期化する際に routes
で指定していた変数です。たとえば次のような形で定義されます。
const routes = [
{
path: '/',
url: './index.html',
},
{
path: '/about/',
url: './pages/about.html',
},
// 途中省略
{
path: '(.*)',
url: './pages/404.html',
},
];
path
キーで指定された形でアクセスがあると、 url
で指定されたページを表示します。
たとえば、パス「/about」にアクセスがあると、urlで指定しているpagesディレクトリ配下にある「about.html」が表示されます。
パス「/about」にリンクするaタグを次のように記述します。ここでは、JavaScriptを記述する必要はありません。
<a href="/about/" class="item-content item-link">
<div class="item-inner">
<div class="item-title">About</div>
</div>
</a>
そして、 pages/about.html
では、 div.page
の中でFramework7のHTMLを記述します。
<div class="page" data-name="about">
<!-- HTMLの内容 -->
</div>
ここまでの準備で、パス「/about」にリンクする aタグをクリックすると、「pages/about.html」が表示されるようになります。
補足となりますが、この url
キーで指定したページの場合、JavaScriptを実行したり、前の画面から情報を引き継いで表示を変更したりできません。
JavaScriptを利用する場合には、次に紹介する componentUrl を使います。
コンポーネント実行
※ ルーターコンポーネントの日本語公式ドキュメントは、ルーターコンポーネント | Framework7 Documentationを参照してください。
動的にページ内容を構築したり、ページ内でクリックなどのイベントを使いたい場合には componentUrl
を使います。ルーティングのファイル内に、次のように記述します。
const routes = [
{
path: '/',
url: './index.html',
},
// 省略
{
path: '/product/:id/',
componentUrl: './pages/product.html',
},
}
この componentUrl
を使った場合、componentUrlで指定したHTMLページの基本形は、次のようになります。この形は固定と考えてください。
<template>
<div class="page">
<!-- 省略 -->
</div>
</template>
<!-- CSS設定 -->
<style>
</style>
<!-- JavaScript -->
<script>
export default function (props, {$f7, $on}) {
return $render;
}
</script>
export default function (props, {$f7, $on}) {
では $f7
と $on
しか取得していませんが、他にも以下のような変数が取得できます。
変数名 | 内容 |
---|---|
$ | Dom7ライブラリです。 |
$el | valueプロパティがページのDom7インスタンスを持ったオブジェクトです。 |
$emit | カスタムイベントを発行するのに使います。 |
$f7 | Framework7アプリのインスタンスです。 |
$f7ready | アプリが初期化された際に実行されます。 |
$f7route | 現在のページのルーティング情報です。 |
$f7router | ページナビゲーションを行うためのルーティングオブジェクトです。 |
$h | Framework7に適したHTMLを生成する際に利用します。 |
$on | ページに関連したイベントを設定します(詳細は後述) |
$onBeforeMount | ライフサイクルです(詳細は後述) |
$onBeforeUnmount | ライフサイクルです(詳細は後述) |
$onBeforeUpdate | ライフサイクルです(詳細は後述) |
$onMounted | ライフサイクルです(詳細は後述) |
$onUnmounted | ライフサイクルです(詳細は後述) |
$onUpdated | ライフサイクルです(詳細は後述) |
$once | ページに関連したイベントを設定します(詳細は後述) |
$store | アプリ全体での変数を管理するストアのオブジェクトです。 |
$theme | iOS/Androidで切り替えるのに利用します。 |
$tick | 表示が更新されたのを通知します。 |
$update | 表示を強制更新する際に実行します。 |
return $renderの意味
componentUrlでは return $render
という処理が出てきます。
この $render
という変数はどこにも存在しませんが、実際にはFramework7側で次のような内容に書き換えられ処理が実行されます。
return function ($ctx) {
var $ = $ctx.$;
var $h = $ctx.$h;
var $root = $ctx.$root;
var $f7 = $ctx.$f7;
var $f7route = $ctx.$f7route;
var $f7router = $ctx.$f7router;
var $theme = $ctx.$theme;
var $update = $ctx.$update;
var $store = $ctx.$store;
return $h`
// <template>タグ内で定義したHTMLの内容
`
};
ライフサイクル&ページイベント
ページをマウントしたり、DOMから削除したりする際に実行されるのがライフサイクルイベントです。
ライフサイクルに合わせて呼び出される順番があります。次の表の上から順番に呼ばれます。
変数名・指定法 | 内容 |
---|---|
$onBeforeMount | DOMがマウントされる前 |
$onMounted | DOMがマウントされた後 |
$on('pageInit') | ページが初期化された後 |
$on('pageBeforeIn') | ページが呼び出される前 |
$on('pageAfterIn') | ページが呼び出された後 |
$on('pageBeforeOut') | ページから移動する前 |
$on('pageAfterOut') | ページから移動した後 |
$on('pageBeforeRemove') | ページがDOMから削除される前 |
$onBeforeUnmount | DOMがアンマウントされる前 |
$onUnmounted | DOMがアンマウントされた後 |
HTML内での動的な表示
templateタグ内は、JavaScriptのテンプレートリテラルとなっています。
templateタグ内で、 ${}
を利用することで変数を表示できます。以下の例では Hello という文字列を出力します。
<template>
<div class="page">
<h1>${a}</h1> <!-- ここに出力されます -->
</div>
</template>
<!-- JavaScript -->
<script>
export default function (props, {$f7, $on}) {
const a = 'Hello'; // ここで変数を定義しています
return $render;
}
</script>
ボタンなどのイベント実行
template内でのイベント(ボタンを押した、項目が変わったなど)でJavaScriptを呼び出す場合には、「@」 以降にイベント名を指定することで実装します。 「@change」 や 「@keyup」 などが利用できます。
<template>
<div class="page">
<button @click=${func}>ボタン</button> <!-- スクリプトで定義した関数を指定します -->
</div>
</template>
<!-- JavaScript -->
<script>
export default function (props, {$f7, $on}) {
// HTML側で指定したイベント
function func() {
// ボタンを押した時の処理
alert('ボタンを押した!');
}
return $render;
}
</script>
非同期処理
ページ遷移するタイミングで、アプリの外部からデータを取得してから画面遷移させることもできます。
それが async
になります。これはルーティングで次のように書きます。
const routes = [
{
path: '/request-and-load/user/:userId/',
// 非同期処理の場合
async: function ({ router, direction, to, from, resolve, reject }) {
// 非同期処理を書きます
// 完了後にresolveを呼びます
resolve({
componentUrl: './pages/request-and-load.html',
},
{
props: {
user,
});
},
},
// 省略
];
上記の関数で取得できるのは次の変数になります。
変数名 | 内容 |
---|---|
app | Framework7アプリ |
direction | "forward" または "backward" |
from | 遷移元の画面の情報 |
to | 遷移先の画面の情報 |
router | ルーターインスタンス |
reject | 非同期処理に失敗した場合にはこの関数を呼び出します |
resolve | 非同期処理が成功して画面遷移を行う時にはこの関数を呼び出します |
from
や to
は次の内容を持っています。
変数名 | 内容 |
---|---|
hash | URLのhash(アンカー) |
name | ルーティングで定義した名前 |
params | URLパターンで定義したパラメータとその値 |
parentPath | 入れ子の場合、親URL |
path | アクセスしているパス |
query | クエリストリング |
route | ルーティングインスタンス |
url | アクセスしているURL |
なお、非同期処理を実行している時には画面遷移せずに止まってしまうため、ローディングアイコンを表示するとユーザビリティーが高くなるでしょう。そのためのメソッドとしてpreloaderが用意されています。
// 表示
app.preloader.show();
// 非表示
app.preloader.hide();
非同期処理が完了したら resolve
関数を呼び出します。その際にはルーティングのようにURL情報を送信します。
resolve({
componentUrl: './pages/request-and-load.html',
},
{
props: {
user,
}
});
JavaScriptで画面遷移を行う場合
これまでは a
タグを使って画面遷移を行っていましたが、JavaScriptから画面遷移させる場面も多いでしょう。その時には、 $f7router
を使います。この時、propsを使ってオブジェクトを渡すこともできます。
なお、これはcomponentUrlでの利用に限定されます。
$f7router.navigate(/request-and-load/user/${userId}/
, {
props: {
user,
},
});
propsを使う利点としては、一覧画面で取得したデータを詳細画面に引き渡すのに利用できます。詳細画面側では、データを再取得する必要がないので、表示が高速に行えるでしょう。
export default function (props, {$f7, $on}) {
// 前画面からのデータ引き渡し
const { user } = props;
return $render;
}
URLではなく name
を使った画面遷移も可能です。この場合、ルーティング時にあらかじめ name
を決めておきます。
たとえば、以下のようなルーティングを作成したとします。
const routes = [
{
path: '/',
url: './index.html',
},
// 省略
{
name: 'Product', // nameを利用
path: '/product/:id/',
componentUrl: './pages/product.html',
},
}
この場合のプロダクト画面への遷移は次のようになります。
$f7router.navigate({
name: 'Product',
},
params: {
id: '0001',
},
);
戻る機能
前の画面に戻る場合には、次のようにします。
$f7router.back();
HTMLの場合は、aタグのclass内に .back
を指定することで、戻る機能を実現できます。
<a href="#" class="link back">
<i class="icon icon-back"></i>
<span class="if-not-md">Back</span>
</a>
DOM
※ DOMの日本語公式ドキュメントは、DOM7 | Framework7 Documentationを参照してください。
Framework7におけるDOMの扱い方です。
まず、jQuery風にDOMを扱えるDom7が用意されています。 $$
にするのがお勧めされています。
const $$ = Dom7;
Dom7は、jQueryライクにDOM操作が可能です。たとえば次のようなメソッドが用意されています。これはjQueryを使ったことがあればすぐに使い方が分かるでしょう。
- addClass
- removeClass
- hasClass
- toggleClass
- append
- appendTo
- prepend
- prependTo
- insertBefore
- insertAfter
- next
- prev
- parent
- parents
- find
- children
- remove
- empty
さらに値を取得する、設定するのに便利なメソッドもあります。
- prop
- attr
- removeAttr
- val
- data
- removeData
- html
- text
CSSを使ったトランスフォームも可能です。
- transform
- transition
イベント系メソッドも用意されています。
- on
- once
- off
- trigger
- transitionEnd
- animationEnd
- click
- focus
- keyup
- keydown
- change
- submit
- touchstart
- touchend
- touchmove
他にも多くのメソッドが用意されています。これだけあれば、DOM操作はとても簡単にできるでしょう。
$hを利用する
componentUrlで動的に画面描画を行う際には、 $h
を使います。
<ul>
${user.links.map((link) => $h`
<li>
<a class="external" target="_blank" href="${link.url}">
${link.title}
</a>
</li>
`)}
</ul>
ストア
※ ストアの日本語公式ドキュメントは、ストア | Framework7 Documentationを参照してください。
ストアは、状態管理用のオブジェクトです。主にアプリ内で共通する情報を管理するのに利用します。JavaScriptではグローバル変数を定義できますが、様々な場所から変更してしまうと、その状態が思いも寄らぬ動作を引き起こすことがあります。そうした誤動作を防ぐ意味でもストアの利用をお勧めします。
まず createStore
関数を使ってストアを定義します。
const createStore = Framework7.createStore;
const store = createStore({
state: {}, // 状態の定義(後述)
getters: {}, // データの取得(後述)
actions: {}, // データの操作(後述)
});
const app = new Framework7({
name: 'My App',
theme: 'auto',
el: '#app',
store: store, // ストアを指定
routes: routes,
});
state
ストアで管理する状態を定義(初期化)します。
const store = createStore({
state: { // 状態の定義
products: [
{
id: '1',
title: 'Apple iPhone 8',
},
{
id: '2',
title: 'Apple iPhone 8 Plus',
},
{
id: '3',
title: 'Apple iPhone X',
},
]
},
// 省略
});
getters
データを取得するためのメソッドを定義します。
const store = createStore({
// 省略
getters: {
products({ state }) {
return state.products;
}
},
// 省略
}
このように定義することで、次のようにアクセスできます。
export default (props, { $f7route, $store }) => {
const products = $store.getters.products;
};
actions
データを追加したり、削除したりするようなメソッドを定義します。既存データに対しては state.products
のようにしてアクセスできます。
const store = createStore({
// 省略
actions: {
addProduct({ state }, product) {
state.products = [...state.products, product];
},
},
});
このメソッドに対しては次のようにアクセスできます。
$store.dispatch('addProduct', {
id: '4',
title: 'Apple iPhone 12',
});
ストア | Framework7 Documentationでは、ストア内のデータを変更することで画面の表示結果を更新する例があります。アプリ全体での状態管理を行うことで、DOM管理やデータ管理を容易にできるでしょう。
まとめ
Framework7の基本動作としては、ルーティング、DOM、ストアを押さえておけば十分でしょう。後は便利なコンポーネントやアイコン表示を覚えれば、リッチなアプリが開発できるようになります。ぜひFramework7を使いこなして、高機能なハイブリッドアプリを開発してください。
Framework7 - Full Featured Framework For Building iOS, Android & Desktop Apps