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>
渲染的页面:
上面的代码可以看出,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>
渲染的页面:
规则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的
三、后备内容
以字面理解,就是替补的意思,正式球员在的话,就是替补,正式球员不在,替补上场,同样,组件标签之间如果提供内容,就会渲染提供的内容,否则渲染后备(默认的)内容,后备内容在子组件中设置,与父子传参的子组件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>
渲染页面:
四、具名插槽
在一个组件里面,有时我们需要多个插槽,如何区分并正确出我们想要的页面,这时需要用到具名插槽
父组件
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>
页面的渲染:
如果父组件默认区域改为
1 <!--任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容并显示在不带slot的内容中--> 2 <p>这是一篇文章内容</p> 3 <!--<template> 中包裹默认插槽的内容,就只会显示这包裹的默认内容,上面的将不会显示--> 4 <template v-slot:default> 5 <p>这是一篇文章的另一个内容</p> 6 </template>
此时页面显示:
有此可以看出,还是以配置的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>
页面显示:
五(一)、独占默认插槽的缩写写法
在上述示父组件情况下,当被提供的内容只有默认插槽时,组件的标签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>
页面显示: