2020-03-17

Nuxt2系へのtypescript及びcomposition-apiの導入

1010real / okamotti

こんにちは。1010realです。 2020/01に株式会社スマートショッピングに入社しました。新参者です。

これまではReactをメインで書いていて、Vuejsを本格的に触り始めてまだ3ヶ月目です。 まだまだ勉強が必要なので、みなさまご教授ください。

Vue3.0はいつリリースされるのか

Vuejsを使っているフロントエンドエンジニアが今一番知りたいテーマはこれだと思います。 この質問に対して、つい先日、githubにVuejsのロードマップが公開されています。 https://github.com/vuejs/vue/projects/6

これを見ると、現時点では以下の様なスケジュールでプロジェクトが進んでいる様です。 - beta版リリース: End of 2020 Q1 - オフィシャルリリース: 2020 Q2

また、Vuejs作者のEvanはこうも言っています。 「このスケジュールは決定事項ではない。私達は優先するのは、デッドラインではなく、本番運用に耐えうる高いクオリティのコードを提供すること」 全面的に同意ですね。楽しみにその時を待ちましょう。

待ちきれない人は、以下のリポジトリを利用して、新しい機能を触ってみるのも良いと思います。 https://github.com/vuejs/vue-next (2020/03/15現在でのバージョンは「3.0.0-alpha.8」なので、betaまであと一歩というところでしょうか?)

Vue3.0の概要

超ざっくりですが、Vue3.0では以下の様な機能が追加されるとのことです

  • Composition API 部品を別で定義・インポートしsetupで組み合わせるタイプのAPI。機能の分離がしやすく、TypeScriptとも仲良し
  • Fragment コンポーネントは単一のDOMを返さなければならないという制約により、不要な要素が追加される問題を解決してくれるやつ
  • Suspense 非同期処理を待ってくれる。(composition-api的には、setupが実行完了するのを待つことができるぽい?)

中でもcomposition-apiは、packageをinstallすればNuxt2系でも利用できますし、移行を踏まえて今から素振りをしたい方も多いのではないでしょうか? かく言う私も社内の管理画面開発において、composition-apiを導入し、現在素振り真っ最中ですので、振り返りも含めて環境設定手順をまとめてみました。

導入手順

1.NuxtのバージョンアップとTypeScript導入

composition-api導入にあたり、まず最初にやったことはTypeScriptの導入です。 Vue2系では、TypeScriptを使用するのはまだまだ難しいと言う声をよく聞いていましたが、その要因の一つがOption Apiのthisに依存した設計です。 composition-apiを導入する一番のメリットはこのthisからの脱却。そしてTypeScriptがより真価を発揮できる様になることだと思います。

さて、そのtypescript導入ですが、nuxtのtypescriptパッケージ(@nuxt/typescript-build)はNuxt2.10またはそれ以上のバージョンで使用されることを想定しています。 なのでまず、nuxtのupgradeを行った後に、typescriptをインストールします。

$ yarn upgrade nuxt
$ yarn add --dev @nuxt/typescript-build

nuxtを2.11.0(3/15現在)に上げ、typescriptをインストールしました。

ちなみに、nuxt2.8以前で既に@nuxt/typescriptを利用していた方は以下の手順で移行すると良いかと思います。 https://typescript.nuxtjs.org/ja/migration.html

次にnuxt.config.jsでbuildModulesに'@nuxt/typescript-build’を追加します。 また、nuxtの元バージョンが低い場合は、buildModulesではなく、devModulesとなっているかと思いますが、buildModulesに変更してしまいましょう。(devModulesはVue3.0から廃止されるようです)

nuxt.config.js javascript export default { buildModules: ['@nuxt/typescript-build'] }

次にプロジェクトルートにtsconfig.jsonを追加します。 tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "checkJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "typeRoots": [
      "types",
      "node_modules/@types"
    ],
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

js->tsへの移行を行う場合には、allowJs、checkJsオプションをtrueにします。 これによりtsファイル内でjsファイルをimportすると言ったことが可能になります。

次に.vueファイルをtypescriptに認識させるために以下のファイルを作成します。 srcフォルダ内にあれば良いので、とりあえずtypesフォルダ配下に置いています。

types/vue-shim.d.ts

declare module "*.vue" {
  import Vue from 'vue'
  export default Vue
}

2.Lint設定

次にtypescriptのlintの設定もしてしまいます。パッケージをインストールします。

// yarn remove @nuxtjs/eslint-config // もしあれば
yarn add -D @nuxtjs/eslint-config-typescript
yarn add -D @typescript-eslint/eslint-plugin

ちなみに@nuxtjs/eslint-config-typescriptには、@nuxtjs/eslint-configも含まれているとのことなので、既に利用していた場合はremoveしてから追加します。

次に.eslintrcを新規作成 or 編集し、以下を設定します。

.eslintrc

...
  parserOptions: {
    parser: '@typescript-eslint/parser' // ←parserに'@typescript-eslint/parser’を指定します
  },
  extends: [
...
    '@nuxtjs/eslint-config-typescript' // ←追加します
  ],
  plugins: [
...
    '@typescript-eslint' // ←typescriptにまつわるeslint設定を追加する場合は必要です
  ],
  // add your custom rules here
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  }

次におすすめのlint設定ですが、基本的にはrecommendされているものをそのまま利用するのが良いと思います。 以下設定でおすすめのlint設定を全て読み込みます。

.eslintrc

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ]
}

ちなみに、plugin:@typescript-eslint/eslint-recommended で読み込まれるルールはこちらの「Recommend」にチェックがついているものです。

最後にpackage.jsonのlintスクリプトが.tsファイルも対象とする様に変更します。

package.json

"lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .”, // .ts を追加

これでlint設定が終わりましたので、試しにlintしてみます。 これまでの設定から大きく変更している場合は、物凄い量のwarning, errorが出てくると思います。

screenshot_linterror

これまで動いていたコードなので、まずはそのままlintを通したいですが、ゆくゆくは直していきたいです。 そのため、errorが出ているruleをまずは'warning'レベルに落としてしまいます。 コンソールのerror内容の右に、表示されている適用されたルールの設定を全て'warn'に変更します。

.eslintrc

  // add your custom rules here
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'space-before-function-paren': 'warn', // ←追加
    '@typescript-eslint/no-unused-vars': 'warn', // ←追加
  }

これで、errorにはならないので、ビルドは通りますが、warnとして表示される様になりました。 徐々にリファクタしていき、一通り対応が終わったruleから、この指定を消していくと、移行がしやすいと思います。(もちろん必要ないと感じたruleは設定を'off'にしても良いと思います)

これでやっとまともにtypescriptを扱える様になりました。

3.composition-api導入

さて、いよいよcomposition-apiの導入ですがここまできたら簡単です。 まずpackageをインストールします。

yarn add @vue/composition-api

Composition-api用のpluginを用意します

plugins/composition-api.ts

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'

Vue.use(VueCompositionApi)

pluginを有効化します。 nuxt.config.js

...
plugins: [
...
  '@/plugins/composition-api’ // ←追加
],

これでcomposition-apiを試す環境ができたと思います。 試しに影響範囲の少ない、小ぶりなコンポーネントからcomposition-api化してみてはいかがでしょうか?

4.composition-apiで既存Componentを書き換えてみる

以下に、propsで受け取ったメールアドレスを表示して、プルダウン内のログアウトを押したらページ遷移する簡単なコンポーネントをcomposition-api化したものを載せておきます。(UI-componentはbuefyを使用しています)

template(共通) html <template lang="pug"> .navbar-item.has-dropdown.is-hoverable.set-hover-color a.navbar-link {{ userEmail }} .navbar-dropdown.is-right a.navbar-item( @click="route('/signout')" ) strong ログアウト </template>

before html <script> export default { props: { userEmail: { type: String, required: true } }, methods: { route(link) { this.$router.push(link) } } } </script>

after ```html import { defineComponent, SetupContext } from '@vue/composition-api'

interface Props { userEmail: string }

export default defineComponent({ props: { userEmail: { type: String, required: true } }, setup(props: Props, context: SetupContext) { const route = (link: string): void => { context.root.$router.push(link) } return { userEmail: props.userEmail, route } } }) </script> ```

scriptタグのlang属性に"ts"を設定することでtypescriptでの記述が可能です。 基本的には、templateは変更する必要なく、script部分だけ変更していくことが可能だと思います。

最後に

この規模のComponentだとあまりComposition-Apiの良さを感じることはできないと思いますが、肥大化したComponentをリファクタすると個別の関数として定義できることの恩恵を感じることができるのかな。。というのが現在の印象です。この辺の知見は今後溜めていきたいです。 とはいえ、導入したからといって、以前の書き方ができなくなることはないので、試してみてはいかがでしょうか?

PR1

まだまだ考えることが多いマイクロサービス開発を実施しているスマートショッピングでは、スマートマットを支えるエンジニア各職を募集しています!

PR2

スマートショッピングではスマートマットを通して在庫管理・自動発注を支援しています!

201912224 s5

最新の記事