個人的メモ
前提
大きめのSPAを開発するに中で、あるコンポーネントを呼び出す際、その中身を呼び出し元毎に変えたいってあるよね。(例えばモーダルとか)
中身のパターンが少しだけならtype
を渡してv-if
で切り替えてもいいけど多くなるとしんどいし、
<slot>
を使えば簡単にできるけど、呼び出し元毎に同じコンポーネントを埋め込むのは非常にナンセンスな気が。。。
storeのstateに中身コンポーネントを格納しておいて呼び出し元でそのstateを別のコンポーネントに更新すると中身コンポーネントも切り替わるという設計で作成すればかなり柔軟なのではないかと行き着きました。
問題
ということでモーダルを作りました。が
いざモーダルを表示してみるとstateの中身コンポーネントがレンダリングされないなぜ!(文字列でfunction(){...}が表示される)
ってインスタンスのcomponentsに登録しないとそりゃまぁレンダリングされませんよね。。
const inner = Vue.extend({...});
export default {
state: {
show: false,
component: inner
}
}
<template>
<div v-if="show" class="modal">
<div class="header">Modal Header</div>
<div class="body">{{ inner }}</div>
</div>
</template>
<script>
export default {
computed: {
...mapState({
show: state => state.show,
inner: state => state.inner
})
}
}
</script>
components
って動的に登録できないよな。。。とかしばらく悩んでたのですがすぐ公式DOCを見ていくうちに解決しました。
isディレクティブを使う
isディレクティブを使えば動的にコンポーネントを切り替えられるみたいです。
公式DOCではcomponentsに登録した名前をisディレクティブに文字列でバインドしているみたいですが、コンポーネント自身も入れることができるみたいです。
<template>
<div v-if="show" class="modal">
<div class="header">Modal Header</div>
<div class="body">
<component is="inner"></component>
</div>
</div>
</template>
<script>
export default {
computed: {
...mapState({
show: state => state.show,
inner: state => state.inner
})
}
}
</script>
こうすれば無事中身コンポーネントもレンダリングされました。