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

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

## 対象読者
Ionic v3のプロジェクトをv5またはv4[^1]へアップデートすることを検討されている方

## なぜアップデートしたか？
Ionic v3系のアップデートが止まっており、v3で残っている[バグ](https://forum.ionicframework.com/t/build-in-prod-mode-cannot-find-module/132231/6)の影響が無視できなくなったためです。

## 大まかな手順
v3->v4バージョンアップ方法の概要については、Ionic公式に[v4への移行ガイド](https://ionicframework.com/jp/docs/building/migration)というページがあるので、これをベースに進めていきます。ざっと、以下の手順で進めていきました。

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からパッケージ名が変更されています。古いパッケージをアンインストールして新しいパッケージをインストールします。

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

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

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

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

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

``` bash
# 起動確認用のコマンド
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`で生成→ソースの中身をまるごとコピペの手順で移行しましょう。

``` bash
ionic g service XXX(サービス名)
```

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

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

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

#### ルーティングの設定
Ionic v3では`NavController`によるページ遷移を行っていましたが、Ionic v5ではAngularのルーティング機能を使ったページ遷移に統合されました。公式では[routerLinkを使ったページ遷移](https://ionicframework.com/jp/docs/angular/navigation)が紹介されていますが、今回は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.json`と`serviceworker`で行っている場合は、Angularの機能で生成し直すか、ファイルをコピー＋設定ファイルの書き換えで対応する必要があります。

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

``` bash
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](http://www.jsondiff.com)というサイトを利用しました。
[^4]: popToRootとか、pushとかの動作
[^5]: チェックツールの漏れなのか、v4とv5の違いなのか、ちょっとよくわかりません。
[^6]: `<button>`→`<ion-button>`はチェックツールでチェックされました。
[^7]: なんでそんな黒魔術使ってるのって言われましても、ソースを引き継いだタイミングでそうなっていたんでどうしようもなかったんです。。。