Vue.js v2 で揮発性の現象を扱う(ダイアログ等を実装する)

同僚に題の相談を受けていたんですが、そのときに「あんまりこの話、ググっても見かけないね」という話になったのでブログに書いておきます。

SPA でダイアログを実装する機会があると思うんですが、よくある事前にコンポーネントツリーのルート直下にダイアログのコンポーネントを入れておく実装だと、マークアップスタイルシートの事情でその位置に入れるのが難しかったり、テンプレートにダイアログのコンポーネントがズラズラ並んでメンテナンスするのが大変になってきます。ダイアログのような、一時的にそのときだけ必要になる UI・コンポーネントは他の GUI プラットフォームでは「揮発性の現象」と呼ばれているようです。こういった UI・コンポーネントは、それが実際に画面に表示される時間と合わせて、必要になったタイミングでコンポーネントツリーにコンポーネントインスタンスが追加され、役目を終えたら削除されるのが理想です。

Vue.js では、以下で取り上げる API を利用することで上に述べた内容を実現することができます。

まず、動的にコンポーネントツリーにコンポーネントインスタンスを追加するところから。次のコードは、コンポーネントのオプションの一部で、ボタンが押されたときに実行されるメソッドが定義されています。

{
  methods: {
    openDialog: function () {
      let fooDialog = new FooDialog().$mount();
      this.$el.appendChild(fooDialog.$el);
    }
  }
}

エントリとなる要素を別で用意しておいて、マウントさせることもできます。( refs: https://jp.vuejs.org/v2/api/?#vm-mount )。

new FooDialog().$mount(el);

ダイアログで表示するデータの設定は、props で渡すか( https://jp.vuejs.org/v2/api/#propsData )、ダイアログを開くためのメソッドをダイアログのコンポーネントに定義して、その呼び出しの引数で渡す等、やり方は色々あります。 生成したコンポーネントインスタンスコンポーネントツリーに追加される = ダイアログの表示と考えてよければ前者で、表示のタイミングをもう少し制御したい場合は後者、というようにアプリケーションに応じて都度判断する必要があります。

コンポーネントインスタンス生成時の propsData オプションは、コンポーネントユニットテストのために用意されたオプションのようなので、本来想定されているケースとは異なる使い方かもしれません。バージョンアップのときに書き換えが発生する可能性があることを理解した上で使うのが良さそうです。

ダイアログの操作のハンドリングは、Vue.js のイベントインターフェイス( refs: https://jp.vuejs.org/v2/guide/components.html#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%A8%E3%81%AE-v-on-%E3%81%AE%E4%BD%BF%E7%94%A8 )を使うか、props にコールバック用の関数を渡して操作が完了したタイミングで呼び出してもらう( https://jp.vuejs.org/v2/api/#propsData )等、これもやり方が色々あります。これに関しても、アプリケーションに応じて適切だと思う方を選べばよいでしょう(悩ましい場合は、おそらくどちらでも問題ないので、好きな方を選べばよいでしょう)。

Vue.js のイベントインターフェイスを利用する例

// コンポーネントインスタンスを生成した親コンポーネントインスタンス
fooDialog.$on('click', (event) => {
});

// コンポーネントインスタンス内
this.$emit('click', event);

props にコールバックを渡す例

// コンポーネントインスタンスを生成した親コンポーネントインスタンス
let fooDialog = new FooDialog({
  propsData: {
    onClick: function (event) {}
  }
});

// コンポーネントインスタンス内
this.$props.onClick(event);

ダイアログの操作が完了したら、$destroy ( refs: https://jp.vuejs.org/v2/api/#vm-destroy ) で破棄して、コンポーネントツリーから除きます。

// コンポーネントインスタンスを生成した親コンポーネントインスタンス
fooDialog.$destroy();
fooDialog = null;

といったかんじで、Vue.js で揮発性の現象を扱うための API を見てきました。ダイアログ等を実装するときの参考にしていただけたらと思います。