组件三大API之三: slot

  • <slot>标签
  • v-slot指令
  • 普通插槽
  • 有默认值的插槽
  • 具名插槽
  • 作用域插槽

v-slotVue 2.6.0引入的一个新语法指令,目的是统一之前slot / slot-scope的用法。统一的指令语法更简洁也更易理解。

之前讲解的prop实现了组件向下的数据传递,而eventv-on / $emit可以实现组件向上的数据传递。这一节v-slot指令实现组件内容的自定义功能。

一个简单的例子,自定义一个按钮组件:

<div id="app">
<p>this is example for slot</p>
<custom-button></custom-button>
</div
const customButton = Vue.extend({
template: `<button>提交</button>`,
}) const vm = new Vue({
el: "#app",
components: {
customButton,
},
})

此时这个自定义按钮注定是一个提交按钮,因为我们把按钮上的文字固定写死了”提交“。如果我们需要根据按钮使用的场景不同,显示不同的文字,比如可以是提交、删除、确认、返回等。此时我们就可以使用组件<slot>标签,它相当于组件里面内容的占位符。

普通插槽

上面的例子我们只需要在组件定义时,在按钮文字的地方用<slot><slot>标签代表即可

const customButton = Vue.extend({
template: `<button>
<slot></slot>
</button>`,
})

然后在使用组件时提供提供内容即可

<div id="app">
<p>this is example for slot</p>
<custom-button>提交</custom-button>
<custom-button>删除</custom-button>
<custom-button>返回</custom-button>
</div

此时自定义按钮上要显示什么文字完成由组件使用时决定,而不是组件定义时决定。

有默认值的插槽

需求升级,我们希望组件在调用时没有写入文字时,默认显示”确认“。此时可以在组件<slot>标签定义时提供一个内容文字,将作用为组件内容缺省时的显示。

const customButton = Vue.extend({
template: `<button>
<slot>确认</slot>
</button>`,
})
div id="app">
<p>this is example for slot</p>
<!-- 有输入内容显示输入的内容:提交 -->
<custom-button>提交</custom-button>
<!-- 没有输入内容时默认显示定义时文字:确认 -->
<custom-button></custom-button>
</div

具名插槽

上面自定义按钮组件只提供了一个占位插槽,但需要抽象为组件的内容各式各样,比如一个段落内容组件,包括自定义标题、段落正文、写作时间三部分。按分析这个组件至少得分三部分,而且三部分的内容也只有在组件使用时才知道输入什么。

所以我们定义组件时,提供三个占位插槽,一个是用于标题、一个用于显示时间,其它内容都作为正文

const customSection = Vue.extend({
template: `<section>
<header>
<slot name="title"></slot>
</header> <main>
<slot></slot>
</main> <footer>
<slot name="time"></slot>
<footer>
</section>`,
})
<custom-section>
<template v-slot:title>段落标题</template>
<template>这是段落正文部分,内容很长,这里就省略了......</template>
<template v-slot:time>2019-5-26</template>
</custom-section>

语法很简单:

  • 组件定义时:<slot>标签的时候指定一个name属性值即可
  • 组件使用时,<template>标签中用v-slot指令参数指明具体的名称,表示当前模板内容用于哪个占位符的slot即可。

在拥有多个具名插槽时,组件调用时写入插槽的内容需要用<template>标签包裹

未命名的默认插槽,有一个内部自带的名称default

<!-- 定义时 -->
<main><slot></slot></main>
<main><slot name="defalut"></slot></main>
<!-- 使用时 -->
<template>这是段落正文部分,内容很长,这里就省略了......</template>
<template v-slot:default>这是段落正文部分,内容很长,这里就省略了......</template>

动态选择插槽

子组件内可以定义多组插槽,外部引用时根据条件决定显示调用哪个具名插槽,相当于一个switch语句。

<div id="app">
<button @click="changeSlot">点击切换slot</button>
<custom-section>
<template v-slot:[variable]>改变了插槽</template>
</custom-section>
</div>
const customSection = Vue.extend({
template: `<article>
<slot name="js">this is content for js</slot>
<slot name="html">this is content for html</slot>
<slot name="css">this is content for css</slot>
</article>`,
})
const vm = new Vue({
el: "#app",
components: {
customSection
},
data: {
index: 0,
arr: ['html', 'css', 'js'],
variable: 'html'
},
methods: {
changeSlot() {
this.variable = this.arr[++this.index % 3]
}
}
})

作用域插槽

这里有一个域的概念,在官方文档中有一句话:

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

简单来说:

插槽内容虽然被子组件标签包裹,但实际上插槽跟子组件标签一样都属性于父级模板作用域。可以直接引用父组件作用域内的数据。但却不能直接引用子模板作用域内的数据。

const customSection = Vue.extend({
template: `<section>
<header>
<slot name="title"></slot>
</header> <main>
<slot></slot>
</main> <footer>
<slot name="time"></slot>
<footer>
</section>`,
data:() => {
return {
innerTitle: '写在子级的标题'
}
}
}) const vm = new Vue({
el: "#app",
data: {
outerTitle: '写在父级的标题'
},
components: {
customSection,
},
})

上面代码,我们在根组件vm中定义了标题outerTitle: '写在父级的标题',也在子组件custom-section定义了标题innerTitle: '写在子级的标题'

<div id="app">
<p>这里可以直接引用父级标题:{{ outerTitle }}</p>
<custom-section>
<!-- 在插槽模板内也可以使用父级作用域内的数据的 -->
<template v-slot:title>{{ outerTitle }}</template>
<!-- <template v-slot:title>段落标题</template> -->
<template>这是段落正文部分,内容很长,这里就省略了......</template>
<template v-slot:time>2019-5-26</template>
</custom-section>
</div>
<div id="app">
<p>这里是肯定不可以直接引用子级标题:{{ innerTitle }}</p>
<custom-section>
<!-- 在插槽也不能直接引用子组件内的数据,控制台会报错!!! -->
<template v-slot:title>{{ innerTitle }}</template>
<!-- <template v-slot:title>段落标题</template> -->
<template>这是段落正文部分,内容很长,这里就省略了......</template>
<template v-slot:time>2019-5-26</template>
</custom-section>
</div>

但插槽的内容最终是显示在子组件中的,所以很多时候需要用子组件内部的数据来进行操作。此时就需要在定义插槽时将子组件内的数据向上传递,让调用插槽时可以使用。

const customSection = Vue.extend({
template: `<section>
<header>
// <slot name="title"></slot>
<slot name="title" :title="innerTitle"></slot>
</header> <main>
<slot></slot>
</main> <footer>
<slot name="time"></slot>
</footer>
</section>`,
data:() => {
return {
innerTitle: '写在子级的标题'
}
}
})
 <div id="app">
<p>这里可以直接引用父级标题:{{ outerTitle }}</p>
<custom-section>
<!-- <template v-slot:title>段落标题</template> -->
<template v-slot:title="slotProp">{{ slotProp.title }}</template>
<template>这是段落正文部分,内容很长,这里就省略了......</template>
<template v-slot:time>2019-5-26</template>
</custom-section>
</div>

看到,在定义时<slot>标签内使用v-bind绑定子组件内需要传递的值

<slot name="title" :title="innerTitle"></slot>

在插槽调用时,通过v-slot的值来的接收后就可以使用了。

<template v-slot:title="slotProp">{{ slotProp.title }}</template>

并且v-slot的值接收过来是一个对象形式,定义时v-bind可以绑定多个,在v-slot的值中以键值对接收,使用时对象打点调用。

<slot name="title" :title="innerTitle" :author="author"></slot>
<template v-slot:title="slotProp">{{ slotProp.title + '-' + slotProp.author }}</template>

插槽prop的解构

当然如果不想使用对象打点调用的方式,也可以使用ES6对象解构的语法调用。

<slot name="title" :title="innerTitle" :author="author"></slot>
<template v-slot:title="{ title, author }">{{ title + '-' + author }}</template>

插槽prop的重命名

如果传递上来的prop可能跟父级作用域引用的变量重名,可以在解构时重命名

<slot name="title" :title="innerTitle" :author="author"></slot>
<template v-slot:title="{ title, author:writer }">{{ title + '-' + writer }}</template>

插槽prop的默认值

也可以写一个默认内容,避免接收的prop是undefined情况

<slot name="title" :title="innerTitle" :author="author"></slot>
<template v-slot:title="{ title, author='anonymity'}">{{ title + '-' + author }}</template>

插槽prop的就是函数参数

在官方文档中说明,插槽内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里,所以ES6函数参数的新语法都可以使用,如解构、参数默认值等

function (slotProps) {}
function ({title, author}) {}
function ({title, author=anonymity}) {
// 插槽内容
}

v-slot的简写#

v-bind简写成:v-on简写成@一样,v-slot简写@

当采用简写#后面必须接一个插槽名称,当#绑定默认插槽时,需要写成#default

// 可以
<template v-slot="slotProp"></template>
// 错误
<template #="slotProp"></template>
// 改成
<template #defalut="slotProp"></template>

独占默认插槽时,v-slot可以绑定在子组件标签上,省略template

<div id="app">
<p>this is example for slot</p>
<custom-button v-slot={type}>{{ type ? '确定' : '删除'}}</custom-button>
<custom-button #default={type}>{{ !type ? '确定' : '删除'}}</custom-button>
</div
const customButton = Vue.extend({
template: `<button><slot :type="btnType"></slot></button>`,
data() => {
return {
btnType: 1, // 1 确定 0 删除
}
}
}) const vm = new Vue({
el: "#app",
components: {
customButton,
},
})

总结

v-slot:slotName="slotProp"
#:slotName="slotProp"

vue-learning:29 - component - 组件三大API之三:slot的更多相关文章

  1. vue-learning:26 - component - 组件三大API之一:prop

    组件三大API之一: prop prop的大小写 prop接收类型 字符串数组形式 对象形式: type / required / default / validator prop传递类型: 静态传递 ...

  2. vue-learning:27 - component - 组件三大API之二:event

    组件三大API之二: event 在上一节中讲到prop单向下行数据绑定的特征,父组件向子组件传值通过prop实现,那如果有子组件需要向父组件传值或其它通信请求,可以通过vue的事件监听系统(触发事件 ...

  3. Vue Login Form Component

    Vue Login Form Component Account Login <template> <div> <slot></slot> <el ...

  4. 第六章 组件 59 组件切换-使用Vue提供的component元素实现组件切换

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  5. Vue.js——60分钟组件快速入门(下篇)

    概述 上一篇我们重点介绍了组件的创建.注册和使用,熟练这几个步骤将有助于深入组件的开发.另外,在子组件中定义props,可以让父组件的数据传递下来,这就好比子组件告诉父组件:"嘿,老哥,我开 ...

  6. Vue.js——60分钟组件快速入门

    一.组件简介 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树: 那么什么是组件呢?组件可以扩展HT ...

  7. Vue.js——60分钟组件快速入门(下篇)

    转自:https://www.cnblogs.com/keepfool/p/5637834.html 概述 上一篇我们重点介绍了组件的创建.注册和使用,熟练这几个步骤将有助于深入组件的开发.另外,在子 ...

  8. Vue:Vue的介绍以及组件剖析

    介绍 现在,随着基于JavaScript的单页应用程序(SPA)和服务器端渲染(SSR)的兴起,可以用JavaScript编写整个前端应用程序,并整洁地管理和维护该应用程序的前端代码.诸如Angula ...

  9. Vue 引出声明周期 && 组件的基本使用

    1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 & ...

随机推荐

  1. Directx11教程(17) D3D11管线(6)

    原文:Directx11教程(17) D3D11管线(6)       VS shader输出clip空间的顶点位置及参数信息(比如颜色)到一个FIFO中,之后PA(primitive assembl ...

  2. javascript如何将时间戳转为24小时制

    var now = new Date(parseInt(1446634507) * 1000);console.log(now.toLocaleString('chinese',{hour12:fal ...

  3. docker 常用的命令

    1.运行容器 sudo docker run -d -t -p : --name demo ubuntu:16.04 2.删除容器 sudo docker rm -f demo 3.在容器中安装必备软 ...

  4. PHPCMS快速建站系列之常用标签

    <span class="Nmore"><a href="/index.php?m=content&c=index&a=lists&am ...

  5. Python内存机制简介

    1: 变量不是盒子,应该把变量视作便利贴.变量只不过是标注,所以无法阻止为对象贴上多个标注.标注就是别名: >>> a = [1, 2, 3] >>> b = a ...

  6. SWF在线绘本批量制作高质量PDF的新方法(重点在批量制作)

    SWF在线绘本批量制作高质量PDF的新方法(重点在批量制作) 2012-12-21  未来决定...       http://www.ebama.net/thread-107643-1-1.html ...

  7. Oracle 11g Pivot函数实现行转列

    先上语法规范: SELECT .... FROM <table-expr> PIVOT ( aggregate-function(<column>) FOR <pivot ...

  8. C++运行时类型识别

    通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型. 通过下面两个操作符提供 RTTI: 1. typeid 操作符,返回指针或引用所指对象的实际类 ...

  9. <Mysql必知必会> ---- 笔记

    转载自  https://www.jianshu.com/p/294502893128 挺基础的mysql的书籍,基本上都是如何操作的语法. 第1章 了解SQL 主键(primary key):能够唯 ...

  10. wepy —— 组件之间通信

    一.props 1.静态传值 —— 父组件向子组件传递常量数据 // 父组件 <coma fruitName="橘子"></coma> // 子组件 // ...