組件(4):使用slot進行內容分發

組件的做用域(一)

父組件模板的內容在父組件做用域內編譯;子組件模板的內容在子組件做用域內編譯。

父子組件的編譯相互獨立,編譯時只能使用各自做用域中的屬性和方法,例如,你不能夠在父組件模板內,將一個指令綁定到子組件的屬性或方法上。若是這麼作控制檯會報一個屬性未定義的錯誤。javascript

若是想要綁定一個指令以便控制子組件的行爲,那麼你能夠在子組件的模板內,將一個指令綁定到子組件的屬性或方法上;或者在父組件的模板內,將指令綁定到父組件的屬性或方法上。java

new Vue({
    el: '#app-2',
    data: {
        makeChildShow: true
    },
    components: {
        "component-2-1": {
            template: '<span>I am an sub component.</span>',
            data: function () {
                return {
                    childIsShow: true
                }
            }
        },
        "component-2-2": {
            template: '<span v-show="childIsShow">I am an sub component.</span>',
            data: function () {
                return {
                    childIsShow: true
                }
            }
        }
    }
})
<div id="app-2">
  <ul>
    <li>綁定到子組件屬性:<component-2-1 v-show="childIsShow"></component-2-1></li>
    <li>綁定到父組件屬性:<component-2-1 v-show="makeChildShow"></component-2-1></li>
    <li>在子組件模板內將指令綁定到子組件屬性:<component-2-2></component-2-2></li>
  </ul>
</div>

clipboard.png

列表第一項,因爲父組件找不到屬性childIsShow,將不會顯示。es6

默認狀況下的內容分發

將父組件的內容插入子組件模板的方式,咱們稱爲內容分發
默認狀況下,在子組件中插入的父組件內容是不顯示的。app

new Vue({
    el: '#app-1',
    data: { message: 'I come from parent.' },
    components: {
        "component-1": {
            template: '<p>I am an sub component.</p>',
        }
    }
})
<div id="app-1" class="demo">
  <component-1>
    {{ message }}
  </component-1>
</div>

內容分發失敗,message不會顯示。spa

clipboard.png

單個插口

若是想使用內容分發,將父組件內容插入到子組件的模板中,必須在子組件的模板內標記一個<slot>備選內容</slot>,父組件將找到這個<slot>備選內容</slot>標記,並將本身的內容替換<slot>備選內容</slot>
若是父組件沒有待分發的內容,備選內容成爲最終渲染的結果。3d

new Vue({
    el: '#app-3',
    components: {
        "component-3": {
            template: '\
            <ul>\
                <li>子組件內容</li>\
                <li>子組件內容</li>\
                <slot><li>插口備選內容</li></slot>\
                <li>子組件內容</li>\
                <li>子組件內容</li>\
            </ul>'
        }
    }
})
<div id="app-3">
  <h5>父組件標題</h5>
  <component-3>
    <li>父組件插入內容</li>
  </component-3>
</div>

clipboard.png

具名插口(多個插口)

單插口模式作內容分發,只能一股腦把套入子模板的內容插入到有<slot></slot>標記的地方。
而具名插口在內容和slot上都作上標記,對應的內容只能分發到對應的slot上。
標記內容用slot="tag"; 標記slot<slot name="tag">code

new Vue({
    el: '#app-4',
    components: {
        "component-4": {
            template: '\
            <div>\
                <header>\
                    <slot name="header"></slot>\
                </header>\
                <article>\
                    <slot></slot>\
                </article>\
                <footer>\
                    <slot name="footer"></slot>\
                </footer>\
                <section><slot></slot></section>\
            </div>'
        }
    }
})
<div id="app-4">
  <component-4>
    <h5 slot="header">我來組成頭部</h5>
    <p>沒被標記的slot都我插</p>
    <div slot="footer">我來組成腿部</div>
  </component-4>
</div>

以上定義了兩個個不具名的插口,雖然這裏顯示正確,可是控制檯報錯,說定義重複的默認插口會有不預期的錯誤component

clipboard.png

組件做用域(二)

根據具名插口,再來看個組件做用域的例子對象

new Vue({
    el: '#app-7',
    methods: {  
        parentMethod: function () {  
            console.log("It is the parent's method");  
        }  
    },  
    components:{
        "component-7":{
            methods:{
                childMethod: function(){
                    console.log("It is the child's method")
                }
            },
            template:"\
                <button>\
                    <slot name='first'></slot>\
                    <span @click='childMethod'>子組件模板定義部分①|</span>\
                    <slot name='second'></slot>\
                    <span @click='childMethod'>子組件模板定義部分②</span>\
                </button>"
        }
    }
})
<div id="app-7">
  <component-7>
      <span slot="first" @click="parentMethod">內容分發部分①|</span>  
      <span slot="second" @click="parentMethod">內容分發部分②|</span>  
  </component-7>
</div>

clipboard.png
內容分發部分屬於父組件做用域,所以點擊按鈕的內容分發部分,會調用父組件方法,輸出"It is the parent's method"
子組件模板定義屬於子組件做用域,點擊這個部分,會調用子組件方法,輸出"It is the child's method"blog

插口做用域

在內容分發的過程當中,父組件分發的內容可使用定義在子組件模板<slot>上的屬性(即插口做用域上定義的屬性)。如<slot slotval="值"></slot>,在父組件分發內容上,能夠經過slot-scope="obj"獲取到全部在插口上定義的屬性,經過{{obj.slotval}}就能夠在slot-scope內部使用這個數據。
特殊的,在<template>標籤中使用slot-scope<template>自身是不會在頁面上顯示,只起到傳遞數據媒介的做用。

new Vue({
    el: '#app-5',
    components: {
        "component-5":{
            template: '<div class="child">\
                <slot slotvala="a、來自插口做用域上的數據" slotvalb="b、來自插口做用域上的數據"></slot>\
                </div>'
        }
    }
})
<div id="app-5">
  <component-5>
    <!--這裏能夠是其餘標籤,但會被渲染到頁面,如<div slot-scope="">-->
    <template slot-scope="slot_data_obj">
      <span>{{slot_data_obj.slotvala}}</span>
      <span>{{slot_data_obj.slotvalb}}</span>
    </template>
  </component-5>
</div>

clipboard.png

因爲slot-scope的值本質上只是個javascript對象,所以可使用es6的解構語法進行<slot>屬性的映射。
以上又能夠怎麼寫

<template slot-scope="{slotvala,slotvalb}">
  <span>{{slotvala}}</span>
  <span>{{slotvalb}}</span>
</template>

插口做用域應用在列表上

插口不只能夠經過自身屬性傳遞數據給分發的內容,還能夠在其上定義v-for指令,從而將迭代的特性也傳遞給分發的內容。

new Vue({
    el: '#app-6',
    components: {
        "component-6":{
            data:function(){
                return {
                    items: [
                        {id:1,text:"哪都通快遞"},
                        {id:2,text:"龍虎山天師府"},
                        {id:3,text:"曜星社"}
                    ]
                }
            },
            template: '\
            <ul>\
                <slot name="item" v-for="item in items" v-bind:text="item.text" v-bind:id="item.id"></slot>\
            </ul>'
        }
    }
})
<div id="app-6">
  <component-6>
    <li slot="item" slot-scope="{text,id}">
      {{ id+"、"+text }}
    </li>
  </component-6>
</div>

內容分發中的<li>被插入slot中,而且由於slot中的v-for指令而進行迭代,迭代以後經過slot-scope獲取slot上的屬性數據。

clipboard.png

內聯模板

當使用子組件的內聯特性——inline-template時,父組件的內容分發部分就被解釋爲子組件的模板,而子組件的template屬性也將被這個部分取代(取代後template失效),而且做用域也屬於子組件。

new Vue({
    el:'#app-8',
    components:{
        'component-8':{
            template:'...'//所有被替換
            data:function(){
                return {
                    childMsg:'child\'s data'
                }
            }
                
        }
    }
})
<div id="app-8">
    <component-8 inline-template>
        <div>
            <p>這些將做爲組件自身的模板。</p>
            <p>而非父組件透傳進來的內容。</p>
            <p>子組件數據: {{ childMsg }}</p>
        </div>
    </component-8>
</div>

clipboard.png

"child's data"來自子組件的childMsg

因爲其特色,在使用內聯模板時,最容易產生的誤區就是混淆做用域。

相關文章
相關標籤/搜索