【Vue.js 入門】スロットの使い方

Vue.js

今回は、スロットの使い方について紹介していきます。


Vue.jsの書き方を最初から知りたい方はこちら(^^)



参考書

スロットとは

コンポーネントでは、タグの間のコンテンツ(例えば ‘<div> … </div>’ の、‘…’ の部分)は無視されて描画されますが、場合によっては状況に応じて外からコンテンツを受け取るほうが再利用性が高まることがあります。

これを実現する機能をスロットと言います。


スロットには単一スロットと、名前付きスロットに大きく分けられます。

単一スロット

まずは、名前のついていない単純な単一スロットから見ていきます。

<div id="app">
  <sample>Hello</sample>
  <sample></sample>
</div>
// main.js(親コンポーネント)

var app = new Vue({
  el: '#app',
  components: {
    'sample': sample
  }
});
// sample.js(子コンポーネント)

var sample = {
  template: `
    <p>
      <slot>Good bye</slot>
    </p>
  `
};

もしスロット( <slot> … </slot> )を使わなければ、<sample>タグの間の ‘Hello’ は無視され、ブラウザには以下のように表示されます。


しかし、スロットを用いたことで実際は以下のようになります。



つまり、HTML側に書いた ‘Hello’ というコンテンツが優先されていることがわかりますね。

下の<sample>タグにはコンテンツを指定していないので、slot 要素内のコンテンツがデフォルトとして扱われています。

名前付きスロット

slot 要素内の name 属性で名前を指定することで、特定の個所にスロットを挿入することができます。

<div id="app">
  <sample>
    <h1 slot="h1">これはh1です</h1>
    <h2 slot="h2">これはh2です</h2>
    <h3>これはh3です</h3>
  </sample>
</div>
// main.js(親コンポーネント)

var app = new Vue({
  el: '#app',
  components: {
    'sample': sample
  }
});
// sample.js(子コンポーネント)

var sample = {
  template: `
    <div>
      <slot name="h1"></slot>
      <slot name="h2"></slot>
      <slot></slot>
    </div>
  `
};



DOMは以下のように描画されます。



以下のようなイメージでデータが結びついています。

ご覧のように、slot 属性をもつコンテンツは、その属性値と同じ name 属性値をもつ <slot> タグに挿入されますね。

name 属性値をもたないスロットは単一スロットとして扱われ、slot 属性をもたないコンテンツがそこに挿入されます。

スロットのスコープ

Vue.js では、スロットとして挿入されるコンテンツでも、親テンプレートには親コンポーネントのスコープが、子テンプレートには子のスコープが適用されます。


サンプルでその挙動を確認してみましょう。

<div id="app">
  <sample>
    {{message}}
  </sample>
</div>
// main.js(親コンポーネント)

var app = new Vue({
  el: '#app',
  components: {
    'sample': sample
  },
  data: function() {
    return {
      message: 'parent'
    }
  }
});
// sample.js(子コンポーネント)

var sample = {
  template: `
    <p>
      <slot></slot>
    </p>
  `,
  data: function() {
    return {
      message: 'child'
    }
  }
};

親と子のどちらのコンポーネントにも message プロパティをデータに持たせています。

そして、実際に {{ message }} にバインドされるのは、親コンポーネントの message プロパティなんですね。つまりブラウザには ‘parent’ と表示されます。


では次に、{{ message }} を親テンプレートから子テンプレートに移します。
それ以外は変えません。

<div id="app">
  <sample></sample>
</div>
// sample.js(子コンポーネント)

var sample = {
  template: `
    <p>
      <slot>{{message}}</slot>
    </p>
  `,
  data: function() {
    return {
      message: 'child'
    }
  }
};

すると今度は子コンポーネントの message プロパティがバインドされ、ブラウザには ‘child’ と表示されます。

これで、親テンプレートには親コンポーネントのスコープが、子テンプレートには子スコープが適用されるということが確認できました。



しかし、親テンプレートから子コンポーネントのデータにアクセスしたい場合があるはずです。
そのような場合は、子から親へデータを渡す処理と、親がデータを受け取る処理が必要になります。


子コンポーネントから親コンポーネントにデータを渡すには、子コンポーネントの slot 要素に v-bind でデータを渡すことができます。

そして、親が受け取るには slot-scope プロパティを用います。

<div id="app">
  <sample v-bind:lists="lists">
    <li slot-scope="{list}" v-bind:key="list.id">
      {{list.name}}
    </li>
  </sample>
</div>
// main.js(親コンポーネント)

var app = new Vue({
  el: '#app',
  components: {
    'sample': sample
  },
  data: function() {
    return {
      lists: [
        { id: 1, name: 'にんじん' },
        { id: 2, name: 'ピーマン' },
        { id: 3, name: 'ごぼう' }
      ]
    }
  }
});
// sample.js(子コンポーネント)

var sample = {
  template: `
    <ul>
      <slot v-for="list in lists"
            v-bind:list="list">
      </slot>
    </ul>
  `,
  props: ['lists']
};

子コンポーネントの v-bind:list=”list” で親にデータを渡す準備をし、親テンプレートの slot-scope=”{ list }” で子からデータを受け取っています。

データの受け渡しは以下のようなイメージです。緑色の線が今回のポイントです(^^)



ブラウザには以下のように表示されます。



※ “{ list }” という書き方に関する補足
これは Vue.js 独自のものではなく ES2015の分割代入という仕様を使っています。





今回は以上になります。
ご覧いただきありがとうございました(^^)

続きはこちら↓




参考書

コメント

コンタクトフォーム

    タイトルとURLをコピーしました