详解vue 2.6 中 slot 的新用法

最近发布不久的Vue 2.6,使用插槽的语法变得更加简洁。 对插槽的这种改变让我对发现插槽的潜在功能感兴趣,以便为我们基于Vue的项目提供可重用性,新功能和更清晰的可读性。 真正有能力的插槽是什么?

如果你是Vue的新手,或者还没有看到2.6版的变化,请继续阅读。也许学习插槽的最佳资源是Vue自己的文档,但是我将在这里给出一个纲要。

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

插槽是什么?

插槽是Vue组件的一种机制,它允许你以一种不同于严格的父子关系的方式组合组件。插槽为你提供了一个将内容放置到新位置或使组件更通用的出口。从一个简单的例子开始:

// frame.vue
<template>
 <div class="frame">
 <slot></slot>
 </div>
</template>

这个组件最外层是一个 div 。假设 div 的存在是为了围绕其内容创建一个样式框架。这个组件可以通用地用于将框架包围在wq你想要的任何内容上,来看看它是怎么用的。这里的 frame 组件指的是我们刚才做的组件。

// app.vue
<template>
 <frame><img src="an-image.jpg"></frame>
</template>

在开始和结束 frame 标记之间的内容将插入到插槽所在的 frame 组件中,替换 slot 标记。这是最基本的方法。还可以简单地通过填充指定要放入槽中的默认内容

// frame.vue
<template>
 <div class="frame">
 <slot>如果这里没有指定任何内容,这就是默认内容</slot>
 </div>
</template>

所以现在如果我们这样使用它:

// app.vue
<template>
 <frame />
</template>

“ 如果这里没有指定任何内容,这就是默认内容 ”是默认内容,但是如果像以前那样使用它,默认文本将被 img 标记覆盖。

多个/命名的插槽

可以向组件添加多个插槽,但是如果这样做了,那么除了其中一个之外,其他所有插槽都需要有名称。如果有一个没有名称的槽,它就是默认槽。下面是如何创建多个插槽:

// titled-frame.vue
<template>
 <div class="frame">
 <header><h2>
 <slot name="header">Title</slot>
 </h2></header>
 <slot>如果这里没有指定任何内容,这就是默认内容</slot>
 </div>
</template>

我们保留了相同的默认槽,但这次我们添加了一个名为 header 的槽,可以在其中输入标题,用法如下:

// app.vue
<template>
 <titled-frame>
 <template v-slot:header>
 <!-- The code below goes into the header slot -->
 My Image's Title
 </template>
 <!-- The code below goes into the default slot -->
 <img src="an-image.jpg">
 </titled-frame>
</template>

就像之前一样,如果我们想将内容添加到默认槽中,只需将其直接放在 titled-frame 组件中。但是,要将内容添加到命名槽中,我们需要用 v-slot 指令将代码包裹在在 template 标记中。在 v-slot 之后添加冒号 (:) ,然后写出要传递内容的 slot 的名称。

注意, v-slot 是 Vue 2.6 的新版本,所以如果你使用的是旧版本,则需要阅读 关于不推荐的slot语法的文档。

作用域插槽

还需要知道的另一件事是插槽可以将数据/函数传递给他们的孩子。 为了证明这一点,我们需要一个完全不同的带有插槽的示例组件:创建一个组件,该组件将当前用户的数据提供给其插槽:

// current-user.vue
<template>
 <span>
 <slot v-bind:user="user">
 {{ user.lastName }}
 </slot>
 </span>
</template>

<script>
export default {
 data () {
 return {
 user: ...
 }
 }
}
</script>

该组件有一个名为 user 的属性,其中包含关于用户的详细信息。默认情况下,组件显示用户的姓,但请注意,它使用 v-bind 将用户数据绑定到 slot 。这样,我们就可以使用这个组件向它的后代提供用户数据

// app.vue
<template>
 <current-user>
 <template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template>
 </current-user>
</template>

为了访问传递给 slot 的数据,我们使用v-slot指令的值指定作用域变量的名称。

这里有几点需要注意:

  • 我们指定了 default 的名称,但是不需要为默认槽指定名称。相反,我们可以使用v -slot="slotProps"
  • 不需要使用 slotProps 作为名称,可以随便叫它什么。
  • 如果只使用默认槽,可以跳过内部 template 标记,直接将 v-slot 指令放到当前 current-user 上。
  • 可以使用对象解构来创建对作用域插槽数据的直接引用,而不是使用单个变量名。换句话说,可以使用 v-slot="{user}" 代替 v-slot="slotProps" ,然后可以直接使用 user 而不是 slotProps.user 。

所以,上面的例子可以这样重写

// app.vue
<template>
 <current-user v-slot="{user}">
 {{ user.firstName }}
 </current-user>
</template>

还有几点要记住:

  • 可以使用 v-bind 指令绑定多个值。
  • 也可以将函数传递到作用域槽。许多库使用它来提供可重用的函数组件。
  • v-slot 的别名是 # 。因此,可以用 #header="data" 来代替 v-slot:header="data" 。还可以使用 #header 来代替 v-slot:header (前提:不是作用域插槽时)。对于默认插槽,在使用别名时需要 指定默认名称 。换句话说,需要这样写 #default="data" 而不是 #="data" 。

可以从 文档 中了解更多的细节,但这足以帮助你理解在本文剩下部分中讨论的内容。

你能用插槽做什么?

插槽不是为了一个目的而构建的,或者至少如果它们是,它们已经超越了最初的意图,成为做许多不同事物的强大工具。

可重用的模式

组件总是被设计为可重用的,但是某些模式对于使用单个“普通”组件来实施是不切实际的,因为为了自定义它,需要的 props 数量可能过多或者需要通过 props 传递大部分内容或其它组件。

插槽可用包裹外部的HTML标签或者组件,并允许其他HTML或组件放在具名插槽对应名称的插槽上。

对于的第一个例子,从简单的东西开始:一个按钮。假设咱们的团队正在使用 Bootstrap。使用Bootstrap,按钮通常与基本的 “btn” 类和指定颜色的类绑定在一起,比如 “btn-primary” 。你还可以添加 size 类,比如 'btn-lg' 。

为了简单起见,现在让我们假设你的应用使用 btn 、 btn-primary 和 btn-lg 。你不希望总是必须在按钮上写下这三个类,或者你不相信新手会记得写下这三个类。

在这种情况下,可以创建一个自动包含所有这三个类的组件,但是如何允许自定义内容? prop 不实用,因为允许按钮包含各种HTML,因此我们应该使用一个插槽。

<!-- my-button.vue -->
<template>
 <button class="btn btn-primary btn-lg">
 <slot>Click Me!</slot>
 </button>
</template>

现在我们可以在任何地方使用它,无论你想要什么内容

<!-- 使用 my-button.vue -->
<template>
 <my-button>
 <img src="/img/awesome-icon.jpg"> 我是小智!
 </my-button>
</template>

当然,你可以选择比按钮更大的东西。 坚持使用Bootstrap,让我们看一个模态:

<!-- my-modal.vue -->
<template>
<div class="modal" tabindex="-1" role="dialog">
 <div class="modal-dialog" role="document">
 <div class="modal-content">
 <div class="modal-header">
 <slot name="header"></slot>
 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  <span aria-hidden="true">×</span>
 </button>
 </div>
 <div class="modal-body">
 <slot name="body"></slot>
 </div>
 <div class="modal-footer">
 <slot name="footer"></slot>
 </div>
 </div>
 </div>
</div>
</template>

现在,使用它:

<!-- 使用 my-modal.vue -->
<template>
 <my-modal>
 <template #header>
 <h5>大家最棒!</h5>
 </template>
 <template #body>
 <p>大家加油</p>
 </template>
 <template #footer>
 <em>大家好样的!</em>
 </template>
 </my-modal>
</template>

上述类型的插槽用例显然非常有用,但它可以做得更多。

复用函数

Vue组件并不完全是关于HTML和CSS的。它们是用JavaScript构建的,所以也是关于函数的。插槽对于一次性创建函数并在多个地方使用功能非常有用。让我们回到模态示例并添加一个关闭模态的函数

<!-- my-modal.vue -->
<template>
<div class="modal" tabindex="-1" role="dialog">
 <div class="modal-dialog" role="document">
 <div class="modal-content">
 <div class="modal-header">
 <slot name="header"></slot>
 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  <span aria-hidden="true">×</span>
 </button>
 </div>
 <div class="modal-body">
 <slot name="body"></slot>
 </div>
 <div class="modal-footer">
 <slot name="footer" :closeModal="closeModal"></slot>
 </div>
 </div>
 </div>
</div>
</template>

<script>
export default {
 //...
 methods: {
 closeModal () {
 // 关闭对话框时,需要做的事情
 }
 }
}
</script>

当使用此组件时,可以向 footer 添加一个可以关闭模​​态的按钮。 通常,在Bootstrap模式的情况下,可以将 data-dismiss =“modal” 添加到按钮来进行关闭。

但我们希望隐藏Bootstrap 特定的东西。 所以我们传递给他们一个他们可以调用的函数,这样使用者就不会知道我们有使用 Bootstrap 的东西。

<!-- 使用 my-modal.vue -->
<template>
 <my-modal>
 <template #header>
 <h5>Awesome Interruption!</h5>
 </template>
 <template #body>
 <p>大家加油!</p>
 </template>
 <template #footer="{closeModal}">
 <button @click="closeModal">
 点我可以关闭烦人的对话框
 </button>
 </template>
 </my-modal>
</template>

无渲染组件

最后,可以利用你所知道的关于使用插槽来传递可重用函数的知识,并剥离所有HTML,只使用插槽。这就是无渲染组件的本质:一个只提供函数而不包含任何HTML的组件。

使组件真正无渲染可能有点棘手,因为需要编写 render 函数而不是使用模板来消除对根元素的依赖,但它可能并不总是必要的。 来看看一个先使用模板的简单示例:

<template>
 <transition name="fade" v-bind="$attrs" v-on="$listeners">
 <slot></slot>
 </transition>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
 transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
 opacity: 0;
}
</style>

这是一个无渲染组件的奇怪例子,因为它甚至没有任何JavaScript。这主要是因为我们正在创建一个内置无渲染函数的预配置可重用版本: transition 。

是的,Vue有内置的无渲染组件。这个特殊的例子取自Cristi Jora的一篇关于 可重用transition 的文章,展示了一种创建无渲染组件的简单方法,该组件可以标准化整个应用程序中使用的 transition 。

对于我们的另一个示例,我们将创建一个组件来处理切换 Promise 的不同状态中显示的内容: pending、resolved 和 failed。这是一种常见的模式,虽然它不需要很多代码,但是如果没有为了可重用性而提取逻辑,它会使很多组件变得混乱。

<!-- promised.vue -->
<template>
 <span>
 <slot name="rejected" v-if="error" :error="error"></slot>
 <slot name="resolved" v-else-if="resolved" :data="data"></slot>
 <slot name="pending" v-else></slot>
 </span>
</template>
<script>
export default {
 props: {
 promise: Promise
 },
 data: () => ({
 resolved: false,
 data: null,
 error: null
 }),
 watch: {
 promise: {
 handler (promise) {
 this.resolved = false
 this.error = null
 if (!promise) {
  this.data = null
  return
 }
 promise.then(data => {
  this.data = data
  this.resolved = true
 })
 .catch(err => {
  this.error = err
  this.resolved = true
 })
 },
 immediate: true
 }
 }
}
</script>

这是怎么回事,小老弟?首先,请注意,该组件接收一个Promise 类型参数。在 watch 部分中,监听 promise 的变化,当 promise 发生变化时,清除状态,然后调用 then 并 catch promise,当 promise 成功完成或失败时更新状态。

然后,在模板中,我们根据状态显示一个不同的槽。请注意,我们没有保持它真正的无渲染,因为我们需要一个根元素来使用模板。我们还将 data 和 error 传递到相关的插槽范围。

<template>
 <div>
 <promised :promise="somePromise">
 <template #resolved="{ data }">
 Resolved: {{ data }}
 </template>
 <template #rejected="{ error }">
 Rejected: {{ error }}
 </template>
 <template #pending>
  请求中...
 </template>
 </promised>
 </div>
</template>
...

我们将 somePromise 传递给无渲染组件。 然后等待它完成,对于 pending 的插槽,显示“请求中...”。 如果成功,显示“Resolved:对应的值”。 如果失败,显示“已Rejected:失败的原因”。 现在我们不再需要跟踪此组件中的 promise 的状态,因为该部分被拉出到它自己的可重用组件中。

那么,我们可以做些什么来绕过 promised.vue 中的插槽? 要删除它,我们需要删除 template 部分并向我们的组件添加 render 函数:

render () {
 if (this.error) {
 return this.$scopedSlots['rejected']({error: this.error})
 }

 if (this.resolved) {
 return this.$scopedSlots['resolved']({data: this.data})
 }

 return this.$scopedSlots['pending']()
}

这里没有什么太复杂的。我们只是使用一些 if 块来查找状态,然后返回正确的作用域 slot (通过 this.$ scopedslot ['SLOTNAME'](…) ),并将相关数据传递到 slot 作用域。当你不使用模板时,可以跳过使用 .vue 文件扩展名,方法是将JavaScript从 script 标记中提取出来,然后将其放入 .js 文件中。在编译这些Vue文件时,这应该会给你带来非常小的性能提升。

总结

Vue的插槽将基于组件的开发提升到了一个全新的水平,虽然本文已经展示了许多可以使用插槽的好方法,但还有更多的插槽。

以上所述是小编给大家介绍的vue 2.6 中 slot 的新用法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • Vue组件中slot的用法

    下面给大家介绍Vue组件中slot的用法 主要是让组件的可扩展性更强. 1. 使用匿名slot 2. 给slot加个名字 如果不在有slot的组件里加入任何标签,slot什么都不会显示的. 总结 以上所述是小编给大家介绍的Vue组件中slot的用法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的.在此也非常感谢大家对我们网站的支持! 您可能感兴趣的文章: 详解Vue学习笔记入门篇之组件的内容分发(slot) Vue.js中组件中的slot实例详解 Vuejs第十一篇组件之

  • 详解vue 2.6 中 slot 的新用法

    最近发布不久的Vue 2.6,使用插槽的语法变得更加简洁. 对插槽的这种改变让我对发现插槽的潜在功能感兴趣,以便为我们基于Vue的项目提供可重用性,新功能和更清晰的可读性. 真正有能力的插槽是什么? 如果你是Vue的新手,或者还没有看到2.6版的变化,请继续阅读.也许学习插槽的最佳资源是Vue自己的文档,但是我将在这里给出一个纲要. 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 插槽是什么? 插槽是Vue组件的一种机制,它允许你以一种不同于严格的父子关系的方式组合组件.插

  • 详解vue2.6插槽更新v-slot用法总结

    在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令).它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的特性.新语法的由来可查阅 RFC. 引vue官方文档 之前做项目时,对插槽理解太少了,这两天学习时,才发现插槽更新用v-slot了,自己做了些简单总结,与大家分享一下~ 插槽 我的理解就是父页面在组件标签内插入任意内容,子组件内插糟slot控制摆放位置(匿名插槽,具名插槽) 插槽分类 插槽一共就三大类 1.匿名

  • vue 组件中slot插口的具体用法

    子组件 <template> <div class="slotcontent"> <ul> <!--<slot></slot>--> <li v-for="item in items">{{item.text}}</li> </ul> </div> </template> <script> export default{ d

  • Vue slot用法(小结)

    之前看官方文档,由于自己理解的偏差,不知道slot是干嘛的,看到小标题,使用Slot分发内容,就以为 是要往下派发内容.然后就没有理解插槽的概念.其实说白了,使用slot就是先圈一块地,将来可能种花种菜,也有可能在这块地上建房子.然而slot可以以一当十,可以插入很多东西.不知明白否? 由于项目经验有限,这篇我就先跟着官网的知识点走,当然会加入自己的部分项目代码. 关于slot是这样说的, 除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃.当子组件模板只有一个没有属性

  • 详解vue使用插槽分发内容slot的用法

    将父组件的内容放到子组件指定的位置叫做内容分发 //在父组件里使用子组件 <son-tmp> <div>我是文字,我需要放到son-tmp组件里面制定的位置</div> </son-tmp> 单个插槽 父组件app.vue <template> <div id="app"> <test-slot> <span>我是父组件里的文字,但是我要被放到子组件里</span> </t

  • 详解Vue的组件中data选项为什么必须是函数

    官方解释 data 必须是函数 构造 Vue 实例时传入的各种选项大多数都可以在组件里使用.只有一个例外:data 必须是函数.实际上,如果你这么做: Vue.component('my-component', { template: '<span>{{ message }}</span>', data: { message: 'hello' } }) 那么 Vue 会停止运行,并在控制台发出警告,告诉你在组件实例中 data 必须是一个函数.但理解这种规则为何存在也是很有益处的,

  • 详解Vue文档中几个易忽视部分的剖析

    针对Vue文档中部分大家可能不会去研读的内容,我做了个小总结,作为有经验者的快餐,不是特别适合初学者,可能有不妥之处,希望大家多提建议. 节省代码量的mixin mixin概念:组件级可复用逻辑,包括数据变量/生命周期钩子/公共方法,从而在混入的组件中可以直接使用,不用重复写冗余逻辑(类似继承) 使用方法: 在某一公共文件夹pub下创建mixin文件夹,其下创建mixinTest.js const mixinTest = { created() { console.log(`components

  • 详解vue在项目中使用百度地图

    第一步,去百度地图开发者申请秘钥. 第二步在项目中引入,具体如下 其中index.html存放地图链接,代码如下 然后在APP.vue里面实现,代码如下 <template> <div id="app"> <div id="allmap" ref="allmap"></div> <router-view></router-view> </div> </tem

  • 详解Vue源码中一些util函数

    JS中很多开源库都有一个util文件夹,来存放一些常用的函数.这些套路属于那种常用但是不在ES规范中,同时又不足以单独为它发布一个npm模块.所以很多库都会单独写一个工具函数模块. 最进尝试阅读vue源码,看到很多有意思的函数,在这里分享一下. Object.prototype.toString.call(arg) 和 String(arg) 的区别? 上述两个表达式都是尝试将一个参数转化为字符串,但是还是有区别的. String(arg) 会尝试调用 arg.toString() 或者 arg

  • 详解Vue.js 作用域、slot用法(单个slot、具名slot)

    作用域 在介绍slot前,需要先知道一个概念:编译的作用域.比如父组件中有如下模板: <child-component> {{message}} <child-component> 这里的message就是一个slot,但是它绑定的是父组件的数据,而不是组件< child-component >的数据. 父组件模板的内容是在父组件作用域内编译,子组件模板的内容是在子组件作用域内编译. <div id="app"> <child-co

  • 详解Vue改变数组中对象的属性不重新渲染View的解决方案

    在解决问题之前,我们先来了解下 vue响应性原理: Vue最显著的一个功能是响应系统-- 模型只是一个普通对象,修改对象则会更新视图. 受到javascript的限制,Vue不能检测到对象属性的添加或删除,因为vue在初始化实列时将属性转为getter/setter,所以属性必须在data对象上才能让vue转换它. 但是vue可以使用 Vue.set(object, key, value)方法将响应属性添加到嵌套的对象上:如下代码: Vue.set(obj, '_isHover', true);

  • 详解VUE 对element-ui中的ElTableColumn扩展

    公司有一个新的需求,点击ElTableColumn的头部可以进行搜索,这个功能同事已经做出来了,但是使用有些不方便,自己就打算封装成一个组件,学习一下. ElTableColumn本来是这个样子的: 要做成的是这个样子: 我直接就放代码了,挨着挨着说明太多了. 代码的结构: 组件 <!-- ElTableColumnPro.vue --> <template> <el-table-column v-if="visible" :formatter="

  • 详解Vue的watch中的immediate与watch是什么意思

    immediate immediate设为true后,则监听的这个对象会立即输出,也就是说一刷新页面就会在控制台输出,当然此时页面上的数据我们还没来得及手动让其发生变化,所以在控制台输出的newValue为我们在代码中默认设置的值,oldValue输出为"undefined".如 当我们手动改变newValue.id的值后,输出如下: 如果不设置immediate,或者将immediate设为false的话,则刷新页面后不会立即监听此对象,也就是控制台不会有输出,必须要监听的对象有值改

  • 详解Html a标签中href和onclick用法、区别、优先级别

    如果不设置 href属性在IE6下面会不响应hover.双击后会选中标签的父容器而非这个一a标签(IE下都存在这一问题). 代码如下 <a href="javascirpt:fn(this)"> <a onclick="fn(this)"> 假定我们有个fn方法,需要取到这个元素,第一个方法传入的this是空值. 所以,比较推荐的写法是 代码如下 <a href="javascript:void(0)" onclic

随机推荐