✌️

Vue アプリそのものに対して props や emit を使う

2024/09/06に公開

Vue アプリそのものへ引数を渡して動的にアプリインスタンスを立ち上げたり、Vue アプリ内の emit で発行されたイベントを Vue アプリの外から購読したかったり、なんてことがありました。
レアケースだと思いますが、試行錯誤してちょっと苦労したので備忘録的に残しておきます。

アプリインスタンスで props を使う

createApp() の第2引数に渡すだけです。

const AppComponent = {
  props: ['name', 'job'],
  setup(props) {
    console.log(props.name)
    console.log(props.job)
    return {}
  }
}

const app = createApp(
  AppComponent,
  {
    name: '太郎',
    job: 'エンジニア',
  },
)
app.mount('#app')

こちらは公式の API リファレンス に書いてあります。

アプリインスタンスで emit を使う

こちらの解決策を実現するのに少々苦労しました。
createApp() の第2引数に onClick のように、 on から続くキャメルケースで関数を渡すと、 emit で発行されたイベントを購読できます。

下記は speak イベントを発行して onSpeak で購読する例です。

const AppComponent = {
  props: ['name', 'job'],
  emits: ['speak'],
  setup(props, { emit }) {
    console.log(props.name)
    console.log(props.job)

    /** 話す */
    function speak() {
      const lines = `私の名前は${props.name}です!`
      console.log(lines)
      emit('speak', lines)
    }

    return { speak }
  }
}

const app = createApp(
  AppComponent,
  {
    name: '太郎',
    job: 'エンジニア',
    onSpeak: lines => {
      console.log(`onSpeak:「${lines}`)
    },
  },
)
app.mount('#app')

これに気づいたのは、 レンダー関数 API のページを眺めていたときです。
レンダー関数を使ってコンポーネントを定義する際に書くイベントリスナと同様の書き方でできるのでは、と試したところ案の定でした。

// イベントリスナーは onXxx として渡す必要があります
h('div', { onClick: () => {} })

ユースケース

そもそも「Vue アプリそのものへ引数を渡して動的にアプリインスタンスを立ち上げたり、Vue アプリで emit で発行されたイベントを Vue アプリの外から購読したかったり、なんてこと」あるん???って感じですね。
例えば CDN での利用でページの一部分にだけ Vue を使用しているときに使いそうな気がします。
もしくは、1つのページ内に複数の Vue アプリが共存するケースで、それぞれを連携するのに使えると思います。

私は Vue アプリ内でモーダルダイアログを実装するときにこれを使いました。
ベースとなる Vue アプリとは別に、モーダルダイアログの中はまた独立した Vue アプリとして構築したかったのです。
でも完全に独立したいわけではなく、モーダルダイアログに値を渡したいし、モーダルダイアログでイベントを発行してベースの Vue アプリで購読したい、というときに活用しました。

「Vue アプリを複数使用する」という点でこのダイアログ実装のヒントとなったのは、私が愛する Quasar Framework の Dialog です。ダイアログを開くときはダイアログ専用の Vue アプリ内に展開されます。
Dialog 以外にも Notify とか他にも同様の実装だったものがあった気がします。

Discussion