Vue3:グローバルコンポーネントの登録方法

はじめに

こんにちは!最近は業務で調査ばかりやっているなーがです。前回はChromeのプロファイルを使って快適な開発を行おうという内容でしたが、今回はVue3のグローバルコンポーネントの登録方法について書こうと思います。解決方法をすぐに知りたい方はこちらから確認できます。

以前、業務でVue2からVue3へのバージョンアップ対応を行ったのですが、その際に発生したWarningではまってしまったので復習と個人的な備忘録も兼ねて記事にしたいと思います。

アプリケーションの構成

今回対応したアプリケーションでは、コンポーネントが下記のように2種類のファイルでグローバルコンポーネントとして登録されていました。モジュールバンドラーはWebpackを使用しています。

// フォルダ構成
APP
├── src
│ ├── App.vue
│ ├── main.js
│ ├── components.js
│ ├── globals.js
│ ├── components
│ │ ├── xxxxx.vue
│ │ ├── yyyyy.vue
│ │ ├── global
│ │ │ ├── ABC.vue
│ │ │ ├── DEF.vue
︙︙︙︙
// src/main.js
import globals.js
import App from "./App.vue"
import Vue from 'vue'

var app = new Vue({
  render: h => h(App)
}).$mount('#app')

「globals.js」では「components/global」配下の全ての画面で使用するコンポーネントを登録しています。

// src/components/globals.js
import Vue from "vue"

const files = require.context('./components/global', true, /.vue$/)
const components = {}
files.keys().forEach(key => {
  components[key.match(/.+\/(.+)\.vue/)] = files(key).default
})

// 読み込んだコンポーネントをグローバルコンポーネントとして登録
Object.keys(components).forEach(key => {
  Vue.component(key, components[key])
})

「components.js」では特定の画面にのみ使用する複数のコンポーネントを登録しています。

// components.js
import Vue from 'vue'
import Xxxxx from "xxxxx"
import Yyyyy from "yyyyy"
// ...

Vue.component('XXXXX', Xxxxx)
app.component('YYYYY', Yyyyy)
// ...

Vue3への対応

Vue3はVue2から書き方が大きく変更されています。そのため、移行ガイドを基にアプリケーションの影響調査を行いました。移行ビルトの手順に沿ってアプリケーションを下記のように修正しました。

// src/main.js
import globals.js
import App from "./App.vue"
import { createApp } from 'vue'

var app = createApp(App)
app.mount('#app')
// src/globals.js
import { createApp } from "vue"

const app = createApp({})

const files = require.context('./components/global', true, /.vue$/)
const components = {}
files.keys().forEach(key => {
  components[key.match(/.+\/(.+)\.vue/)] = files(key).default
})

// 読み込んだコンポーネントをグローバルコンポーネントとして登録
Object.keys(components).forEach(key => {
  app.component(key, components[key])
})
// src/components.js
import { createApp } from 'vue'
import Xxxxx from "xxxxx"
import Yyyyy from "yyyyy"
// ...

const app = createApp({})

app.component('XXXXX', Xxxxx)
app.component('YYYYY', Yyyyy)
// ...

ビルドが通ったのでアプリケーションにアクセスしました。しかし、アプリケーションでグローバルコンポーネントとして登録したコンポーネントを使用した箇所が機能しなかったため、開発者ツールを開くと以下のようなWarningが発生していました。

Failed to resolve component: XXXXX
If this is a native custom element, make sure to exclude it from component resolution via 
compilerOptions.isCustomElement. 

Warningでは「コンポーネントXXXXXの解決に失敗しました。 これがネイティブのカスタム要素である場合は、次のようにしてコンポーネント解決から除外してください。」とあったので、公式ドキュメントを参考にして以下を「webpack.config.js」に追加しました。

// webpack 設定
rules: [
  {
    test: /\.vue$/,
    use: 'vue-loader',
    options: {
      compilerOptions: {
        isCustomElement: tag => tag === 'XXXXX'
      }
    }
  }
// ...
]

しかし、Warningは消えずグローバルコンポーネントを使用している箇所は機能しませんでした。そのため、開発者ツールから変数 appを確認したところ、「globals.js」及び「components.js」で登録したはずのコンポーネントが登録されていませんでした。

登録されていなかった原因

移行ガイドの「新しいグローバル API: createApp」に以下のような記載があります。

技術的には、Vue 2 には「アプリ」という概念がありません。私たちがアプリと定義しているのは、単に new Vue() によって作成されたルート Vue インスタンスです。同じ Vue コンストラクターから作成されたすべてのルートインスタンスは、同じグローバル設定を共有します

Vue 3 ではグローバル API である use が利用できなくなったため、このやり方は機能しなくなり、Vue.use() を呼び出すと警告が表示されるようになりました。代わりに、エンドユーザーはアプリのインスタンスでプラグインの使用を明示的に指定する必要があります

つまり、Vue2では読み込まれたファイルのどこかでコンポーネントとして登録されれば、グローバルコンポーネントとして使用することが出来ます。しかし、Vue3からは「app インスタンス」という新しい概念が取り入れられたため、作成したインスタンス(この場合は変数 app)にコンポーネントとして登録する必要があります。

解決方法

こちらの記事を参考に以下のように修正したところ、グローバルコンポーネントが正常に機能しました。

// src/main.js
import Globals from 'globals'
import Components from 'components'
import App from "./App.vue"
import { createApp } from 'vue'

var app = createApp(App)
app.use(Globals)
app.use(Components)
app.mount('#app')
// src/globals.jsconst files = require.context('./components/global', true, /.vue$/) const components = {} files.keys().forEach(key => { components[key.match(/.+\/(.+)\.vue/)] = files(key).default }) // 読み込んだコンポーネントをグローバルコンポーネントとして登録 export default { install(app: App) { Object.keys(components).forEach(key => { app.component(key, components[key]) }) }, }
// src/components.js
import Xxxxx from "xxxxx"
import Yyyyy from "yyyyy"
// ...

export default {
  install(app, Opinon) {
    app.component('XXXXX', Xxxxx)
    app.component('YYYYY', Yyyyy)
    // ...
  },
}

開発者ツールから  を確認したところ、「globals.js」及び「components.js」で登録したコンポーネントがグローバルコンポーネントとして登録されていました。

さいごに

今回はVue3のグローバルコンポーネントの登録方法について書きました。自身のVueに関する知見が乏しかったことが一番の要因ではありますが、公式ドキュメントを読んだだけでは理解できていませんでした。動作確認も実際の環境を修正して行っていたため、今後このような状況に陥った場合は簡単なテストアプリを作成して調査する重要性を感じました。これからも業務中に学んだことをブログにしていきたいと思います。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です