Vue的slot和作用域插槽详解

slot


当某个组件被多个地方使用 , 每个地方使用该组件时对该组件的内部有一部分需要特殊定制 , 这个时候slot可以让我们更好的复用组件的同时并对其定制

当组件中某一项需要单独定义,那么就应该使用solt

例如项目中需要一个模态框提示 付款成功,付款失败。那么这个模态框也就仅仅差这几个字或者是状态图片而已。那么此时应用solt就是一个非常不错的选择。

  • 子组件模板包含至少一个 插口,否则父组件的内容将会被丢弃
  • 当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身

子组件没有插口

<template>
  <div id="app">
      <children>
        <span>子组件内部元素</span>
      </children>
  </div>
</template>
 
 
<script>
  export default {
    name: 'hello',
    components: {
      children: {
        template: '<div>这里是子组件</div>'
      }
    }
  }
</script>

<!--渲染结果-->
<div id="app">
    <div>这里是子组件</div>
</div>
复制代码

如果想让span显示那么此刻就应该使用slot。

<template>
  <div id="app">
      <children>
        <span>子组件内部元素</span>
      </children>
  </div>
</template>
 
<script>
  export default {
    name: 'hello',
    components: {
      children: {
        template: '<div><slot><p>默认效果</p></slot>这里是子组件</div>'
      }
    }
  }
</script>

<!--渲染结果-->
<div id="app">
    <span>子组件内部元素</span>
    "这里是子组件"
</div>
复制代码

具名slot

  • 如果想使用多个slot那么此时就应该使用具名slot
  • 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。
  • 仍然可以有一个匿名 slot ,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。
<template>
  <div id="app">
    <children>
      <div slot="header">
        <ul>
          <li>首页</li>
          <li>商城</li>
        </ul>
      </div>
      <div>
        这个是默认的没有具名的solt
      </div>
      <div slot="footer">
          <p>备案号</p>
      </div>
    </children>
  </div>
</template>
<script>
  var Child = {
    template: ' <div>这是子组件<div></div> <slot name="header"></slot> <slot></slot> <slot name="footer"></slot> </div>'
  }
  export default {
    name: 'hello',
    components: {
      children: Child
    }
  }
</script>

<!--渲染结果为-->
<div id="app">
    <div>
        "这是子组件"
        <div></div>
        <div>
            <ul>
                <li>首页</li>
                <li>商城</li>
            </ul>
        </div>
        <div>
            "这个是默认没有具名的slot"
        </div>
        <div>
            <p>备案号</p>
        </div>
    </div>
</div>
复制代码

作用域插槽

作用域插槽的关键之处就在于,父组件能接收来自子组件的slot传递过来的参数

<div id="app2">
    <!-- 组件使用者只需传递users数据即可 -->
    <my-stripe-list :items="users" odd-bgcolor="#D3DCE6" even-bgcolor="#E5E9F2">
        <!-- props对象接收来自子组件slot的$index参数 -->
        <template slot="cont" scope="props">
            <span>{{users[props.$index].id}}</span>
            <span>{{users[props.$index].name}}</span>
            <span>{{users[props.$index].age}}</span>
            <!-- 这里可以自定[编辑][删除]按钮的链接和样式 -->
            <a :href="'#edit/id/'+users[props.$index].id">编辑</a>
            <a :href="'#del/id/'+users[props.$index].id">删除</a>
        </template>
    </my-stripe-list>
</div>
<script>
    Vue.component('my-stripe-list', {
        /*slot的$index可以传递到父组件中*/
        template: `
            <div>
                <div v-for="(item, index) in items" style="line-height:2.2;" :style="index % 2 === 0 ? 'background:'+oddBgcolor : 'background:'+evenBgcolor">
                    <slot name="cont" :$index="index"></slot>
                </div>
            </div>
        `,
        props: {
            items: Array,
            oddBgcolor: String,
            evenBgcolor: String
        }
    });
    new Vue({
        el: '#app2',
        data: {
            users: [
                {id: 1, name: '张三', age: 20},
                {id: 2, name: '李四', age: 22},
                {id: 3, name: '王五', age: 27},
                {id: 4, name: '张龙', age: 27},
                {id: 5, name: '赵虎', age: 27}
            ]
        }
    });
</script>
复制代码
<div id="demo1">
    <div>
        <h3>1、单个插槽</h3>
        <single>
            dsafd
        </single>
    </div>

    <muli-slots>
        <!--在html中不支持驼峰式的标签名,需改为横线式-->
        <h3 slot="header">2、多插槽/具名插槽</h3>
        <p>主要内容的一个段落。</p>
        <p>另一个主要段落。</p>
        <p slot="footer">这里有一些联系信息</p>
    </muli-slots>
    <!--作用域插槽-->
    <!--在父级中,具有特殊特性 slot-scope 的 <template> 元素必须存在,表示它是作用域插槽的模板。
                slot-scope 的值将被用作一个临时变量名,此变量接收从子组件传递过来的 prop 对象:-->
    <div class="parent">
        <h3>3、作用域插槽</h3>
        <scoped-slot>
            <template slot-scope="props">
                        <!--这里的props指的是子组件的props-->
                            <span>this is from parent</span>
                            <span>{{props.msg}}</span>
                        </template>
        </scoped-slot>
    </div>
    <div>
        <h3>4、使用render代替子组件中的template</h3>
        <render-slot></render-slot>
    </div>
    <div>
        <h3>5、子组件中同时存在template和render函数</h3>
        <render-template></render-template>
    </div>
    <div>
        <h3>6、在render函数中使用作用域插槽</h3>
        <render-scope>
            <template slot-scope="props">
                                <p>{{props.msg}}</p>
                            </template>
        </render-scope>

    </div>

    <!--父组件通过props向子组件传值-->
    <div class="parent">
        <h3>7、父组件通过props向子组件传值,值在template的slot标签中接收</h3>
        <!--传递的值需要写在子组件的标签中-->
        <child datamsg="传递的值" msg2="第二个值"></child>
    </div>

    <div>
        <h3>8、父组件通过props向子组件传值,值在render函数返回的createElement函数中接收</h3>
        <child-render msg="render send data">
            <template slot-scope="props">
                        <em>{{props.text}}</em>
                    </template>
        </child-render>
    </div>
    <div>
        <h3>9、与8等价的template</h3>
        <child-render-template msg="template send data">
            <template slot-scope="props">
                                <em>{{props.text}}</em>
                            </template>
        </child-render-template>
    </div>
</div>

<script>
 //单个插槽
        Vue.component('single', {
            //<slot> 不能作为根元素,当<single>标签为空元素时,会显示<slot>里面的内容
            template: '<div><slot>单个插槽</slot></div>'
        })

        //多个插槽/具名插槽
        Vue.component('muliSlots', {
            template: `
                <div>
                    <header> <slot name="header"></slot></header>
                    <main><slot>没有内容时,这个会出现</slot></main>
                    <footer><slot name="footer"></slot></footer>
                </div>
            `
        })

        //作用域插槽
        //作用域插槽是一种特殊类型的插槽,用作一个(能被传递数据的)可重用模板,来代替已经渲染好的元素。
        Vue.component('scopedSlot', {
            template: `
                <div class="child">
                    <slot msg="this is from child"></slot>
                </div>
            `
        })

        //使用render代替template
        Vue.component('renderSlot', {
            render(createEle) {
                return createEle('em', 'render test')
            }
        })

        // render 和 template同时存在的情况,会忽略template
        Vue.component('render-template', {
            template: '<div>this is from template</div>',
            render(createEle) {
                return createEle('div', 'this is from render')
            }
        })

        //render 使用作用域插槽
        Vue.component('render-scope', {
            render(createEle) {
                return createEle('p', this.$scopedSlots.default({
                    msg: 'this is from render-scopeded slot'
                }))
            }
        })

        //父组件传值
        Vue.component('child', {
            props: ['datamsg', 'msg2'],
            template: '<div><p>{{datamsg}}</p><p>{{this.msg2}}</p></div>'
        })

        //父组件使用render 传值
        Vue.component('child-render', {
            props: ['msg'], //this is from parent
            //<div><slot text="this is from render-scopeded slot"><slot><p>{{msg}}</p></div>
            render(createEle) {
                var p = createEle('p', {
                    domProps: {
                        innerHTML: this.msg //这里的this不能省略
                    }
                })
                return createEle('div', [this.$scopedSlots.default({
                    text: 'this is from render-scopeded slot' //子组件自己的props属性
                }), p])
            }
        })

        //与上面等价的template
        Vue.component('child-render-template', {
            props: ['msg'], //this is from parent
            template: `<div>
                <slot text="this is from render-scopeded slot"></slot>
                <p>{{msg}}</p>
            </div>`
        })
        new Vue({
            el: '#demo1'
        })
</script>

复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值