vue的插槽

vue官网的插槽例子,现记录熟悉一下:

一、插槽内容

父组件:

 1 <template>
 2   <div>
 3     <navigation-link url="/profile">
 4       Your Profile
 5     </navigation-link>
 6     <br>
 7     <navigation-link url="/profile">
 8       <Icon type="ios-checkmark" size="16"/>
 9       <span>Your Profile</span>
10     </navigation-link>
11   </div>
12 </template>
13 <script>
14   import NavigationLink from '@/components/NavigationLink';
15   export default {
16     components:{
17       NavigationLink
18     },
19     data(){
20       return {
21       }
22     }
23   }
24 </script>

子组件:

 1 <template>
 2   <a :href="url" class="nav-link">
 3     <slot></slot>
 4   </a>
 5 </template>
 6 <script>
 7     export default {
 8         name: "NavigationLink",
 9         props: {
10           href:{
11             type:String,
12             default:''
13           }
14         },
15     }
16 </script>

渲染的页面:

vue的插槽

上面的代码可以看出,slot就是把组件NavigationLink起始标签之间的内容显示出来,如果子组件没有包含<slot></slot>元素,NavigationLink起始标签之间的内容将没有任何意义

二、编译作用域

 1 <template>
 2   <div>
 3     <navigation-link url="/profile">
 4       以用户名{{ user.name }}登录
 5     </navigation-link>
 6     <br>
 7     <navigation-link url="/profile">
 8       点击此处将发送到: {{ url }}
 9       <!--这里的 `url` 会是 undefined,因为 "/profile" 是_传递给_ <navigation-link> 的
10       而不是在 <navigation-link> 组件内部定义的-->
11     </navigation-link>
12   </div>
13 </template>
14 <script>
15   import NavigationLink from '@/components/NavigationLink';
16   export default {
17     components:{
18       NavigationLink
19     },
20     data(){
21       return {
22         user:{
23           name:'admin'
24         }
25       }
26     }
27   }
28 </script>

渲染的页面:

vue的插槽

规则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的

三、后备内容

以字面理解,就是替补的意思,正式球员在的话,就是替补,正式球员不在,替补上场,同样,组件标签之间如果提供内容,就会渲染提供的内容,否则渲染后备(默认的)内容,后备内容在子组件中设置,与父子传参的子组件props接收父组件传的参数设置默认值有些相似:

父组件:

 1 <template>
 2   <div>
 3     <submit-button></submit-button>
 4   </div>
 5 </template>
 6 <script>
 7   import SubmitButton from '@/components/SubmitButton';
 8   export default {
 9     components:{
10       SubmitButton
11     },
12     data(){
13       return {
14       }
15     }
16   }
17 </script>

子组件:

 1 <template>
 2   <div>
 3     <button type="submit">
 4       <slot>Submit</slot>
 5     </button>
 6   </div>
 7 </template>
 8 <script>
 9     export default {
10         name: "SubmitButton"
11     }
12 </script>

渲染页面:

vue的插槽

四、具名插槽

在一个组件里面,有时我们需要多个插槽,如何区分并正确出我们想要的页面,这时需要用到具名插槽

父组件

 1 <template>
 2   <div>
 3     <base-layout>
 4       <template v-slot:header>
 5         <h1>这是一篇文章标题</h1>
 6       </template>
 7 
 8       <!--任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容并显示在不带slot的内容中-->
 9       <p>这是一篇文章内容</p>
10 
11       <template v-slot:footer>
12         <p>这是一些文章底部信息</p>
13       </template>
14     </base-layout>
15   </div>
16 </template>
17 <script>
18   import BaseLayout from '@/components/BaseLayout';
19   export default {
20     components:{
21       BaseLayout
22     },
23     data(){
24       return {
25       }
26     }
27   }
28 </script>

子组件:

 1 <template>
 2   <div class="container">
 3     <header>
 4       <slot name="header"></slot>
 5     </header>
 6     <main>
 7       <slot></slot>
 8       <!--一个不带 name 的 <slot> 出口会带有隐含的名字“default”-->
 9     </main>
10     <footer>
11       <slot name="footer"></slot>
12     </footer>
13   </div>
14 </template>
15 <script>
16     export default {
17         name: "BaseLayout"
18     }
19 </script>

页面的渲染:

vue的插槽

如果父组件默认区域改为

1 <!--任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容并显示在不带slot的内容中-->
2 <p>这是一篇文章内容</p>
3 <!--<template> 中包裹默认插槽的内容,就只会显示这包裹的默认内容,上面的将不会显示-->
4 <template v-slot:default>
5    <p>这是一篇文章的另一个内容</p>
6 </template>

此时页面显示:

vue的插槽

有此可以看出,还是以配置的v-slot:default为准,配置了这个v-slot:default,父组件中没有包裹在带v-slot的template标签中的内容都会丢失

五、作用域插槽

子组件:

 1 <template>
 2     <span>
 3       <!--为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个特性绑定上去-->
 4       <slot v-bind:user="user">
 5         {{ user.lastName }}
 6       </slot>
 7     </span>
 8 </template>
 9 <script>
10     export default {
11         name: "CurrentUser",
12         data(){
13           return {
14             user:{
15               firstName:'Tom',
16               lastName:'Green'
17             }
18           }
19         }
20     }
21 </script>

子组件的属性值user.firstName想让父组件获取并覆盖掉插槽后备内容user.lastName,可以将user作为一个属性绑定到 <slot> 上,绑定在 <slot> 元素上的特性被称为插槽 prop。现在在父级作用域中,我们可以给 v-slot 带一个值来定义我们提供的插槽 prop 的名字:在示例中我们将包含所有插槽 prop 的对象命名为 slotProps

父组件:

 1 <template>
 2   <div>
 3     <current-user>
 4       <template v-slot:default="slotProps">
 5         {{ slotProps.user.firstName }}
 6       </template>
 7     </current-user>
 8   </div>
 9 </template>
10 <script>
11   import CurrentUser from '@/components/CurrentUser';
12   export default {
13     components:{
14       CurrentUser
15     },
16   }
17 </script>

页面显示:

vue的插槽

  五(一)、独占默认插槽的缩写写法

  在上述示父组件情况下,当被提供的内容只有默认插槽时,组件的标签current-user才可以被当作插槽的模板template来使用。这样我们就可以把 v-slot 直接用在组件上:

1 <current-user v-slot:default="slotProps">
2   {{ slotProps.user.firstName }}
3 </current-user>

  这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot 被假定对应默认插槽

1 <current-user v-slot="slotProps">
2   {{ slotProps.user.firstName }}
3 </current-user>

  说白了,就是父组件里面的写法可以精简为最后那种写法,实现的效果是一样的,只就是缩写写法,但是需要注意的是一旦出现具名插槽,就不能用缩写写法,就要老老实实写完整的基于 <template> 的语法,每个插槽都要指明所用插槽的名称

  五(二)、解构插槽Prop

  父组件:

 1 <template>
 2   <div>
 3     <!--解构user-->
 4     <current-user v-slot="{ user }">
 5       {{ user.firstName }}
 6     </current-user>
 7     <!--将person重命名为people-->
 8     <current-user v-slot="{ person: people }">
 9       {{ people.lastName }}
10     </current-user>
11     <!--以下的写法直接编译报错-->
12     <current-user v-slot="{ user = { firstName: 'Guest' } }">
13       {{ user.firstName }}
14     </current-user>
15     <!--改为这种写法,并不能输出值-->
16     <current-user #default="{ person = { firstName: 'Guest' } }">
17       {{ person.firstName }}
18     </current-user>
19   </div>
20 </template>
21 <script>
22   import CurrentUser from '@/components/CurrentUser';
23   export default {
24     components:{
25       CurrentUser
26     },
27   }
28 </script>

子组件:

 1 <template>
 2     <span>
 3       <slot v-bind:user="user" v-bind:person="person">
 4         {{ user.lastName }}
 5       </slot>
 6     </span>
 7 </template>
 8 <script>
 9     export default {
10         name: "CurrentUser",
11         data(){
12           return {
13             user:{
14               firstName:'Tom',
15               lastName:'Green'
16             },
17             person:{
18               lastName:'Smith'
19             }
20           }
21         }
22     }
23 </script>

官网给的覆盖后备内容的解构写法,用于插槽子组件的person的prop 是 undefined 的情形:

1 <current-user v-slot="{ user = { firstName: 'Guest' } }">
2   {{ user.firstName }}
3 </current-user>

编译报错,去网上百度解决方法,有人说改成简写形式

1 <current-user #default="{ user = { firstName: 'Guest' } }">
2   {{ user.firstName }}
3 </current-user>

确实不报错了,但是也不输出值'Guest',如果有哪位大佬有解决方法,欢迎评论

六、动态插槽名

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

1 <base-layout>
2   <template v-slot:[dynamicSlotName]>
3     ...
4   </template>
5 </base-layout>

父组件:

 1 <template>
 2   <div>
 3     <base-layout>
 4       <template v-slot:[header]="headerProp">
 5         <h1>{{headerProp.header.content}}</h1>
 6       </template>
 7       <template v-slot:[def]="defaultProp">
 8         <p>{{defaultProp.def.content}}</p>
 9       </template>
10       <template v-slot:[footer]="footerProp">
11         <p>{{footerProp.footer.content}}</p>
12       </template>
13     </base-layout>
14   </div>
15 </template>
16 <script>
17   import BaseLayout from '@/components/BaseLayout';
18   export default {
19     components:{
20       BaseLayout
21     },
22     data(){
23       return {
24         header:'header',
25         def:'default',
26         footer:'footer'
27       }
28     }
29   }
30 </script>

子组件:

 1 <template>
 2   <div class="container">
 3     <header>
 4       <slot name="header" v-bind:header="header"></slot>
 5     </header>
 6     <main>
 7       <slot v-bind:def="def"></slot>
 8     </main>
 9     <footer>
10       <slot name="footer" v-bind:footer="footer"></slot>
11     </footer>
12   </div>
13 </template>
14 
15 <script>
16     export default {
17         name: "BaseLayout",
18         data(){
19           return {
20             header:{
21               name:'header',
22               content:'这是标题'
23             },
24             def:{
25               name:'default',
26               content:'这是内容'
27             },
28             footer:{
29               name:'footer',
30               content:'这是底部信息'
31             }
32           }
33         }
34     }
35 </script>
36 
37 <style scoped>
38 
39 </style>

页面显示:

vue的插槽