深入了解Vue组件 — 插槽
1.插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<navigation-link :url="url">Your Profile({{ user.name }})</navigation-link>
</div>
<script>
Vue.component('navigation-link', {
props: {
url: String,
},
template: `<a :href="url"><slot></slot></a>`
});
new Vue({
el: '#app',
data: {
url: 'https://www.baidu.com',
user: {
name: 'hjj'
}
}
});
</script>
</body>
</html>
当组件渲染的时候,<slot></slot>
将会被替换为"Your Profile"。
注意:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<navigation-link url="/profile">Your Profile {{ url }}</navigation-link>
</div>
<script>
Vue.component('navigation-link', {
template: `<a><slot></slot></a>`
});
new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
上面代码通过{{ url }}
访问url会报错。
1.1 默认值
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<submit-button></submit-button>
<submit-button>提交</submit-button>
</div>
<script>
Vue.component('submit-button', {
template: `<button type="submit"><slot>Submit</slot></button>`
});
new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
注意到<slot>Submit</slot>
的Submit是默认值。
1.2 具名插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<!-- <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<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>
<script>
Vue.component('base-layout', {
template: `<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`
});
new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
在向具名插槽提供内容的时候,我们可以在一个<template>
元素上使用v-slot
指令,并以v-slot
的参数的形式提供其名称(比如<template v-slot:header>
,它匹配<slot name="header"></slot>
)。
注意:<main><slot></slot></main>
的<slot>
没有带name
,则带有隐含的名字default
。任何没有被包裹在带有v-slot
的<template>
中的内容都会被视为默认插槽的内容。
1.3 作用域插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
<br/>
<!-- 当只有默认插槽时,我们可以把`v-slot`直接用在组件上。 -->
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
<br/>
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
</div>
<script>
Vue.component('current-user', {
data: function(){
return {
user: {
firstName: 'He'
}
}
},
template: `<span><slot v-bind:user="user"></slot></span>`
});
new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
绑定在<slot>
元素上的特性被称为插槽 prop。这里我们将包含所有插槽 prop 的对象命名为slotProps
,但你也可以使用任意你喜欢的名字。
1.3.1 解构插槽prop
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<current-user v-slot="{user}">
{{ user.firstName }}
</current-user>
<br/>
<current-user v-slot="{user: person}">
{{ person.firstName }}
</current-user>
</div>
<script>
Vue.component('current-user', {
data: function(){
return {
user: {
firstName: 'He'
}
}
},
template: `<span><slot v-bind:user="user"></slot></span>`
});
new Vue({
el: '#app'
});
</script>
</body>
</html>
1.4 具名插槽的缩写
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<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 #default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
<br/>
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
</div>
<script>
Vue.component('base-layout', {
template: `<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`
});
Vue.component('current-user', {
data: function(){
return {
user: {
firstName: 'He'
}
}
},
template: `<span><slot v-bind:user="user"></slot></span>`
});
new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
具名插槽v-slot:header
可简写为#header
。
1.5 其它示例
插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<todo-list>
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>
<todo-list>
<template v-slot:todo="{ todo }"></template>
</todo-list>
<todo-list></todo-list>
</div>
<script>
Vue.component('todo-list', {
data: function(){
return {
todos: [
{id: 1, text: 'Hey', isComplete: true},
{id: 2, text: 'Thank you', isComplete: true},
{id: 3, text: 'Very much', isComplete: false},
]
}
},
template: `<ul>
<li v-for="todo in todos" v-bind:key="todo.id">
<slot name="todo" v-bind:todo="todo">
{{ todo.text }}
</slot>
</li>
</ul>`
});
new Vue({
el: '#app'
});
</script>
</body>
</html>
1.6 总结
<navigation-link :url="url">Your Profile({{ user.name }})</navigation-link>
Vue.component('navigation-link', {props: {url: String},template: `<a :href="url"><slot></slot></a>`});
data: {url: '...', user: {name: '...'}}
1、默认值
<submit-button></submit-button>
<submit-button>提交</submit-button>
template: `<button type="submit"><slot>Submit</slot></button>`
2、具名插槽
<base-layout><template v-slot:header>...</template></base-layout>
template: `<div class="container"><header><slot name="header"></slot></header>...</div>`
3、作用域插槽
// 不是具名插槽,就是默认插槽。
<current-user><template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template></current-user>
<current-user v-slot:default="slotProps">{{ slotProps.user.firstName }}</current-user>
<current-user v-slot="slotProps">{{ slotProps.user.firstName }}</current-user>
Vue.component('current-user', {
data: function(){return {user: {firstName: 'He'}}},
template: `<span><slot v-bind:user="user"></slot></span>`
});
// 解构
<current-user v-slot="{user}">{{ user.firstName }}</current-user>
<current-user v-slot="{user: person}">{{ person.firstName }}</current-user>
Vue.component('current-user', {
data: function(){return {user: {firstName: 'He'}}},
template: `<span><slot v-bind:user="user"></slot></span>`
});
4、具名插槽的缩写
<base-layout><template #header>...</template></base-layout>
<current-user #default="slotProps">{{ slotProps.user.firstName }}</current-user>
<current-user #default="{ user }">{{ user.firstName }}</current-user>
template: `<div class="container"><header><slot name="header"></slot></header>...</div>`
template: `<span><slot v-bind:user="user"></slot></span>`
5、其他示例
<todo-list>
<template v-slot:todo="{ todo }"><span v-if="todo.isComplete">✓</span>{{ todo.text }}</template>
</todo-list>
<todo-list>
<template v-slot:todo="{ todo }"></template>
</todo-list>
Vue.component('todo-list', {
data: function(){ return { todos: [...] }},
template: `<ul>
<li v-for="todo in todos" v-bind:key="todo.id">
<slot name="todo" v-bind:todo="todo">{{ todo.text }}</slot>
</li>
</ul>`
});
参考: