Ionic v3 -> v5 アップデートでやったこと


表題の通り、Ionic v3ベースで作成されていたプロジェクトをv5に置き換えました。そのときに行った操作などをまとめます。(移行後のフレームワークはAngularです)

対象読者

Ionic v3のプロジェクトをv5またはv41へアップデートすることを検討されている方

なぜアップデートしたか?

Ionic v3系のアップデートが止まっており、v3で残っているバグの影響が無視できなくなったためです。

大まかな手順

v3->v4バージョンアップ方法の概要については、Ionic公式にv4への移行ガイドというページがあるので、これをベースに進めていきます。ざっと、以下の手順で進めていきました。

  1. CLIのバージョンアップ
  2. バージョンによって異なる記法の修正(チェックツール使用)
  3. 新プロジェクトの作成
  4. package.jsonの移行
  5. serviceの生成&ソース移行
  6. 画面、コンポーネントの生成&ソース移行
  7. スタイルシートの移行(必要に応じて)
  8. manifest.jsonとserviceworkerの移行
  9. 各種設定ファイルの移行

基本的には移行ガイドの以下の記述の通り、v5の新しいプロジェクトを作成して、各種コードをコピペしていく感じになります。

多くの場合、Ionic CLIを使用して新しいオブジェクトを生成してからコードをコピーするとうまくいきます。

新プロジェクトを作成せずに、古いバージョンのプロジェクトのコードを直接書き換えてバージョンアップしようとするのは茨の道2なのでやめておいたほうがいいです。

各手順の説明

1. CLIのバージョンアップ

ionicのCLIはv4からパッケージ名が変更されています。古いパッケージをアンインストールして新しいパッケージをインストールします。

npm -g uninstall ionic
npm -g install @ionic/cli

2. バージョンによって異なる記法の修正(チェックツール使用)

v3->v4のバージョンアップにおける記法の差異をチェックしてくれる@ionic/v4-migration-tslintというチェックツールを使用して既存プロジェクト(以下、旧環境)のソースを修正していきます。チェックツールから出力されるログの通りに直していけば、v5用のソースがほぼ完成します。チェックツールの使い方はREADMEを読めばわかると思いますので省略します。

3. 新プロジェクトの作成

1でインストールした新しいIonic CLIを使って、新しいIonicプロジェクト(以下、新環境)を立ち上げます。テンプレートは特に理由がなければblankで良いでしょう。

ionic start XXX(プロジェクト名) blank
cd XXX

念のため、プロジェクトが作成できたら起動できるかどうかも確認しておきましょう。

# 起動確認用のコマンド
ionic serve

4. package.jsonの移行

旧環境にインストールされていたパッケージを移行するためにpackage.jsonの中身を移行します。新環境には新しいpackage.jsonが作成されているので、旧環境と新環境のpackage.jsonで差分を取り3、IonicやAngularに関係ないパッケージの記述のみをコピペして新環境のpackage.jsonへ手作業マージしました。

package.jsonのマージが完了したら、npm installでパッケージをインストールしておきましょう。

5. serviceの生成&ソース移行

ここからプロダクトコードを移行していきます。ここからの作業は、ionic serveデバッグ用サーバーを起動しておきましょう。ビルドができなくなったときに、早めに検知することができます。

まず、他ソースとの関わりが大きいserviceを移行します。serviceの中身についてはv3とv5でほとんど変更はありません。が、生成される場所の変更があるのでionic generateで生成→ソースの中身をまるごとコピペの手順で移行しましょう。

ionic g service XXX(サービス名)

6. 画面、コンポーネントの生成&ソース移行

1画面ごとに画面やコンポーネントを移行していきます。serviceと同様に、ionic generateで新しいコンポーネントを生成、中身をコピペの手順です。

pageかcomponentか?

Ionic v3では画面ごとにionic generate pageしていましたが、v5ではpagecomponentどちらを生成すべきでしょうか?状況にも依りますが、基本的にはcomponentで良さそうです。(この違いは別記事にまとめる予定です)

ionic g component XXX(ページ名、コンポーネント名)

ルーティングの設定

Ionic v3ではNavControllerによるページ遷移を行っていましたが、Ionic v5ではAngularのルーティング機能を使ったページ遷移に統合されました。公式ではrouterLinkを使ったページ遷移が紹介されていますが、今回はIonicのページ遷移4をそのまま残すためにTypeScript側で対応しました。

v3->v5の移行例

例えばv3で以下のようにNavController.push()でページ遷移していたとします。

(旧環境)homepage_v3.ts
import { DetailPage } from '../detail/detail';

export class Homepage {
  constructor() {
    nav: NavController,
  }
  ...
  showDetailPage() {
    this.nav.push(DetailPage);
  }
}

新環境では、以下のようにNavController.navigateForward()を利用して移行しました。

(新環境)app-routing.module.ts
import { RouterModule } from '@angular/router';

@NgModule({
  imports: [
  ...
  RouterModule.forRoot([
    { path: '', component: HomePage },
    { path: 'detail', component: DetailComponent }, // <- 追加
  ])
  ],
})
(新環境)homepage_v5.ts
export class Homepage {
  constructor() {
    nav: NavController,
  }
  ...
  showDetailPage() {
    this.nav.navigateForward('/home/search');
  }
}

一部のソースは修正が必要

手順2のチェックツールで大部分のv3用の古い記法は修正されていますが、一部チェックされていない設定があるようです5。私の場合、ビルドエラーにはならないのですが、画面の表示が変わってしまいました。該当の箇所を探して、一括変換で対応しました。覚えている範囲で対応内容を記載します。

内容 旧環境の記述例 新環境の修正例
expand属性に移行すべき属性 <button block> <ion-button expand="block"> 6
slot属性に移行すべき属性 <button item-end> <ion-buttons slot="end">

「戻る」ボタンを明記する

Ionic v4から、<ion-header>内の戻るボタンはデフォルトで表示されなくなりました。(チェックツールでWarningが出ます)以下のように、<ion-back-button>を明示的に追加する必要があります。

(旧環境)page_v3.html
    <ion-navbar>
        <ion-title>タイトル</ion-title>
    </ion-navbar>
(新環境)page_v5.html
    <ion-toolbar>
      <ion-buttons slot="start">
        <ion-back-button icon="md-arrow-back" defaultHref="/home/"></ion-back-button>
      </ion-buttons>
      <ion-title class="ion-text-center">タイトル</ion-title>
    </ion-toolbar>

[追記]ion-checkboxのionChangeで取得できるeventの変更

以下のように<ion-checkbox>(ionChange)を使ってチェックボックス操作時の処理を書くことがありますが、

homepage.html
<ion-checkbox (ionChange)="onChangeFunc($event)">

チェックボックスのチェック状態を取得するために$eventを引数に渡すことができます。このときにJavaScript側で取得できるeventの構造が変わっているようなのでそれを修正。

(旧環境)homepage_v3.ts
onChangeFunc(event) {
  if(event.checked) {
    ...
  }
(新環境)homepage_v5.ts
onChangeFunc(event) {
  if(event.detail.checked) {  // 'detail'が間に挟まっている
    ...
  }

7. スタイルシートの移行(必要に応じて)

私の持っていたIonic v3プロジェクトでは、Ionicがビルド時に自動展開するHTMLやclassをセレクタにしたCSSを適用するという黒魔術的なことをやっていました7。これらの展開されるHTMLやclassの仕様はv5ではまるっきり変わっており、新環境のCSSが全く当たりませんでした。SCSSの書き方を直すことで大部分は対応できるかと思います。こればっかりはセオリーがないのでKIAIでなんとかしてください

【追記】 8. manifest.jsonとserviceworkerの移行

ホーム画面アイコンやキャッシュのなどの設定を manifest.jsonserviceworkerで行っている場合は、Angularの機能で生成し直すか、ファイルをコピー+設定ファイルの書き換えで対応する必要があります。

8−1. 生成し直す場合

ng add コマンドでPWA用のモジュールを追加するとmanifest.jsonserviceworkerに相当するファイルを生成してくれます。生成されたファイルに対して、必要に応じて旧環境の設定を移行してください。

ng add @angular/pwa

8-2. 元のファイルをコピーする場合

Ionic V3のときと同様にindex.htmlでのファイルロードの設定を追記するのと、Angular.json内のビルド設定の追記が必要です。

index.html
<head>
...
  <!-- manifest.jsonのロード -->
  <link rel="manifest" href="manifest.json">

  <!-- ServiceWorkerの登録 -->
  <script>
  if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js');
  }
  </script>
...
</head>
angular.json
...
  "options": {
  ...
    assets: [
      {
        "glob": "**/*",
        "input": "src/assets",
        "output": "assets"
      },
      {
        "glob": "**/*.svg",
        "input": "node_modules/ionicons/dist/sv
        "output": "./svg"
      },
      "src/manifest.json"    // <- 追記
    ],
// 同様の修正を2箇所行う("options"の親が"build"と"test"と2箇所ある)

9. 各種設定ファイルの移行

ここまでで、プロダクトコードの移行は終わっています。Karmaやtslintなどの設定ファイルを移行して、元の開発環境を復旧させましょう。これで終わりです。お疲れさまでした。

所感

手作業コピペが多く、大掛かりな作業だった感は否めません。その代わり、移行後はAngularの機能を存分に使うことができるので、機能の実装が楽になりました。Ionic v3は独自仕様が多い&情報も少ない&おそらくもうアップデートされないという状況ですので、移行できてない方は早めの移行を検討しましょう。


  1. v4とv5の差異はv3とv4間の差異に比べて大きくないのでv4へのアップデートの方も参考になるかと思います。とはいえ今からv4にするくらいならv5にしてしまったほうが良いと思いますが… 

  2. 最初、「プロジェクト作り直しとかありえんやろ!そのままアップデートしたる!」と意気込んでやろうとしましたが、見事に撃沈しました。 

  3. JSONの差分はJSON Diffというサイトを利用しました。 

  4. popToRootとか、pushとかの動作 

  5. チェックツールの漏れなのか、v4とv5の違いなのか、ちょっとよくわかりません。 

  6. <button><ion-button>はチェックツールでチェックされました。 

  7. なんでそんな黒魔術使ってるのって言われましても、ソースを引き継いだタイミングでそうなっていたんでどうしようもなかったんです。。。