Nuxt.js 2.4 に更新するためにやったこと


## Nuxt.js 2.4 で変わったこと

公式リリースノートより一部抜粋していきます。
(※スクリーンショットはそれぞれ公式より引用させていただいております。)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/32b322ef-70b7-da10-00ea-ac2444b568ad.png)
ref: https://github.com/nuxt/nuxt.js/releases/tag/v2.4.0

### TypeScriptに対応

- やったね！！ 公式に TypeScript サポートが開始されました
- 内部的には `ts-loader` と `fork-ts-checker-webpack-plugin` が使用されています
- :warning: Nuxt.js 内部の Webpack は js から優先的に依存を解決しようとするので同名の js と ts が存在する場合は注意
    - 必要な場合は module か config で extendBuild の仕組みを使用して Webpack に設定されている定義順を入れ替えましょう

![image](https://user-images.githubusercontent.com/904724/51127667-51b2b180-1826-11e9-9e8f-07bb39d09d3a.gif)

### prefetch に対応

ref: https://nuxtjs.org/api/components-nuxt-link

- nuxt-link を使用している場合、dev.to みたいにページの先読みがされるようになりました。
- 実装は google/quicklink.js を参考にしたそうです。
- 意図的に prefetch を無効化するときは nuxt-link に `no-prefetch` を設定しましょう。

![image](https://user-images.githubusercontent.com/904724/51692960-4158be80-1ffe-11e9-9299-61881d06412e.gif)

- プリフェッチなし (v2.3): https://nuxt-no-prefetch.surge.sh
- プリフェッチあり (v2.4): https://nuxt-prefetch.surge.sh

### Store が HMR に対応

- Hot Module Replacement に Vuex が対応し、ストアのコードをいじっても初期化されなくなりました

![image](https://user-images.githubusercontent.com/904724/51385153-b3bd3080-1b1e-11e9-891f-c136a3182ca0.gif)

### Vetur サポートの強化

- VSCode で Vetur を使用している場合、Nuxt オリジナルの関数やメンバーが補完されるようになりました

![image](https://user-images.githubusercontent.com/904724/51854163-70d33800-232a-11e9-98b0-f5e7406996cd.gif)

### 生成されるBundleの省サイズ化

- bundle が小さくなることはいいこと！

| モード | v2.4以前             | v2.4以降              | サイズ差                     |
| ------ | -------------------- | --------------------- | ---------------------------- |
| Dist   | 8.2M                 | 7.2M                  | -1M / 12%                    |
| Dev    | 281 MB (RSS: 439 MB) | 237 MB (RSS: 354 MB)  | -44M / 16% (RSS: -85M / 19%) |
| Start  | 106 MB (RSS: 169 MB) | 71.7 MB (RSS: 137 MB) | -34M / 32% (RSS: -32M / 19%) |

----

### その他にもいろいろ！

- nuxt-link が n-link で使えるようになりました
- 内部で使用される Webpack のバージョンが新しくなりました
- プラグインの読み込み時に拡張子ベースで Server Only / Client Only を設定できるようになりました
- 開発モードでデフォルトのポートが使用中の場合ランダムなポートを変わりに使用するようになりました

この他にもたくさん機能追加、改修がされています

ref: https://github.com/nuxt/nuxt.js/releases/tag/v2.4.0

----

## アップグレードまでの道

担当しているプロジェクトで Nuxt.js v2.4 にするに当たり実施した改修などなどをご紹介します。

### 前提知識

プロジェクトの構成は下記のようになっています。

- 使用しているパッケージ
  - TypeScript 3.1.x
  - Nuxt.js v2.3.x
  - Express 4.16.x
  - Vuetify 1.4.x
- nuxt.config を含む全てのスクリプトを TypeScript で記載

### 普段どおりのアップデート方法

パッケージ更新を普段の運用に組み込んでいて、下記のように実施しています。
基本的に package-lock.json を一度破棄して、最初から生成し直し依存パッケージをすべて更新しています。

```bash
# ロックを解除
rm -rf node_modules package-lock.json

# パッケージを更新できるものはすべて更新
ncu -a

# 型定義ファイルの追加を検知
typesync

# オリャー！！
npm install
```

### Nuxt.js 2.4 にして起こった問題

普段であれば、上記の手順であとは動作チェックと細かい追従対応を行い対応を完了していますが、今回は少し大きめに変更を加える必要がありました。

- 今まで通っていたビルドが大量にimportエラーを吐いてコケた
- 今まで問題のなかった箇所でSSRでDOMの不整合が発生するようになった
- .nuxt/router.js 内部での Unexpected token error

#### エラーの例

> Nuxt.js が出力するファイルで構文エラーが起こった例
> ![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/ee210140-a4d2-0135-cab3-b68cb108548d.png)

> 今まで使用できていた画像を参照できなくなった例
> ![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/2ac9bedb-9051-1de2-21fd-ec13ec8e21ba.png)

## 大量に起こったimportエラーについて

### 原因

- 正体は単一ファイルコンポーネント(SFC)の style ブロック内部の`url('~/assets/hoge.svg')`
- 画像の読み込みが v2.3 までと微妙に異なる様子...

### 解決策

- **【解決策】CSS 内部では `~assets` を使いましょう**
- 英語版マニュアルに下記のように記載があります
    - https://nuxtjs.org/guide/assets#webpack
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/b2350207-e478-0c99-e092-45186e6ac043.png)
- 日本語版マニュアルにもちょっと控えめに書いてありました
    - > css-loader のアップグレードにより、Nuxt v2.0 から CSS のデータ型 では、~assets（スラッシュなし）を使わなければなりません。例：background: url("~assets/banner.svg") 

## SSRのDOMの不整合

### 原因

- 正直良くわかりませんでした。。。
- SSR の差分検知のロジックに変化があったか、そもそもいままでの書き方が悪かった可能性が高い
- `vue-server-renderer` の更新で直るかもしれない(？)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/d9cd45b6-b374-dcd9-e545-660ee8edf876.png)

### 解決策

- **【解決策】必要に応じて`<no-ssr>`を使いましょう**
- DOMの不整合が起きている箇所のうち、差分検知ができている場合は、開発モードの場合のみヒントになる情報が検証ツールで出ています。
- CSSのセレクタやDOMの構造を頼りに問題箇所を特定して根本的に直すか、工数的に難しい場合などは`<no-ssr>`を活用しましょう。

## .nuxt/router.js の Unexpected token error

### 原因

これが一番困った...
下記のIssueをにたどり着きどうやら npm のバグによるものらしく、パッケージの依存解決に失敗する場合がある様子。

- https://github.com/nuxt/nuxt.js/issues/4839
- https://github.com/webpack/webpack/issues/8656
- https://github.com/npm/cli/pull/147

流れとしては、下記のような感じ

- Webpack v4.29 が依存している acorn というパッケージが更新された
- Nuxt.js v2.4 は Webpack v4.29 をに依存している
- npm のバグにより Webpack と acorn の依存をうまく解決できないのが問題らしいとのこと

この問題は Issue のリストに上げたように Nuxt.js 側でも報告されていて、`yarn` でパッケージを取得している場合は発症しないことがわかっていました。

### 解決策

- **【解決策】acorn 6.0.5 以上をインストールする**

公式の案内は下記のように `acorn` を更新し、 `npm dedupe` すると良いと案内がありましたが、こちらでは解決しませんでした。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/750c148d-bf97-516e-9ff1-013bb826d212.png)

```bash
rm -rf node_modules package-lock.json
npm install
npm update acorn --depth 20
npm dedupe
```

npm install している途中で、peerDependencies が欠けている警告が出ていたので試しに acorn を明示的にインストールすると問題は解決できました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/27fde94e-1b37-fe07-8061-6c9d881aa9e4.png)

解決したあとで確認すると、同様の解決手法が Nuxt.js の Issue に追記されていました。

ref: https://github.com/nuxt/nuxt.js/issues/4839#issuecomment-458720610

----

## その他対応したところ

### PostCSS の設定が配列からオブジェクトが推奨に変更

v2.0 以降で対応の必要があります。

nuxt.config の build.postcss の設定が配列からオブジェクトに変更されており、オブジェクトで宣言するとデフォルトの設定といい感じにマージしてくれます。
一方で配列で定義すると Nuxt.js で設定されている cssnano などの仕組みが上書きされて消えてしまうので注意が必要です。

日本語版マニュアルは配列で設定する方法が乗っているのですが英語版ですと更新されているようでした

- 日本語版: https://ja.nuxtjs.org/faq/postcss-plugins/
- 英語版: https://nuxtjs.org/faq/postcss-plugins/

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35603/1022daad-b16a-7627-0f16-79992c101dac.png)

### TypeScript の対応方法の追従

module/typescript.js などを用意して、旧来の手法でTypeScriptを使用していた場合は Nuxt.js が持っているTypeScript対応の仕組みと重複するので、必要に応じて削除や調整が必要です。

----

## まとめ

<font size="6pt">:tada: 「ねんがんの Nuxt.js v2.4 に なったぞ！ ▼」</font>

- ハマりポイントとしては以上のような依存関係のものが大きかったです。
- 対応策を探す上で、マニュアルやリリースノートを確認することが重要になってきますが、日本語翻訳に頼り切るのではなく、英語のIssueやPRなどの1次情報も目を通していくお気持ちが大切でした。