弁護士ドットコム株式会社 Creators’ blog

弁護士ドットコムがエンジニア・デザイナーのサービス開発事例やデザイン活動を発信する公式ブログです。

CloudSign の社内用管理画面を Vue2.7 に移行した話

こんにちは。弁護士ドットコム クラウドサイン事業本部の篠田 (@tttttt_621_s) です。

普段はフロントエンドの開発を担当しています。

Vue.js を使ったプロダクト開発において、今年の課題としてあげられるものは Vue2 のサポート終了による Vue3 への移行ではないでしょうか。

弊社プロダクトの CloudSign や社内用の管理画面(以下 backyard と呼びます)も Vue.js で開発されています。

プロダクトの安定性を担保するためにも段階的に移行していく必要があります。本記事では Backyard を Vue2.7 に移行した取り組みを紹介します。

Backyard の構成

Backyard は Golang のテンプレートエンジンで生成された HTML に Vue をマウントしており、規模としては 40 画面程あります。

以下に主な技術構成を紹介します。

  • vue
  • @vue/composition-api
  • @vue/test-utils
  • jest
  • typescript
  • webpack

調査

毎週火曜日に 1 時間程度、有志のメンバーで集まり Backyard で使用しているライブラリの見直しや Vue2.7 移行に必要な周辺ライブラリの調査をしました。

vue2.7移行に必要な周辺ライブラリの調査を行なっている様子の画像
oVice でのライブラリ調査の様子の画像

vue2.7移行に必要な周辺ライブラリの調査を行なった際のドキュメントの画像
ライブラリ調査のドキュメントの画像

やったこと

Vue2.7 に移行のためにやったことは主に以下 3 つです。 - @vue/composition-api の削除 - createApp の対応 - その他ライブラリの更新

またレビュアの負荷軽減のため Vue.js のアップデート関連以外の関心事は切り出し、小さい粒度で MR(マージリクエスト)を作成しました。

@vue/composition-api の削除

@vue/composition-api は、Vue2 用 Composition API のプラグインです。

Backyard では、約 80 ファイルほどがこのプラグインを使って実装されていました。

Composition API が組み込まれた Vue 2.7 のリリースに伴って、このプラグインは Vue 2.7 以前のバージョンのみをサポートすることになっています(現在はサポート終了)。

そのため、このプラグインをプロジェクトから削除し、Composition API のインポートを Vue 本体から行うように変更しました。

- import { ref } from '@vue/composition-api'
+ import { ref } from 'vue'

createApp の対応

Vue 2.7 では、Vue 3 の機能がバックポートされているため、その恩恵を受けることができます。

しかし、すべての機能がバックポートされているわけではありません。 その中の 1 つに createApp があります。

Backyard では、これまで以下のように createApp を使ってインスタンスを作成していました。

import { createApp } from '@vue/composition-api'
import App from './App.vue'

const app = createApp(App)

よって、createApp を使っているファイルを new Vue に置き換える必要がありました。

すべて new Vue に書き換えるのは多少の手間がかかります。

また今後 Vue 3 に移行した際は再び createApp を使用することになるので、new Vue を内包したモジュールをプロジェクト内に作成しそれを使用することにしました。

import Vue from 'vue'

export const createApp = (params) => {
  return new Vue(params)
}
- import { createApp } from '@vue/composition-api'
+ import { createApp } from '@modules/createApp'
import App from './App.vue'

const app = createApp(App)

eslint-loader から eslint-webpack-plugin へ移行

eslint-loader は webpack の ESlint 用のローダーです。

こちらのリポジトリは、すでにアーカイブされているため非推奨となっています。

よって webpack.config.js を修正し、eslint-webpack-plugin へ移行しました。

+ const ESLintPlugin = require('eslint-webpack-plugin')

module.exports = (env, options) => {
  return {
    module: {
      rules: [
-       {
-         enforce: 'pre',
-         test: /\.(js|vue)$/,
-         exclude: /node_modules/,
-         use: [
-           {
-             loader: 'eslint-loader'
-           }
-         ]
-       },
      ]
    },
    plugins: [
+     }),
+     new ESLintPlugin({
+       extensions: ['.js', '.vue', '.ts']
      })
    ],
  }
}

@vue/test-utils と typescript のアップデート

Backyard のユニットテストは以下のように @vue/test-utils を使用しています。

<template>
  <div>
    <h1 data-test-message>{{ message }}</h1>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const message = ref('Hello')
    
    return {
      message
    }
  }
})
</script>
import App from './App.vue'
import { mount } from '@vue/test-utils'

describe('App.vue', () => {
  test('Hello と表示されていること', () => {
    const wrapper = mount(App)
    expect(wrapper.find('[data-test-message]').text()).toBe('Hello')
  })
})

Vue2.7 にアップデートすると、ビルド時にテストファイルで以下のような型エラーになりました。

TS2321: Excessive stack depth comparing types 'Vue3Instance<Data, Readonly<ExtractPropTypes<{}>>, 
Readonly<ExtractPropTypes<{}>>, 
{}, {}, true, ComponentOptionsBase<any, any, any, ... 6 more ..., any>> & ... 5 more ... &
Readonly<...>' and 'Vue<Record<string, any>, Record<string, any>, never, never, 
(event: string, ...args: any[]) => Vue<Record<string, any>, Record<string, any>, never, never, ...>>'.

こちらに関しては地道に検証し @vue/test-utils と typescript を以下のバージョンまでアップデートすることで解消しました。

  • "@vue/test-utils": "1.3.0",
  • "typescript": "4.3.2",

jest のアップデート

Vue 2.7 移行とは直接的には関係ありませんが、ついでにテスト環境も改善したいと考え、今回 jest のバージョンを 27 にアップデートしました。jest は、バージョン 27 でデフォルトの挙動に変更が入っています。

今回影響があったのは以下で、それぞれ対応をしました。

  • testRunner が jest-jasmine2 から jest-circus に変更
  • testEnvironment が jsdom から node に変更

testRunner が jest-jasmine2 から jest-circus に変更

jest-circus は、done コールバックをサポートしていません。 実際に以下のような記述があるテストは失敗するようになりました。

beforeAll(async (done) => {
  await flushPromises()
  done()
})

引き続き testRunner で jest-jasmine2 を使用するため、以下のように設定を追記しました。

"jest": {
+  "testRunner": "jest-jasmine2"
}

testEnvironment が jsdom から node に変更

testEnvironment の変更により、DOM API を使うテストが失敗するようになりました。 こちらも、引き続き jsdom を使用するため以下のように設定を追記しました。

"jest": {
  "testRunner": "jest-jasmine2"
+ "testEnvironment": "jsdom"
}

おわりに

CloudSign の社内用の管理画面を Vue2.7 に移行した取り組みを紹介させていただきました。

今回紹介しきれなかったことも多々ありますが、Vue2.7 に移行したことでプロダクトの安定性の担保や開発者体験の向上に繋がったと考えています。

今回のような改善活動は有志のメンバーで行われていましたが、この度フロントエンドの改善に特化したチームが新設されました。

Vue2.7 移行は Vue 3 に向けての最初の 1 歩となりました。今後も少しずつ歩んでいきます。

最後まで読んでいただき、ありがとうございました。

クラウドサインのフロントエンドに興味があるという方をお待ちしております。