本文将会着重介绍Vue的slot插槽,介绍完后将Vue插槽和微信小程序的template进行对比讲解。
本着多看文档的原则,介绍顺序按照文档的来。
插槽内容
// 父组件
<navigation-link url="/profile">
Your Profile
</navigation-link>
import navigationLink from 'xxxx'
components:{
navigationLink
}
// navigation-link 组件
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
props:{
url:{
type: String,
default: ''
}
}
.nav-link{
color: #fff;
}
最终将会渲染出一个白色、昵称为Your Profile、链接地址为 /profile 的按钮。这里的Your Profile可以更换为任何内容,甚至是其它的组件,都可以渲染在这个超链接上。需要注意的是,如果在<navigation-link>
的 template
中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
后备内容(默认内容)
有时为一个插槽设置具体的后备 (默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button>
组件中,我们可能希望这个子组件的 <button>
内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 <slot>
标签内:
<button type="submit">
<slot>Submit</slot>
</button>
现在当我们在一个父级组件中使用 <submit-button>
并且不提供任何插槽内容时,后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
如果在父组件中提供内容:
<submit-button>
Save
</submit-button>
则这个提供的内容将会被渲染从而取代后备内容:
<button type="submit">
Save
</button>
具名插槽
有时我们需要多个插槽。<slot>
元素有一个特殊的 attribute:name
。这个 attribute 可以用来定义额外的插槽;一个不带 name
的 <slot>
出口会带有隐含的名字“default”。例如对于一个带有如下模板的 <base-layout>
组件:
// base-layout 组件内部
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
<slot name="header"></slot>
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
<slot></slot>
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
<slot name="footer"></slot>
</footer>
</div>
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称。现在 <template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot
的 <template>
中的内容都会被视为默认插槽的内容。
// 在父组件中使用 base-layout 组件
<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>
最终将会渲染为:
<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-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
</base-layout>
废弃了的语法
这里单独介绍一下带有 slot-scope attribute的作用域插槽,这个用法我曾经在Element-ui的table中遇到过,在一个column容器中通过slot-scope可以拿到这一行元素的所有详细数据。
<slot-example>
<template slot="default" slot-scope="slotProps">
{{ slotProps.msg }}
</template>
</slot-example>
不过这种用法将在Vue3中被彻底舍弃,不推荐使用。
微信小程序之模板
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
定义模板
使用 name 属性,作为模板的名字。然后在<template/>
内定义代码片段,如:
<template name="msgItem">
<view>
<text> {{index}}: {{msg}} </text>
<text> Time: {{time}} </text>
</view>
</template>
使用模板
使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:
<!-- 需要使用模板的页面 -->
<template is="msgItem" data="{{...item}}"/>
Page({
data: {
item: {
index: 0,
msg: 'this is a template',
time: '2016-09-15'
}
}
})
引用
如果定义的模板在其他的页面,我们可以通过WXML提供的两种文件引用方式import和include,这里主要介绍import,因为Include可以将将目标文件除了 <template/>
<wxs/>
外的整个代码引入,相当于是拷贝到 include
位置,无法使用template,与本文冲突。
import
import
可以在该文件中使用目标文件定义的template
,如在 item.wxml 中定义了一个叫item
的template
:
<template name="item">
<text>{{text}}</text>
</template>
在 index.wxml 中引用了 item.wxml,就可以使用item
模板:
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
在使用import的时候需要注意作用域问题,即即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。如:C import B,B import A,在C中可以使用B定义的template
,在B中可以使用A定义的template
,但是C不能使用A定义的template
。
<!-- A.wxml -->
<template name="A">
<text> A template </text>
</template>
<!-- B.wxml -->
<import src="a.wxml"/>
<template name="B">
<text> B template </text>
</template>
<!-- C.wxml -->
<import src="b.wxml"/>
<template is="A"/> <!-- 报错!无法在 C页面 使用 A页面 中定义的template -->
<template is="B"/>
区别
我们知道,微信小程序中也可以使用slot插槽,那么问题其实可以转化为:使用template模板和使用component组件之间的差别。
这两者的最大区别是:template主要是用来展示现有数据,方法则需要在调用者的页面中定义。而component组件则有自己的业务逻辑,可以看做一个独立的page页面。简单来说,如果只是展示,使用template就足够了,如果涉及到的业务逻辑交互比较多,那就最好使用component组件。正是这一点区别,使用template模板的时候,通常创建一个pages同级文件夹 template , 然后在某个模板的文件夹中只能存放.wxml和.wxss文件。