vue2.x学习笔记(十六)
接着前面的内容:https://www.cnblogs.com/yanggb/p/12616543.html。
组件中的插槽
在2.6.0的版本中,vue为具名插槽和作用域插槽引入了一个新的统一的语法(即【v-slot】指令),它取代了【slot】和【slot-scope】这两个目前已经被废弃但是还没被移除且仍在文档中的attribute。
插槽内容
vue实现了一套内容分发的api,这套api的设计灵感来源于web components的规范草案,将<slot>元素作为承载分发内容的出口。
它允许你像这样合成组件:
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在<navigation-link>的模板中可能会写为:
<a v-bind:href="url" class="nav-link">
<slot></slot>
</a>
当这个组件被渲染的时候,<slot></slot>就会被替换为Your Profile。这里的<slot></slot>就是插槽,插槽内可以包含任何模板代码,包括html:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
甚至其他组件:
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果<navigation-link>组件没有包含一个<slot>元素的话,则该组件起始标签和结束标签之间的任何内容都会被抛弃,因为它们没有能够找到对应的插槽进行内容替换(分发)。
插槽的编译作用域
当你想要在一个插槽中使用数据的时候,比如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
该插槽跟模板的其他地方一样,都可以访问相同的实例属性(也就是和父级模板相同的作用域),而不能访问<navigation-link>的作用域。例如下例中的url是访问不到的:
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的url会是undefined,因为"/profile"是
传递给<navigation-link>的而不是
在<navigation-link>组件的内部定义的。
-->
</navigation-link>
请记住一条规则:父级模板里的所有内容都是在父级作用域中编译的,而子模板里的所有内容都是在子作用域中编译的。
后备内容
有的时候为一个插槽设置具体的后备(也就是默认的)内容是十分有用的,这些后备内容只会在没有提供内容的时候被渲染。
例如在一个<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>
而当我们提供内容的时候,这个提供的内容就会被渲染,从而代替后备的内容。
具名插槽
关于具名插槽的内容自2.6.0起有所更新,这里是官方文档日前的最新语法,对于旧的语法不做理会。
有的时候我们可能会需要多个插槽,例如对于一个带有如下模板的<base-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:插槽名】指令的参数形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template> <p>A paragraph for the main content.</p>
<p>And another one.</p> <template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
现在<template>元素中的所有内容都会被传入到相应的插槽中。任何没有被包裹在带有【v-slot】指令的<template>中的内容,都会被视为默认插槽的内容。
虽然机制是这样的,但是为了让页面逻辑更明确一些,依然可以在一个<template>中包裹默认插槽的内容。
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template> <template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template> <template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
任何一种写法都会渲染出:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
要注意的是,【v-slot】指令只能添加在<template>上(只有一种例外情况,在后面的作用域插槽会说明),这一点和已经废弃的【slot】属性不同。
作用域插槽
关于作用域插槽的内容自2.6.0起有所更新,这里是官方文档日前的最新语法,对于旧的语法不做理会。
有的时候让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的<current-user>组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我们可能会想要换掉备用的内容,用名而非姓来显示,如下:
<current-user>
{{ user.firstName }}
</current-user>
然而上面的代码并不会如期望的那样工作,因为只有<current-user>组件才可以访问到user对象,而我们提供的插槽内容实在父级模板中渲染的。
因此,为了让user对象能够在父级组件的插槽内容中可用,我们需要将user对象作为<slot>元素的一个绑定属性:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
绑定在<slot>元素上的属性就被称为插槽prop。现在在父级的作用域中,我们就可以使用带值的【v-slot】指令来定义我们提供的插槽prop的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
在这个例子中,我们选择将包含所有插槽prop的对象命名为slotProps,但你也可以使用任意你喜欢的名字。
独占默认插槽的缩写语法
在上述的情况下,当被提供的内容只有默认插槽的时候,组件的标签是可以被当做插槽的模板来使用的,这样我们就可以把【v-slot】指令直接用在组件上而不需要包裹一层<template>:
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
这种写法还可以更简单,就像假定未知名的内容对应默认插槽一样,不带参数的【v-slot】指令也被假定对应默认插槽:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
但是要注意,默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<!-- 无效,会导致警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
建议还是依照规范去写而非使用缩写,这样能有效避免一些不必要的错误或麻烦。因此在存在多个插槽的情况下,请始终为所有的插槽使用完整的基于<template>的语法:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template> <template v-slot:other="otherSlotProps">
...
</template>
</current-user>
结构插槽prop
作用域插槽的内部工作原理其实就是将你的插槽内容包括在一个传入单个参数的函数里:
function (slotProps) {
// 插槽内容
}
这就意味着,【v-slot】指令的值实际上可以是任何能够作为函数定义的参数的javascript表达式。所以在支持的环境下(单文件组件或现代浏览器),你都可以使用es205解构来传入具体的插槽prop,如下:
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
这样就能够使得模板更加简洁,尤其是在该插槽提供了多个prop的时候,它同样开启了prop重命名等其他可能,比如将user重命名为person:
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
你甚至可以定义后备内容,用于插槽prop是undefined的情形:
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
关于这部分的内容理解起来比较抽象,需要对javascript中解构的知识有一定的基础。
动态插槽名
在vue2.6.0中新增了一个语法,可以将动态指令参数用在【v-slot】指令上,来定义动态的插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
具名插槽的缩写
在vue2.6.0中,新增了具名插槽的缩写。即跟【v-on】指令和【v-bind】指令一样,【v-slot】指令也有缩写,即把参数之前的所有内容(v-slot:)替换为字符【#】,例如【v-slot:header】可以被缩写为【#header】:
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template> <p>A paragraph for the main content.</p>
<p>And another one.</p> <template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
然而和其他的指令一样,该缩写只有在其有参数的时候才可用,这就意味着以下的语法是无效的:
<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
如果你希望使用缩写的话,必须始终以明确的插槽名取而代之:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
其他示例
插槽prop允许我们将插槽转换为可复用的模板,这些模板可以基于输入的prop渲染出不同的内容。这一特性在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件的场景中是最有用的。
例如,我们要实现一个<todo-list>组件,它是一个列表且包含布局和过滤逻辑:
<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id">
{{ todo.text }}
</li>
</ul>
我们可以将每个todo作为父级组件的插槽,以此来通过父级组件对其进行控制,然后将todo作为一个插槽的prop进行绑定:
<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id">
<!--
我们为每个 todo 准备了一个插槽,将todo对象作为一个插槽的prop传入。
-->
<slot name="todo" v-bind:todo="todo">
<!-- 后备内容 -->
{{ todo.text }}
</slot>
</li>
</ul>
现在当我们使用<todo-list>组件的时候,我们就可以选择为todo定义一个不一样的<template>作为替代方案,并且可以从子组件中获取数据。
<todo-list v-bind:todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>
这只是作用域插槽用武之地的冰山一角,可以通过浏览各种第三方库的源码实现来深入了解作用域插槽的应用,诸如vue virtual scroller、vue promised和portal vue等库。
废弃了的语法
【v-slot】指令自vue 2.6.0起被引入,提供更好的支持【slot】和【slot-scope】属性的api替代方案。虽然在接下来的所有2.x版本中的【slot】和【slot-scope】属性依然会被支持,但是它们已经被官方废弃且不会再在vue 3中出现。
带有【slot】属性的具名插槽
在<template>上使用特殊的【slot】属性,可以将内容从父级传给具名插槽(新的语法使用【v-slot:插槽名】指令代替)。
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template> <p>A paragraph for the main content.</p>
<p>And another one.</p> <template slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>
或者可以直接把【slot】属性使用在一个普通的元素上:
<base-layout>
<h1 slot="header">Here might be a page title</h1> <p>A paragraph for the main content.</p>
<p>And another one.</p> <p slot="footer">Here's some contact info</p>
</base-layout>
这里其实还有一个未命名的插槽(默认插槽),用来捕获所有未被匹配的内容。上述两个示例的html渲染结果均为:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
带有【slot-scope】属性的作用域插槽
在<template>上使用特殊的【slot-scope】属性,可以接收传递给插槽的prop(新的语法使用【v-slot:插槽名="参数对象名"】指令代替)。
<slot-example>
<template slot="default" slot-scope="slotProps">
{{ slotProps.msg }}
</template>
</slot-example>
这里的【slot-scope】属性声明了被接收的prop对象会作为slotProps变量存在于<template>的作用域中,你可以像命名javascript的函数参数一样随意命名slotProps。
这里的slot="default"还可以被忽略为隐性写法:
<slot-example>
<template slot-scope="slotProps">
{{ slotProps.msg }}
</template>
</slot-example>
另外,【slot-scope】属性也可以直接用于非<template>元素(包括组件):
<slot-example>
<span slot-scope="slotProps">
{{ slotProps.msg }}
</span>
</slot-example>
【slot-scope】属性的值还可以接收任何有效的可以出现在函数定义的参数位置上的javascript表达式,这意味着在支持的环境(单文件组件或现代浏览器)下,你都可以在表达式中使用es2015解构,如下:
<slot-example>
<span slot-scope="{ msg }">
{{ msg }}
</span>
</slot-example>
使用上面的<todo-list>作为示例,与它等价的使用【slot-scope】属性的代码是:
<todo-list v-bind:todos="todos">
<template slot="todo" slot-scope="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>
因为新的语法(v-slot)是在2.6.0+的版本才被支持的,因此如果低于此版本的话要注意使用旧版的语法或升级新的vue版本(更建议)。
"我还是很喜欢你,像木石前盟心誓许,无论结局。"
vue2.x学习笔记(十六)的更多相关文章
- python3.4学习笔记(十六) windows下面安装easy_install和pip教程
python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...
- (C/C++学习笔记) 十六. 预处理
十六. 预处理 ● 关键字typeof 作用: 为一个已有的数据类型起一个或多个别名(alias), 从而增加了代码的可读性. typedef known_type_name new_type_nam ...
- JavaScript权威设计--CSS(简要学习笔记十六)
1.Document的一些特殊属性 document.lastModified document.URL document.title document.referrer document.domai ...
- MySQL学习笔记十六:锁机制
1.数据库锁就是为了保证数据库数据的一致性在一个共享资源被并发访问时使得数据访问顺序化的机制.MySQL数据库的锁机制比较独特,支持不同的存储引擎使用不同的锁机制. 2.MySQL使用了三种类型的锁机 ...
- python 学习笔记十六 django深入学习一 路由系统,模板,admin,数据库操作
django 请求流程图 django 路由系统 在django中我们可以通过定义urls,让不同的url路由到不同的处理函数 from . import views urlpatterns = [ ...
- SharpGL学习笔记(十六) 多重纹理映射
多重纹理就把多张贴图隔和在一起.比如下面示例中,一个表现砖墙的纹理,配合一个表现聚光灯效果的灰度图,就形成了砖墙被一个聚光灯照亮的效果,这便是所谓的光照贴图技术. 多重纹理只在OpenGL扩展库中才提 ...
- yii2源码学习笔记(十六)
Module类的最后代码 /** * Registers sub-modules in the current module. * 注册子模块到当前模块 * Each sub-module shoul ...
- Swift学习笔记十六:协议
Protocol(协议)用于统一方法和属性的名称,而不实现不论什么功能. 协议可以被类.枚举.结构体实现.满足协议要求的类,枚举,结构体被称为协议的遵循者. 遵循者须要提供协议指定的成员,如属性,方法 ...
- PHP学习笔记十六【方法】
<?php //给一个函数传递基本数据类型 $a=90; $b=90.8; $c=true; $d="hello world"; function test1($a,$b,$ ...
- Java基础学习笔记十六 集合框架(二)
List List接口的特点: 它是一个元素存取有序的集合.例如,存元素的顺序是11.22.33.那么集合中,元素的存储就是按照11.22.33的顺序完成的. 它是一个带有索引的集合,通过索引就可以精 ...
随机推荐
- Django 视图笔记
视图 概述 作用:视图接受web请求,并响应 本质:python中的一个函数 响应: 网页;重定向:错误视图(404.500) json数据 url配置 配置流程 1:指定根基url配置文件 sett ...
- 使用scrapy-selenium, chrome-headless抓取动态网页
在使用scrapy抓取网页时, 如果遇到使用js动态渲染的页面, 将无法提取到在浏览器中看到的内容. 针对这个问题scrapy官方给出的方案是scrapy-selenium, 这是一个把sel ...
- MySQL5.6 选项和变量整理
MySQL5.6 选项和变量整理 --allow-suspicious-udfs 这个选项控制是否用户定义函数只有一个xxx符号用于主函数加载.默认,该选项是关闭并且只具有至少一个辅助符号的UDFs ...
- python之装饰器的进阶
一.带参数的装饰器 (必须会) 针对不同的app的验证,比如:天猫和天猫超市,京东商城和京东超市 def wrapper_out(n): print(n) def wrapper(f): def in ...
- 阿里云ECS(Ubuntu)单节点Kubernetes部署
参考资料: kubernetes官网英文版 kubernetes官网中文版 前言 这篇文章是比较久之前写的了,无聊翻了下博客发现好几篇博文排版莫名其妙的变了... 于是修改并完善了下.当初刚玩k8s的 ...
- Selenium系列(十四) - Web UI 自动化基础实战(1)
如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...
- iOS 编译过程原理(1)
一.前言 一般可以将编程语言分为两种,编译语言和直译式语言. 像 C++.Objective-C 都是编译语言.编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在 CPU 上执行,所以 ...
- 解析Tomcat构成及运行原理
本文的知识点分为: Server.Service.Connector.Container四大组件之间的关系和联系,以及他们的主要功能点: Tomcat执行的整体架构,请求是如何被一步步处理的: Eng ...
- Ubuntu系统下命令行查看自己已安装的桌面环境问题
原因:有时我们进行远程连接时需要知道我们的Ubuntu系统已安装的桌面环境,这时我们可以使用[dpkg]命令. [dpkg]:dpkg命令是Debian Linux系统用来安装.创建和管理软件包的实用 ...
- Ubuntu添加新用户并给普通用户赋予root新权限
添加新用户 首先用adduser命令添加普通用户: #adduser newusername 只有在root权限才可以添加新用户 修改密码: #passwd username 赋予root权限 方法1 ...