<template> に存在していない Vue コンポーネントを動的に追加
Vue インスタンスを作成して $mount
すればおk。
add() { const instance = new Bar({ propsData: { date: new Date().valueOf() } }) .$on("click", date => console.log(date)) .$mount(); document.body.appendChild(instance.$el); }
$mount
に elementOrSelector を設定、あるいは Vue インスタンスの生成時に el
を設定しても一応可能ですが、その場合「追加」ではなく「置換」になるので注意が必要です。
vue-append-instance-programmatically - StackBlitz
利用ケース
デバッグ用の View を追加したいけど、<DebugView v-if="isDebug" />
のように既存のコードに手を加えたくない場合にこんな感じで対応しました。
Nuxt の場合 plugin を利用して以下のように記述できると思います。
// plugins/routes-viewer/index.ts // 登録されている全てのルートを表示するコンポーネント import { Context } from '@nuxt/types'; import VueRouter, { RouterOptions } from 'vue-router'; import Viewer from './Viewer.vue'; export default (context: Context) => { const router = context.app.router; if (!router) { return; } const removeGuard = router.afterEach(() => { const instance = new Viewer({ propsData: { paths: pickPaths(router), }, }) .$on('click', (path: string) => router.push(path)) .$mount(); document.body.appendChild(instance.$el); removeGuard(); }); }; const pickPaths = (router: VueRouter) => { const routes = (router.options as RouterOptions).routes; if (!routes) { return []; } return routes.map(route => route.path).sort(); };
メモ
$el
$el
は Vue インスタンスが管理しているルート DOM 要素です。$mount される前はまだ存在しないです。
const instance = new Bar({ ... }); console.log(instance.$el); // => undefined instance.$mount(); console.log(instance.$el); // => <div>Bar<br>1578449862325</div>
new Bar()
or new Vue()
?
Bar
がプレーンなオブジェクトなのか、Vue.extend
を利用して生成された Vue コンストラクタのサブクラスなのか。
Bar
が Vue コンストラクタのサブクラスならばサンプル通りに new Bar()
が利用できます。そうではない場合、当然ですが new Bar()
ではエラーになります。new Vue({ ...Bar, /* */ });
のようになります。
生成した Vue インスタンス内で Router などが使えない
新たに作成した Vue インスタンスは、既存のルート Vue インスタンスとは別の管理になります。つまり、このままでは this.$router
や this.$store
にはアクセスできません。
新たに inject することもできますが、今回のようなケースでは追加されるコンポーネントには機能をあまりもたせず、追加する側で対応する方がよさそうに思えます。