Vue作用域插槽的前世今生

256 阅读3分钟

引言

对于插槽的概念,这里就不做赘述。在说作用域插槽之前,有必要说一下具名插槽,二者有相关性。 父组件通过prop向子组件传递数据,通过插槽向子组件传递内容。前者传递的是组件的属性,后者传递的是vNode节点。

具名插槽的前世(已废弃)

【1】在父组件调用子组件的开始标签和结束标签中添加任意DOM代码或者template块,给template块或者任意标签添加slot='插槽名'

【2】在子组件中添加<slot name='插槽名'></slot>

parent.vue

<template>
  <div>
    <old-child>
      <template slot='header'>
        <div>
          <p>这是template中的内容,也是一个具名插槽</p>
          <div>这是template中的内容</div>
        </div>
      </template>
      <div slot="footer">这是一个普通的标签,也是一个具名插槽</div>
      <div>这只是一个标签</div>
    </old-child>
  </div>
</template>

<script>
  import OldChild from './oldChild'
  export default {
    name: 'newParent',
    components: {
      OldChild
    }
  }
</script>
<style scoped>
</style>

child.vue

<template>
  <div>
    <slot name='header'></slot>
    <slot></slot>
    <slot name='footer'></slot>
  </div>
</template>

<script>
  export default {
    name: 'oldChild'
  }
</script>

<style scoped>
</style>

效果图:

具名插槽的今生

【1】在父组件调用子组件的开始标签和结束标签中添加template块或任意DOM,给具名插槽的DOM使用template包裹,给具名插槽的template标签添加v-slot:插槽名

【2】在子组件中添加<slot name='插槽名'></slot>

v-slot只能添加在template标签上

parent.vue

<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
  <div>
    <slot-child>
      <template v-slot:header>
        <ul>
          <li>新具名插槽</li>
        </ul>
      </template>
<!--      <div slot-scope='dataArr'>旧slot-scope,这是一个默认的插槽{{dataArr}}</div>-->
      <div>这是一个默认的插槽的内容</div>
      <template v-slot:footer>
        <p>新具名插槽,这是一个底部</p>
      </template>
    </slot-child>
  </div>
</template>

<script>
  import SlotChild from './slotChild'
  export default {
    name: 'slotParent',
    components: {
      SlotChild
    }
  }
</script>

<style scoped>
</style>

child.vue

<template>
  <div>
    <slot name='header'></slot>
    <slot name='footer'></slot>
    <slot></slot>
  </div>
</template>

<script>
  export default {
    name: 'slotChild',
    data: function () {
      return {
        dataArr: [{
          id: 1
        },
        {
          id: 2
        }],
        user: 'Ross'
      }
    }
  }
</script>

<style scoped>
</style>

效果如图:

作用域插槽的前世(已废弃)

【1】在父组件调用子组件的开始标签和结束标签中添加任意DOM代码或者template块,给template块或者任意标签添加slot-scope="slotProps"

【2】在子组件中通过v-bind:变量名='子组件数据'将子组件中的数据通过插槽传递给父元素

parent.vue

<template>
  <div>
    <old-child>
      <div slot='nav' slot-scope='slotscopes'>{{slotscopes}}</div>
      <template slot='header' slot-scope='slotscopes'>
        <div>
          {{slotscopes}}
        </div>
      </template>
      <div slot="footer" slot-scope='slotscopes'>{{slotscopes}}</div>
      <div slot-scope='slotscopes'>{{slotscopes}}</div>
    </old-child>
  </div>
</template>

<script>
  import OldChild from './oldChild'
  export default {
    name: 'newParent',
    components: {
      OldChild
    }
  }
</script>

<style scoped>
</style>

child.vue

<template>
  <div>
    <slot name='nav' v-bind:dataArr='dataArr'></slot>
    <slot name='header' v-bind="dataArr"></slot>
    <slot v-bind="userName"></slot>
    <slot name='footer' v-bind:userName="userName"></slot>
  </div>
</template>

<script>
  export default {
    name: 'oldChild',
    data: function () {
      return {
        dataArr: [{
          id: '1',
          name: 'lili'
        },
        {
          id: '2',
          name: 'jack'
        }
        ],
        userName: 'jjjj'
      }
    }
  }
</script>

<style scoped>
</style>

效果图:

通过效果图可以看出,子元素未绑定变量时,会对数据进行遍历,所以通过子组件向父组件传递数据,应该绑定变量。

关于子元素未绑定变量时,为什么会对数据进行遍历的问题,同事去看了源码,帮解决了这个疑问(我,弱小),源码如下:

if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {
  warn(
    'slot v-bind without argument expects an Object',
    this
  );
}

slot上的v-bind如果没有参数,应该传入一个对象,而不是一个数组。

注意:①子组件向父组件传递数据应该绑定变量;②slot和slot-scope已废弃

作用域插槽的今生

【1】在父组件调用子组件的开始标签和结束标签中添加template块或任意DOM,给具名插槽的DOM使用template包裹,给具名插槽的template添加v-slot:插槽名

【2】在子组件中通过v-bind:变量名='子组件数据'将子组件中的数据通过插槽传递给父元素

【3】如果数据要传递给具名插槽,在具名插槽的template标签上修改v-slot:插槽名v-slot:插槽名='slotProps'

【4】如果数据要传递给默认插槽,将默认插槽的DOM使用template包裹,在template标签上添加v-slot='slotProps',因为v-slot只能在template标签上使用

parent.vue

<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
  <div>
    <slot-child>
      <template v-slot:header='dataArr'>
        <ul>
          <li>新具名插槽{{dataArr}}</li>
        </ul>
      </template>
<!--      <div slot-scope='dataArr'>旧slot-scope,这是一个默认的插槽{{dataArr}}</div>-->
      <template v-slot='dataArr'>给默认插槽传递数据,这是一个默认的插槽{{dataArr}}</template>
      <template v-slot:footer="slotProps">
        <p>新具名插槽,这是一个底部{{slotProps}}</p>
      </template>
    </slot-child>
  </div>
</template>

<script>
  import SlotChild from './slotChild'
  export default {
    name: 'slotParent',
    components: {
      SlotChild
    }
  }
</script>

<style scoped>
</style>

child.vue

<template>
  <div>
    <slot name='header' v-bind:arr='dataArr'></slot>
    <slot name='footer' :myUser="user"></slot>
    <slot v-bind:arr='dataArr'></slot>
  </div>
</template>

<script>
  export default {
    name: 'slotChild',
    data: function () {
      return {
        dataArr: [{
          id: 1
        },
        {
          id: 2
        }],
        user: 'Ross'
      }
    }
  }
</script>

效果如下: