首页 > 编程笔记 > JavaScript笔记
阅读:81
Vue slot(插槽)详解
在 Vue 中,组件是当作自定义的 HTML 元素来使用的,其元素可以包括属性和内容,通过组件定义的 prop 来接收属性值,那么组件的内容怎么实现呢?可以使用插槽(slot 元素)来解决。
在 Vue 实例中使用这个组件:
在 Chrome 浏览器中运行程序,渲染的结果如下图所示。

图 1 插槽的基本用法
如果 page 组件中没有 slot 元素,<page> 元素中的内容将不会渲染到页面。
有一条规则要记住:父组件模板中的所有内容都是在父级作用域中编译的,子组件模板中的所有内容都是在子作用域中编译的。

图 2 设置插槽的默认内容
在向命名插槽提供内容的时候,可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
然而,如果希望更明确一些,仍然可以在一个 <template> 中包裹默认命名插槽的内容:

图 3 命名插槽
与 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容(v-slot:)替换为字符 #。例如下面的代码:
在父级作用域下使用该组件时,可以给 v-slot 指令一个值来定义组件提供的插槽 prop 的名字。代码如下:
【实例】访问插槽的内容。

图 4 命名插槽

图 5 解构插槽prop
插槽的基本用法
下面定义一个组件:
vm.component('page', {
template:'<div><slot></slot></div>'
});
在 page 组件中,div 元素内容定义了 slot 元素,可以把它理解为占位符。在 Vue 实例中使用这个组件:
<div id="app">
<page>如今直上银河去,同到牵牛织女家。</page>
</div>
page 元素给出了内容,在渲染组件时,这个内容会置换组件内部的 <slot> 元素。在 Chrome 浏览器中运行程序,渲染的结果如下图所示。

图 1 插槽的基本用法
如果 page 组件中没有 slot 元素,<page> 元素中的内容将不会渲染到页面。
编译作用域
当想通过插槽向组件传递动态数据时,例如:
<page>欢迎来到{{name}}的官网</page>
代码中,name 属性是在父组件作用域下解析的,而不是 page 组件的作用域。在 page 组件中定义的属性,在父组件中是访问不到的,这就是编译作用域。有一条规则要记住:父组件模板中的所有内容都是在父级作用域中编译的,子组件模板中的所有内容都是在子作用域中编译的。
默认内容
有时为一个插槽设置默认内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button> 组件中:
<button type="submit">
<slot></slot>
</button>
如果希望这个 <button> 内绝大多数情况下都渲染文本“Submit”,可以将“Submit”作为默认内容放在 <slot> 标签内:
<button type="submit">
<slot>Submit</slot>
</button>
现在在一个父组件中使用 <submit-button>,并且不提供任何插槽内容:<submit-button></submit-button>默认内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
但是如果提供内容:
<submit-button> 提交 </submit-button>则这个提供的内容将会替换掉默认值 Submit,渲染如下:
<button type="submit">
提交
</button>
【实例】设置插槽的默认内容。
<div id="app">
<page>流年莫虚掷,华发不相容。</page>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
//创建一个应用程序实例
const vm= Vue.createApp({ });
vm.component('page', {
template:`<button type="submit">
<slot>Submit</slot>
</button>
'
});
//在指定的DOM元素上装载应用程序实例的根组件
vm.mount('#app');
</script>
在 Chrome 浏览器中运行程序,渲染的结果如下图所示。
图 2 设置插槽的默认内容
命名插槽
在组件开发中,有时需要使用多个插槽。例如对于一个带有如下模板的 <page-layout> 组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot> 元素有一个特殊的特性 name,它用来命名插槽。因此,可以定义多个名字不同的插槽,例如下面的代码:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name 的 <slot> 元素,它有默认的名字“default”。在向命名插槽提供内容的时候,可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
<page-layout>
<template v-slot:header>
<h1>这里有一个页面标题</h1>
</template>
<p>这里有一段主要内容</p>
<p>和另一段主要内容</p>
<template v-slot:footer>
<p>这是一些联系方式</p>
</template>
</page-layout>
现在 <template> 元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。然而,如果希望更明确一些,仍然可以在一个 <template> 中包裹默认命名插槽的内容:
<page-layout>
<template v-slot:header>
<h1>这里有一个页面标题</h1>
</template>
<template v-slot:default>
<p>这里有一段主要内容</p>
<p>和另一段主要内容</p>
</template>
<template v-slot:footer>
<<p>这是一些联系方式</p>
</template>
</page-layout>
上面两种写法都会渲染出如下代码:
<div class="container">
<header>
<h3>这里有一个页面标题</h3>
</header>
<main>
<p>这里有一段主要内容</p>
<p>和另一段主要内容</p>
</main>
<footer>
<p>这是一些联系方式</p>
</footer>
</div>
【实例】命名插槽。
<div id="app">
<page-layout>
<template v-slot:header>
<h2 align='center'>书河上亭壁</h2>
</template>
<template v-slot:main>
<h3>岸阔樯稀波渺茫,独凭危槛思何长。</h3>
<h3>萧萧远树疏林外,一半秋山带夕阳。</h3>
</template>
<template v-slot:footer>
<p align='right'>经典古诗</p>
</template>
</page-layout>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
//创建一个应用程序实例
const vm= Vue.createApp({ });
vm.component('page-layout', {
template:`
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot name="main"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
});
//在指定的DOM元素上装载应用程序实例的根组件
vm.mount('#app');
</script>
在 Chrome 浏览器中运行程序,效果如下图所示。
图 3 命名插槽
与 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容(v-slot:)替换为字符 #。例如下面的代码:
<page-layout>
<template #header>
<h1>这里有一个页面标题</h1>
</template>
<template #main>
<p>这里有一段主要内容</p>
<p>和另一段主要内容</p>
</template>
<template #footer>
<<p>这是一些联系方式</p>
</template>
</page-layout>
作用域插槽
在父级作用域下,在插槽的内容中是无法访问子组件的数据属性的,但有时需要在父级的插槽内容中访问子组件的属性,我们可以在子组件的 <slot> 元素上使用 v-bind 指令绑定一个 prop 属性。看下面的组件代码:
vm.component('page-layout', {
data:function(){
return{
info:{
name:'小明',
age:18,
sex:"男"
}
}
},
template:`
<button>
<slot v-bind:values="info">
{{info.name}}
</slot>
</button>
`
});
这个按钮可以显示 info 对象中的任意一个,为了让父组件可以访问 info 对象,在 <slot> 元素上使用 v-bind 指令绑定一个 values 属性,称为插槽 prop,这个 prop 不需要在 props 选项中声明。在父级作用域下使用该组件时,可以给 v-slot 指令一个值来定义组件提供的插槽 prop 的名字。代码如下:
<page-layout>
<template v-slot:default="slotProps">
{{slotProps.values.name}}
</template>
</page-layout>
因为 <page-layout> 组件内的插槽是默认插槽,所以这里使用其默认的名字 default,然后给出一个名字 slotProps,这个名字可以随便取,代表的是包含组件内所有插槽 prop 的一个对象,然后就可以在父组件中利用这个对象访问子组件的插槽 prop,prop 是绑定到 info 数据属性上的,所以可以进一步访问 info 的内容。示例代码如下。【实例】访问插槽的内容。
<div id="app">
<page-layout>
<template v-slot:default="slotProps">
{{slotProps.values.city}}
</template>
</page-layout>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
//创建一个应用程序实例
const vm= Vue.createApp({ });
vm.component('page-layout', {
data:function(){
return{
info:{
name:'苹果',
price:8.86,
city:"深圳"
}
}
},
template:`
<button>
<slot v-bind:values="info">
{{info.city}}
</slot>
</button>
`
});
//在指定的DOM元素上装载应用程序实例的根组件
vm.mount('#app');
</script>
在 Chrome 浏览器中运行程序,效果如下图所示。
图 4 命名插槽
解构插槽prop
作用域插槽的内部工作原理是将插槽内容传入函数的单个参数中:
function (slotProps) {
// 插槽内容
}
这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下(单文件组件或现代浏览器),也可以使用 ES 6 解构来传入具体的插槽 prop,示例代码如下:
<current-verse v-slot="{ verse }">
{{ verse.firstContent }}
</current-user>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其他可能,例如将 verse 重命名为 poetry:
<current-verse v-slot="{ verse: poetry }">
{{ poetry.firstContent }}
</current-verse>
甚至可以定义默认的内容,用于插槽 prop 是 undefined 的情形:
<current-verse v-slot="{ verser = { firstContent: '古诗' } }">
{{ verse.Content}}
</current-verser>
【实例】解构插槽prop。
<div id="app">
<current-verse>
<template v-slot="{verse:poetry}">
{{poetry.firstContent }}
</template>
</current-verse>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
//创建一个应用程序实例
const vm= Vue.createApp({ });
vm.component('currentVerse', {
template: '
<span><slot :verse="verse">{{ verse.lastContent }}</slot></span>',
data:function(){
return {
verse: {
firstContent: '此心随去马,迢递过千峰。',
secondContent: '野渡波摇月,空城雨翳钟。'
}
}
}
});
//在指定的DOM元素上装载应用程序实例的根组件
vm.mount('#app');
</script>
在 Chrome 浏览器中运行程序,效果如下图所示。
图 5 解构插槽prop
ICP备案:
公安联网备案: