在了解插槽之前,我们需要先理解一个概念:编译作用域。
什么是编译作用域呢?
1.编译作用域
官方的解释是:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译
怎么理解这句话的含义呢?通过一个例子来说明。(偷个懒,不搭环境了)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>编译作用域</title>
</head>
<body>
<div id="app">
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2>组件标题</h2>
<p>组件内容</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = Vue.extend({
template: '#cpn',
data() {
isShow: false
}
})
const app = new Vue({
el: '#app',
components: {
cpn
},
data: {
isShow: true
}
})
</script>
</body>
</html>
最终的结果是:isShow为true,子组件的内容会显示;
这是因为:使用的时候,整个组件的使用过程是相当于在父组件中出现的。作用域就是父组件,使用的属性也是属于父组件的属性。因此,isShow使用的是Vue实例中的属性,而不是子组件的属性。
2.为什么要使用插槽slot
插槽的目的在于,是组件更具有扩展性。举个栗子,电脑预留的usb接口,可以用来连接多种外部设备,耳机、音响、U盘等等,使得更具有扩展性。插槽slot的作用正是如此,例如,组件中的一个地方,默认情况下为button,而在使用的时候,我们有需求需扩展为span,扩展为input,这时候我们就需要使用到插槽。
如何封装合适呢?抽取共性,保留不同。
最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。是搜索框,还是文字,还是菜单。由调用者自己来决定。
3.solt插槽的使用
//-------------------------- index.vue 父组件
<template>
<div>
<cpn><button>我是button元素</button></cpn>
<cpn></cpn>
</div>
</template>
<script>
import cpn from './cpn.vue'
export default {
data() {
return {
}
},
components:{
cpn
}
}
</script>
//-------------------分割线--------------------cpn组件:cpn.vue
<template>
<div>
<div>我是div元素</div>
<h2>我是h2元素</h2>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
}
},
}
</script>
显示效果:
子组件预留的slot位置,父组件在使用子组件的时候,在子组件中定义的内容,将渲染在slot的位置处,如果不定义,则不显示。
4.具名插槽
在上面了解了slot的强大之处后,我们就有一个疑问:如果我想在使用子组件的时候,有多个slot插槽,且想指定添加到想要的slot的位置,那应该怎么处理呢?
再例如:当子组件的功能复杂时,子组件的插槽可能并非是一个。比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
这时候,我们就需要给slot指定一个name属性,也就是具名插槽。
//-------------------------- index.vue 父组件
<template>
<div>
<cpn>
<div slot="center"><h2>中间被替换</h2></div>
</cpn>
</div>
</template>
<script>
import cpn from './cpn.vue'
export default {
data() {
return {
}
},
components:{
cpn
}
}
</script>
//-------------------分割线--------------------cpn组件:cpn.vue
<template>
<div>
<slot name="left">我是左边</slot>
<slot name="center">我是中间</slot>
<slot name="right">我是右边</slot>
</div>
</template>
<script>
export default {
data() {
return {
}
},
}
</script>
显示效果:
在使用的时候,指定solt的name,就可以替换指定的slot插槽。
5.作用域插槽
官方的定义比较拗口,讲讲自己的理解:
父组件在使用的时候可以替换slot插槽中的显示页面结构,但展示的数据还是来源于子组件。
看一个需求:子组件中包括一组数据,比如:pLanguages: [‘JavaScript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’]
需要在多个界面进行展示:
1.某些界面是以水平方向一一展示的,
2.某些界面是以列表形式展示的,
3.某些界面直接展示一个数组
内容在子组件,希望父组件告诉我们如何展示,怎么办呢?
//-------------------------- index.vue 父组件
<template>
<div>
<!-- 展示结构1 -->
<cpn>
<template slot-scope="slotProps">
<ul>
<li v-for="item in slotProps.data">{{item}}</li>
</ul>
</template>
</cpn>
<!-- 展示结构2 -->
<cpn>
<template slot-scope="slotProps">
<h2>{{slotProps.data.join('-')}}</h2>
</template>
</cpn>
</div>
</template>
<script>
import cpn from './cpn.vue'
export default {
data() {
return {
}
},
components:{
cpn
}
}
</script>
//-------------------分割线--------------------cpn组件:cpn.vue
<template>
<div>
<slot :data="languages"></slot>
</div>
</template>
<script>
export default {
data() {
return {
languages: ['Java', 'Python', 'JavaScript', 'TypeScript', 'C++', 'OC']
}
},
}
</script>
显示效果:
我们来看看大型的网站中是怎么时候作用于插槽的:
element-ui中的el-table: element-ui表单 el-table
截取部分代码:
<template slot-scope="scope">
<el-button
@click.native.prevent="deleteRow(scope.$index, tableData)"
type="text"
size="small">
移除
</el-button>
</template>
其中的scope代表的就是表格中的一行的数据。
slot插槽在封装组件的过程中,使用的非常频繁,后续有时间再对开发中的一些实用方法进行总结,欢迎补充不足之处,非常感谢。