47
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

既存のVue.jsプラグインがVue 3で使えない場合の対応

Last updated at Posted at 2020-09-18

いくつかのパターンをまとめましたが、簡単な修正だけで直るケースも多いです。
ただし、基本的にはプラグイン側の修正が必要なので、ここではその修正箇所をまとめています。

目次:

ケース1: this.$xxx() 系のプラグインで起きる問題

Vueインスタンスメソッドに機能が拡張されるタイプのプラグインで起きるのは、
プラグインのインストール時、Vueのprototypeの拡張に失敗しているという事象です。

Uncaught TypeError: Object.defineProperty called on non-object
または
Uncaught TypeError: Cannot set property '$awesomeVuePlugin' of undefine

あたりのエラーが出ていないでしょうか?

プラグインがどのように注入されるか

先にVueのプラグインのインストールについて少し説明させてください。

プラグインはVue.use()に渡されますが、ここに渡されるオブジェクトにはinstallというメソッドが実装されている必要があります。

installメソッドの第一引数には、Vueオブジェクトが渡されるので、それに対しdirectiveやmixin、prototypeなどを拡張することができます。

export default {
  install (Vue) {
    // Vueのインスタンスメソッドとしてプラグインの機能を使えるようにする
    Vue.prototype.$awesomeVuePlugin = awesomeVuePlugin
  }
}

Vue 3での変化

Vue 3ではVueオブジェクトは扱わなくなります。
初期化時は代わりにcreateAppというAPIで、アプリインスタンスを生成します。

旧Vue.js
import Vue from 'vue'
Vue.use(プラグイン)
new Vue({ ... })
Vue3
import { createApp } from 'vue'
const app = createApp(...)
app.use(プラグイン) // appインスタンスに対してuseします

そしてuse()したときに、プラグインのinstallメソッドに渡される引数もVueではなくそのアプリインスタンスになります。

このappはprototypeを持ちませんので、アサインしようとして$xxx of undefinedとなってしまいます。

export default {
  install (app) {
    console.log(app.prototype) // -> undefined
    app.prototype.$xxx = ... // -> Error
  }
}

解決策: globalPropertiesを使う

Vue 3では、app.config.globalPropertiesを拡張するのがその代替となります。

before
export default {
  install (Vue) {
    Vue.prototype.$awesomeVuePlugin = awesomeVuePlugin
  }
}
after
export default {
  install (app) {
    app.config.globalProperties.$awesomeVuePlugin = awesomeVuePlugin
  }
}

Composition APIで使う

上記の方法は、Vue3にバージョンだけ上げてOptions APIを使い続ける場合は問題ありませんが、

Composition APIを使う場合、setup内ではthisへアクセスできませんので、this.$awesomeVuePluginといったことはそもそもできません。

なので、以下のようにインポートして使うような仕組みに直す必要があります。

import { useAwesomeVuePlugin } from 'awesomeVuePlugin'

プラグイン側の修正量が多くなるかもしれません。

別の案としては、代わりにprovide / injectを使う方法です。

プラグイン内でprovideする

import { provide } from 'vue'ではなく、appインスタンスが待つprovideを使います。

export default {
  install (app) {
    app.config.globalProperties.$awesomeVuePlugin = awesomeVuePlugin
    app.provide('awesomeVuePlugin', awesomeVuePlugin)
  }
}
import { inject } from 'vue'
setup () {
  const awesomeVuePlugin = inject('awesomeVuePlugin')
}

但し、プラグインで勝手にprovideするのは親切にも、ありがた迷惑にもなりそうです。

自分でprovideさせる仕組みにする、provide名を指定できるようにする、プラグイン固有の名詞など明らかに衝突しない名前にする、といった工夫が必要だと思います。

参考までに、Vuexはprovideなし、Vue routerはrouterという名前で勝手にprovideしてくれていました。

ケース2: <div v-xxx>系のプラグインで起きる問題

次に、ディレクティブが拡張されるタイプのプラグインです。

エラーなどもなくただ機能が動作しない、といった事象が起きていませんか?

Vue 3では、カスタムディレクティブで用いられるメソッドの名前が変わったことが原因と思われます。

解決策: 新しいメソッド名に修正する

Vue.js Vue 3
bind beforeMount
inserted mounted
- beforeUpdate
update -
componentUpdated updated
- beforeUnmount
unbind unmounted

Ref: https://v3.vuejs.org/guide/migration/custom-directives.html#_3-x-syntax
無くなったupdateupdatedを使ってください。

before
app.directive('xxx', {
  inserted (el, binding, vnode) {
    el.style.background = binding.value
  }
  unbind () {}
})
after
app.directive('xxx', {
  mounted (el, binding, vnode) {
    el.style.background = binding.value
  }
  unmounted () {}
})

ケース3: コンポーネント系プラグインで起きる問題

import Slider from 'vue-awesome-slider'
export default {
  components: { Slider }
}

色々な変更が影響している可能性があります。

僕が遭遇したのはライフサイクルフックの名称変更による問題です。
destroyedunmountedに変わっており、コンポーネント破棄時の処理が呼ばれない問題が起きていました。

before
Slider = {
  created () {},
  destroyed () {}
}
after
Slider = {
  created () {},
  unmounted () {}
}

補足1: Vue.jsとVue 3両方に対応させる

アプリインスタンスもVueオブジェクトもversionというプロパティから使われているVueのバージョンを取得可能です。

これを利用して実装を分岐しましょう。

export default {
  install (app) {
    console.log(app.version) // -> '3.0.0-rc.12'

    const isVue3 = app.version.startsWith('3')

    // インスタンスメソッドの拡張
    const prototype = isVue3 ? app.config.globalProperties : app.prototype
    prototype.$awesomeVuePlugin = awesomeVuePlugin

    // ディレクティブの拡張
    app.directive('xxx', {
      [isVue3 ? 'mounted' : 'inserted'] (el, binding, vnode) {
        el.style.background = binding.value
      }
      [isVue3 ? 'unmounted' : 'unbind'] () {}
    })
  }
}

補足2: プラグインの修正ってどうやるの?

本題とは逸れますが、そもそもインストールしてきたプラグインをどのように修正すればよいか。

  1. プラグインのGitHubリポジトリを、自分のアカウントにForkする
  2. 該当箇所を見つけて修正する
  3. 本家リポジトリに(リポジトリのルールに沿って)プルリクを出す
  4. 本家がアップデートされるまではyarn add GitHubユーザー名/awesome-vue-plugin で自分がForkしたものをインストールする
47
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?