ハイブリッドなモバイルアプリを開発する際には、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 非同期処理が成功して画面遷移を行う時にはこの関数を呼び出します

fromto は次の内容を持っています。

変数名 内容
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