Madogiwa Blog

主に技術系の学習メモに使っていきます。

個人のWebサービスをVue2からVue3系へのアップグレードしてみたのでMEMO

個人のWebサービスをVue2系からVue3系にアップグレードしてみたので、やったこととかメモしておきます📝

基本的な流れとしては以下の記事を参考にさせていただきました🙇‍♂️

zenn.dev

事前準備

plugin:vue/vue3-recommendedを有効化する

まずはeslint-plugin-vuevue3-recommendedを有効化して静的解析を行うとVue3で非推奨になったオプション等に警告を出してくれるので、以下の通り有効化して対応を行いました👮

  "extends": [
    "eslint:recommended",
    "plugin:vue/vue3-recommended",

Vue本体のアップデート

ライブラリの更新・不要なライブラリの削除

以下のような形で関連ライブラリの更新を行いました。

yarn add vue-jest@next @vue/test-utils@next @vue/compiler-sfc vue-jest@next --dev
yarn add vue@next

またVueの更新により不要となった以下のライブラリはpackage.jsonから削除しました。

  • vue-template-compiler
  • @vue/composition-api

型定義の更新

参考記事の通り以下のようにVueの型定義を更新しました。

declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

createAppを使ってVueインスタンスを作成

以下のような形でnew VueではなくcreateAppを使ったVueインスタンスの生成方法に変更しました。

// before
import Vue from "vue";
import IndexBoardContainer from "@js/components/containers/IndexBoardContainer.vue";

new Vue({
  el: "#vue-root",
  components: { IndexBoardContainer },
});

// After
import { createApp } from "vue";
import IndexBoardContainer from "@js/components/containers/IndexBoardContainer.vue";

const app = createApp(IndexBoardContainer);
app.mount("#vue-root");

Componentの書き換え

基本的には記事通りで以下のようにコンポーネントを書き換えていきます。

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

export default defineComponent({})
</script>

Composion APIに書き換えなくてもOptionsAPIの場合でも以下のように書き換えることで使用出来るようになりました。

// before
import Vue from "vue";
export default Vue.extend({})

// after
import { defineComponent } from "vue";
export default defineComponent({})

関連ライブラリまわりの対応

Webpack関係

vue-loader

requireの処理を以下のように修正しました。

const { VueLoaderPlugin } = require("vue-loader");

qiita.com

設定の修正

aliasの削除

以下のaliasを削除、多分buildしたものを参照しなくても良くなっている。

    alias: {
      vue: "vue/dist/vue.esm.js",
2021/07/18 追記 互換性を維持するには消しちゃだめだった

完全ビルドが必要な場合はaliasを削除するのではなく以下に書き換える必要あり

    alias: {
      vue: "vue/dist/vue.esm-bundler.js",

blog.capilano-fw.com

警告You are running the esm-bundler build of Vueの抑制

以下の警告が発生するようになったので

VM373 runtime-core.esm-bundler.js:4615 You are running the esm-bundler build of Vue. It is recommended to configure your bundler to explicitly replace feature flag globals with boolean literals to get proper tree-shaking in the final bundle. See http://link.vuejs.org/feature-flags for more details.

以下を参考にしてDefinePluginを使って値を設定するようにしました。

  plugins: [
    new webpack.DefinePlugin({
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false,
    }),

stackoverflow.com

設定値の詳細はこちら

https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags

vue-fontawesome

Vue3にアップグレード後に以下の警告が出るようになりました。

VM2681 runtime-core.esm-bundler.js:150 [Vue warn]: Failed to resolve component: font-awesome-icon
  at <FeedCard feed=

公式ガイドにしたがって@prereleaseを使うようにしました

github.com

vue-infinite-loading

I've published an pre-alpha version vue-infinite-loading@3.0.0-alpha.0-0 base on the PR branch, as the temporary way for Vue.js 3.0 projects.

vue-infinite-loading@3.0.0-alpha.0-0が公開されているとのことで、以下のコマンドでinstall。一旦動いてそうな感じだったので、こちらをそのまま使うことにしました。

yarn add vue-infinite-loading@3.0.0-alpha.0-0

github.com

ハマった事象と対応したこと

なぜかcreateAppにComponentを指定してもレンダリングされない。

// エラーにらなずにレンダリングが走らない。
// const app = createApp({
//   components: { NewBoardContainer },
// });

// こっちだと行ける
const app = createApp(NewBoardContainer);

// 以下のFontAwesomeIconは適用されているのでRootComponentを空にしてるとだめっぽい?
app.component("FontAwesomeIcon", FontAwesomeIcon);
app.mount("#vue-root");

とりあえずentryからレンダリングするcomponentは全部RootComponentにするようにmountまわりを調整して対応しました。。。

2021/07/18 追記

「2021/07/18 追記 互換性を維持するには消しちゃだめだった」に記載した件が原因、ランタイムビルドがデフォルトで読み込まれてしまうので、完全ビルドをimportする必要があった。

参考:vue/dist/vue.esm.js って何~【とりあえず動くからいいや】からの卒業~ - Qiita

RootComponentにhtmlからpropsが渡せない。

const app = createApp(ShowFeedContainer);
app.component("FontAwesomeIcon", FontAwesomeIcon);
app.mount("#vue-root");
<div id="vue-root">
  <!-- component側だとfeedIdがundifinedになる -->
  <show-feed-container :feed-id="1">
</div>

解決できなかったので、ComponentのcreatedでPropsで取得していた値をAPI経由で渡すようにして対応しました。。。

2021/07/18 追記

おそらく「2021/07/18 追記 互換性を維持するには消しちゃだめだった」に記載した件が原因、ランタイムビルドがデフォルトで読み込まれてしまうので、完全ビルドをimportする必要があった。

参考:vue/dist/vue.esm.js って何~【とりあえず動くからいいや】からの卒業~ - Qiita

TypeError: babelJest.getCacheKey is not a functionが発生してjest実行時に落ちる。

https://twitter.com/TheJaredWilcurt/status/1406812612600422400

babel-jestjestのバージョンをあわせたら治りました。

  "babel-jest": "^26.6.3",
  "jest": "^26.6.3",

TypeError: moment_1.default is not a functionが発生してjest実行時に落ちる

    TypeError: moment_1.default is not a function

      101 |     },
      102 |     lastPublishedAtText: function (): string {
    > 103 |       return moment(this.feed.lastEntry.publishedAt).format("YYYY/MM/DD h:mm:ss");
          |              ^
      104 |     },
      105 |     lastPublishedAtFromNow: function (): string {
      106 |       return moment(this.feed.lastEntry.publishedAt).fromNow();

Component側でimport * as moment from "moment"するようにしたら治りました。

https://github.com/aurelia/skeleton-navigation/issues/606#issuecomment-232802977

まとめ

Vue2からVue3は結構破壊的な変更が多そうなのと、まだ対応できていない関連ライブラリ等も多そうな印象に見えたのので、まだハマりどころが多そうな印象ですが、参考になりましたら幸いです…!