官方地址:https://cn.vuejs.org/v2/guide/components-slots.html#ad

官方解释:Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。

插槽能干什么?

1.匿名插槽

我们现在想自己制作一个button标签

我们新建了一个子组件mbutton.vue

 1 <template>
 2     <div>
 3         <div class="mbutton">
 4         <slot>
 5         <!--插槽,承载内容的出口-->
 6         </slot>
 7         </div>
 8     </div>
 9 </template>
10 
11 <script>
12     export default {
13         
14     }
15 </script>
16 
17 <style  scoped>
18 .mbutton{
19     display: inline-block;
20     padding: 5px 10px;
21     background: blue;
22     color: white;
23     border-radius: 5px;
24 }
25 </style>

在父组件中调用这个mbutton

 1 <template>
 2   <div>
 3     <mbutton>
 4     确定
 5     </mbutton>
 6   </div>
 7 </template>
 8 
 9 <script>
10 import mbutton from "./components/mbutton.vue"
11   export default {
12     components:{
13       mbutton
14     }
15   }
16 </script>
17 
18 <style scoped>
19 
20 </style>

页面中会渲染对应的文本

插槽_作用域

机理就是子组件内部的slot就是用来存放父组件的内容部分,slot就是一个内容入口,子组件将父组件的内部文本通过slot进行结合

slot最后会被替代,变成下面的代码

1 <div>
2     <div class="mbutton">
3         确定
4     </div>
5 </div>

确定的文字是slot最后展示的内容,展示的是父组件的内容部分,这个内容可以是文本也可以是元素,甚至是组件

如果slot中有内容,这个内容是后备内容

1 <div class="m-button">
2     <!--插槽,承载内容的出口-->
3     <slot>
4         确定
5     </slot>
6 </div>

父组件的调用

1 <mbutton></mbutton>

插槽_作用域

 

我们为什么要使用slot,有什么好处

答:slot可以帮助我们拓展很多功能,比如我们模仿ui框架制作按钮

mbutton.vue

 1 <template>
 2     <div>
 3         <div class="mbutton" :class="{[type]:true,[size]:true}">
 4         <slot>
 5         <!--插槽,承载内容的出口-->
 6         确定
 7         </slot>
 8         </div>
 9     </div>
10 </template>
11 
12 <script>
13     export default {
14         props:{
15             type:{
16                 type:String,
17                 default:"default"
18             },
19             size:{
20                 type:String,
21                 default:"default"
22             }
23         }
24     }
25 </script>
26 
27 <style  scoped>
28 .mbutton{
29     display: inline-block;
30     padding: 5px 10px;
31     background: blue;
32     color: white;
33     border-radius: 5px;
34     margin: 5px;
35 }
36 .primary{
37     background: red;
38 }
39 .error{
40     background: green;
41 }
42 .large{
43     padding: 12px 18px;
44     font-size: 18px;
45 }
46 .samll{
47     padding: 5px 8px;
48     background: skyblue;
49 }
50 </style>

 

 App.vue

1 <template>
2   <div>
3     <mbutton type="primary" size="large"></mbutton>
4     <mbutton type="samll" >提交</mbutton>
5     <mbutton type="error" ></mbutton>
6   </div>
7 </template>

插槽_作用域_03

 

2.具名插槽

我们现在制作一个dialog弹出效果

 App.vue

 1 <template>
 2   <div>
 3     <Dialog>
 4      <div slot="header">      
 5       警告
 6       </div> 
 7     <p>123456789123456789</p>
 8     <p>123456789123456789</p>
 9     <p>123456789123456789</p>
10     <p>123456789123456789</p>
11     <p>123456789123456789</p>
12     <p>123456789123456789</p>
13     <p>123456789123456789</p>
14     <div slot="footer">
15     <mbutton>取消</mbutton>
16     <mbutton type="primary">提交</mbutton>
17     </div>
18     </Dialog>
19   </div>
20 </template>
21 
22 <script>
23 import Dialog from "./components/Dialog.vue"
24 import mbutton from "./components/mbutton.vue"
25   export default {
26     components:{
27       Dialog,
28       mbutton
29     }
30 
31   }
32 </script>
33 
34 <style  scoped>
35 
36 </style>

mbutton.vue

 1 <template>
 2     <div class="warp">
 3         <div class="mbutton" :class="{[type]:true, [size]:true}">
 4         <slot></slot>
 5         </div>
 6     </div>
 7 </template>
 8 
 9 <script>
10     export default {
11         props:{
12             type:{
13                 type:String,
14                 default:"default"
15             },
16             size:{
17                 type:String,
18                 default:"default"
19             }
20         }
21         
22     }
23 </script>
24 
25 <style  scoped>
26 .mbutton{
27     display: inline-block;
28     padding: 8px 16px ;
29     background: skyblue;
30     color: #fff;
31     border-radius: 5px;
32     margin: 5px;
33 }
34 .default{
35     border: 1px solid #ccc;
36     background: #fff;
37     color: #555;
38 }
39 .primary{
40     background: navy;
41   color: #fff;
42 }
43 .large{
44     padding: 12px 18px;
45     font-size: 18px;
46 }
47 
48 .error{
49     background: red;
50 }
51 .small{
52     padding: 5px 8px;
53 }
54 .warp{
55     display: inline-block;
56 }
57 
58 </style>

Dialog.vue

 1 <template>
 2     <div>
 3         <dib class="box">
 4         <header>
 5         <slot name="header">提示</slot>
 6         </header>
 7         <main>
 8        <slot></slot>
 9         </main>
10         <footer>
11        <slot name="footer"></slot>
12         </footer>
13         </dib>
14     </div>
15 </template>
16 
17 <script>
18     export default {
19 
20     }
21 </script>
22 
23 <style  scoped>
24 .box{
25     width: 60%;
26 background: #fff;
27 box-shadow: 0px 0px 3px 2px rgba(0, 0, 0,0.4);
28 position: fixed;
29 top: 50%;
30 left: 50%;
31 transform: translate(-50%,-50%);
32 border-radius: 4px;
33 }
34 header{
35     padding: 10px 5px ;
36     border-bottom: 1px solid #ccc;
37 }
38 footer{
39     padding: 10px 5px;
40     border-top: 1px solid #ccc;
41     text-align: right;
42 }
43 </style>

 

在Dialog.vue 组件中发现<slot></slot> 元素有name属性,这个属性就是配置插槽的名称,称为具名插槽

作用是如果一个模板中需要用到多个slot,此时如果使用这个模板的地方如果配置不明确,都以匿名插槽进行渲染,所以,如果我们需要在合理的,地方渲染,使用具名插槽

slot='header'指的是具名插槽<slot name='header'>的渲染位置

橙色文字部分是匿名插槽的渲染位置

slot="footer"指的是具名插槽<slot name='footer'>的渲染位置

插槽_数据_04

 

 3.作用域插槽

什么是作用域插槽,其实就是在父组件中修改子组件的插槽的后备内容

组件Dialog.vue的内容

Dialog.vue

 

 1 <template>
 2     <div>
 3         <div class="box">
 4         <header>
 5         <slot name="header">提示</slot>
 6         </header>
 7         <main>
 8        <slot :list="list">
 9        <div v-for="(item,index) in list" :key=index>
10        {{item.name}}
11        </div>
12        </slot>
13         </main>
14         </div>
15     </div>
16 </template>
17 
18 <script>
19     export default {
20 props:{
21     list:{
22         type:Array,
23         default:()=>{
24             return []
25         }
26     }
27 }
28     }
29 </script>
30 
31 <style  scoped>
32 .box{
33     width: 60%;
34 background: #fff;
35 box-shadow: 0px 0px 3px 2px rgba(0, 0, 0,0.4);
36 position: fixed;
37 top: 50%;
38 left: 50%;
39 transform: translate(-50%,-50%);
40 border-radius: 4px;
41 }
42 main{
43 margin: 5px;
44 }
45 header{
46     padding: 10px 5px ;
47     border-bottom: 1px solid #ccc;
48 }
49 footer{
50     padding: 10px 5px;
51     border-top: 1px solid #ccc;
52     text-align: right;
53 }
54 </style>

list是通过props接收的数组内容list是通过props接收的数组内容

 

App.vue

 1 <template>
 2   <div>
 3     <Dialog :list="arr" >
 4    
 5     </Dialog>
 6   </div>
 7 </template>
 8 
 9 <script>
10 import Dialog from "./components/Dialog.vue"
11 
12   export default {
13     components:{
14       Dialog,
15      
16     },data(){
17       return{
18         arr:[
19           { name: "苹果 ",price: 5,num: 10},
20           {name: "香蕉 ",price: 6,num: 114},
21           {name: "橘子 ",price: 5,num: 55},
22           {name: "葡萄 ",price: 5,num: 142}
23         ]
24       }}
25     }
26 
27   
28 </script>
29 
30 <style  scoped>
31 
32 </style>

 

data中的数据为父组件传进来的props内容

 此时页面的渲染为

 插槽_数组_05

 

 

 

接下来我们要通过作用域插槽去修改传到Dialog.vue的内容

此时我们要在父组件中通过子组件传出的:list内容进行修改

 

父组件的调用

1 <template>
2   <div>
3     <Dialog :list="arr" v-slot="list">
4       <div v-for="(item,index) in list.list" :key="index">
5       {{item.name}}的价格为{{item.price}}
6       </div>
7     </Dialog>
8   </div>
9 </template>

 

 通过v-slot指令进行渲染和修改Dialog组件插槽后备内容

 插槽_数据_06

 

 

上面的代码修改的是匿名插槽的内容,如果有多个插槽都需要修改,就得template进行辅助,然后具名修改

Dialog.vue组件的插槽

 

 1 <template>
 2     <div>
 3         <div class="box">
 4         <header>
 5         <slot name="header">提示</slot>
 6         </header>
 7         <main>
 8        <slot :list="list">
 9        <div v-for="(item,index) in list" :key="index">
10        {{item.name}}
11        </div>
12        </slot>
13        <slot name="price" :price="list">
14        <div v-for="(item,index) in list" :key="index+10">
15        价格{{item.price}}
16        </div>
17        </slot>
18         </main>
19         </div>
20     </div>
21 </template>
22 
23 <script>
24     export default {
25 props:{
26     list:{
27         type:Array,
28         default:()=>{
29             return []
30         }
31     }
32 }
33     }
34 </script>
35 
36 <style  scoped>
37 .box{
38     width: 60%;
39 background: #fff;
40 box-shadow: 0px 0px 3px 2px rgba(0, 0, 0,0.4);
41 position: fixed;
42 top: 50%;
43 left: 50%;
44 transform: translate(-50%,-50%);
45 border-radius: 4px;
46 }
47 main{
48 margin: 5px;
49 }
50 header{
51     padding: 10px 5px ;
52     border-bottom: 1px solid #ccc;
53 }
54 footer{
55     padding: 10px 5px;
56     border-top: 1px solid #ccc;
57     text-align: right;
58 }
59 </style>

通过动态属性传出对应的作用域内容

App.vue

 1 <template>
 2   <div>
 3     <Dialog :list="arr">
 4       <template v-slot:default="list">
 5         <div v-for="(item,index) in list.list" :key="index">
 6         水果名称:{{item.name}}        
 7         </div>
 8       </template>
 9        <template v-slot:price="price">
10         <div v-for="(item,index) in price.price" :key="index+10">    //index+10是为了防治key值重复导致报错
11         水果价格:{{item.price+2}}    //所有水果价格加2        
12         </div>
13       </template>
14     </Dialog>
15   </div>
16 </template>
17 
18 <script>
19 import Dialog from "./components/Dialog.vue"
20 
21   export default {
22     components:{
23       Dialog,
24      
25     },data(){
26       return{
27         arr:[
28           { name: "苹果 ",price: 5,num: 10},
29           {name: "香蕉 ",price: 6,num: 114},
30           {name: "橘子 ",price: 5,num: 55},
31           {name: "葡萄 ",price: 5,num: 142}
32         ]
33       }}
34     }
35 
36   
37 </script>
38 
39 <style  scoped>
40 
41 </style>

父组件通过template分别进行接收

注意:default代表的是匿名插槽的名称

插槽也有自己的语法糖就是#

 

 1 <template>
 2   <div>
 3     <Dialog :list="arr">
 4       <template #default="list">
 5         <div v-for="(item,index) in list.list" :key="index">
 6         水果名称:{{item.name}}        
 7         </div>
 8       </template>
 9        <template #price="price">
10         <div v-for="(item,index) in price.price" :key="index+10">
11         水果价格:{{item.price+2}}        
12         </div>
13       </template>
14     </Dialog>
15   </div>
16 </template>

插槽_作用域_07

 

 

和v-bind或者v-on一样v-slot也有自己的语法糖就是#

v-bind语法糖为“:”

v-on语法糖为“@”

v-slot语法糖为“#”