导航
<slot> 占位<MyButton>(文本、HTML、组件)</MyButton>MyButton.vue
<template>
<button>
<slot></slot>
</button>
</template>
<script>
export default {
name: 'MyButton'
}
</script>
App.vue
<template>
<div>
<my-button>
Hello<span>World</span>
</my-button>
</div>
</template>
<script>
import MyButton from './MyButton.vue';
export default {
name: 'App',
components: {
MyButton
}
}
</script>

MyButton.vue
<template>
<button>
<slot>Lance</slot>
</button>
</template>
<script>
export default {
name: 'MyButton'
}
</script>
App.vue
<template>
<div>
<my-button></my-button>
</div>
</template>
<script>
import MyButton from './MyButton.vue';
export default {
name: 'App',
components: {
MyButton
}
}
</script>

把 fontawesome 放进 public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
...
<link href="<https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.3/css/all.min.css>" rel="stylesheet">
</head>
<body>
...
</body>
</html>
App.vue 中测试
<template>
<div>
<my-button>
<span class="fa fa-spinner fa-spin"></span>
</my-button>
</div>
</template>
<script>
import MyButton from './MyButton.vue';
export default {
name: 'App',
components: {
MyButton
}
}
</script>

在转的 loading 图标
MyButton.vue
<template>
<button @click="handleClick">
<span
v-if="loadingIcon && isLoading"
class="fa fa-spinner fa-spin"></span>
<slot>Lance</slot>
</button>
</template>
<script>
export default {
name: 'MyButton',
props: ['text', 'loadingIcon', 'isLoading'],
methods: {
handleClick() {
this.$emit('changeStatus');
}
}
}
</script>
App.vue
<template>
<div>
<my-button
@changeStatus="changeStatus"
:disabled="isLoading"
:loadingIcon="true"
:isLoading="isLoading"
>
{{ isLoading ? 'Loading...' : 'Click' }}
</my-button>
</div>
</template>
<script>
import MyButton from './MyButton.vue';
export default {
name: 'App',
components: {
MyButton
},
data() {
return {
isLoading: false
}
},
methods: {
changeStatus() {
console.log(1);
this.isLoading = true;
setTimeout(() => {
this.isLoading = false;
}, 3000);
}
}
}
</script>
<slot name="myBtn"></slot>v-slot 只能用在 <template> 上<template v-slot**:myBtn**></template><template #myBtn></template>MyButton.vue
<template>
<button @click="handleClick">
<span
v-if="loadingIcon && isLoading"
class="fa fa-spinner fa-spin"></span>
<slot name="myBtn">Click</slot>
</button>
</template>
<script>
export default {
name: 'MyButton',
props: ['text', 'loadingIcon', 'isLoading'],
methods: {
handleClick() {
this.$emit('changeStatus');
}
}
}
</script>
App.vue
<template>
<div>
<my-button
@changeStatus="changeStatus"
:disabled="isLoading"
:loadingIcon="true"
:isLoading="isLoading"
>
<!-- {{ isLoading ? 'Loading...' : 'Click' }} -->
<!-- <template v-slot:myBtn> -->
<template #myBtn>
{{ isLoading ? 'Loading...' : 'Click' }}
</template>
</my-button>
</div>
</template>
<script>
import MyButton from './MyButton.vue';
export default {
name: 'App',
components: {
MyButton
},
data() {
return {
isLoading: false
}
},
methods: {
changeStatus() {
console.log(1);
this.isLoading = true;
setTimeout(() => {
this.isLoading = false;
}, 3000);
}
}
}
</script>
如果没有在组件中定义具名slot插槽,那么外边如果非要用 template,则可以用 <teamplte v-slot:default> 或者 <template #default> 的方式来当做默认插槽。
MyButton.vue
<template>
<button @click="handleClick">
<span
v-if="loadingIcon && isLoading"
class="fa fa-spinner fa-spin"></span>
<slot>Click</slot>
</button>
</template>
App.vue
<template>
<div>
<my-button
@changeStatus="changeStatus"
:disabled="isLoading"
:loadingIcon="true"
:isLoading="isLoading"
>
<!-- <template v-slot:default> -->
<template #default>
{{ isLoading ? 'Loading...' : 'Click' }}
</template>
</my-button>
</div>
</template>
MyButton.vue
<template>
<button @click="handleClick">
<span
v-if="loadingIcon && isLoading"
class="fa fa-spinner fa-spin"></span>
<!-- <slot name="myBtn">Click</slot> -->
<slot name="title"></slot>
<slot name="title"></slot>
<slot>Click</slot>
<slot>Click</slot>
</button>
</template>
<script>
export default {
name: 'MyButton',
props: ['text', 'loadingIcon', 'isLoading'],
methods: {
handleClick() {
this.$emit('changeStatus');
}
}
}
</script>
App.vue
<template>
<div>
<my-button
@changeStatus="changeStatus"
:disabled="isLoading"
:loadingIcon="true"
:isLoading="isLoading"
>
<template #title>
<h1>TITLE</h1>
</template>
<template #default>
{{ isLoading ? 'Loading...' : 'Click' }}
</template>
</my-button>
</div>
</template>
<script>
import MyButton from './MyButton.vue';
export default {
name: 'App',
components: {
MyButton
},
data() {
return {
isLoading: false
}
},
methods: {
changeStatus() {
console.log(1);
this.isLoading = true;
setTimeout(() => {
this.isLoading = false;
}, 3000);
}
}
}
</script>


components/BaseLayout/index.vue
<template>
<div class="container">
<header class="base-header">
<slot name="baseHeader">HEADER</slot>
</header>
<footer class="base-footer">
<slot name="baseFooter">FOOTER</slot>
</footer>
<aside class="base-sidebar">
<slot name="baseSidebar">SIDEBAR</slot>
</aside>
<main class="base-main">
<slot>MAIN</slot>
</main>
</div>
</template>
<script>
export default {
name: 'BaseLayout'
}
</script>
<style lang="scss">
html {
margin: 0;
}
.base-header,
.base-footer,
.base-sidebar,
.base-main {
position: fixed;
left: 0;
}
.base-header {
top: 0;
z-index: 2;
width: 100%;
height: 60px;
background-color: #000;
}
.base-footer {
bottom: 0;
z-index: 2;
width: 100%;
height: 60px;
background-color: #ddd;
}
.base-sidebar {
top: 0;
z-index: 1;
width: 200px;
height: 100%;
padding: 90px 0;
background-color: #eee;
box-sizing: border-box;
}
.base-main {
top: 0;
width: 100%;
height: 100%;
padding: 90px 30px 90px 230px;
box-sizing: border-box;
}
</style>
App.vue
<template>
<div>
<base-layout>
<template #baseHeader>
<main-logo></main-logo>
</template>
<template #baseFooter>
<footer-content></footer-content>
</template>
<template #baseSidebar>
<base-list></base-list>
</template>
<template #default>
<h1>This is my MAIN BOARD</h1>
</template>
</base-layout>
</div>
</template>
<script>
import BaseLayout from './components/BaseLayout';
import MainLogo from './components/MainLogo';
import FooterContent from './components/FooterContent';
import BaseList from './components/BaseList';
export default {
name: 'App',
components: {
BaseLayout,
MainLogo,
FooterContent,
BaseList
}
}
</script>
<slot :item="item" :field="field"></slot><template v-slot="props">PicBoard.vue
<template>
<div>
<ul>
<li v-for="item of picData" :key="item.id">
<div>
<h1>{{ item.title }}</h1>
<slot :item="item" :field="1"></slot>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'PicBoard',
data() {
return {
picData: [
{
id: 1,
title: '123',
desc: '12333333',
url: '<https://placekitten.com/100/100>'
},
{
id: 2,
title: '234',
desc: '234444444',
url: '<https://placekitten.com/200/200>'
},
{
id: 3,
title: '345',
desc: '34555555',
url: '<https://placekitten.com/300/300>'
}
]
}
}
}
</script>
<style>
</style>
App.vue
<template>
<div>
<pic-board>
<template #default="props">
{{ props.item }} {{ props.field }}
</template>
</pic-board>
</div>
</template>
<script>
import PicBoard from './components/PicBoard';
export default {
name: 'App',
components: {
PicBoard
}
}
</script>
如果没有具名插槽,上边的 <template #default="props" 还能写成:
<template v-slot="props">
<pic-board v-slot="props">
<img :src="props.url" :alt="props.desc" style="width: 200px;"/>
<p>{{ props.desc }}</p>
</pic-board>
推荐写法:
<pic-board #default="props">
<img :src="props.url" :alt="props.desc" style="width: 200px;"/>
<p>{{ props.desc }}</p>
</pic-board>
<pic-board v-slot="{ url, desc }">
<img :src="url" :alt="desc" style="width: 200px;"/>
<p>{{ desc }}</p>
</pic-board>
<pic-board v-slot="{ url: imgUrl, desc: description }">
<img :src="imgUrl" :alt="description" style="width: 200px;"/>
<p>{{ description }}</p>
</pic-board>
<pic-board v-slot="{ url: imgUrl, desc: description, field = 0 }">
<img :src="imgUrl" :alt="description" style="width: 200px;"/>
<p>【排行:{{field}}】{{ description }}</p>
</pic-board>

PicBoard.vue
<template>
<div>
<ul>
<li v-for="item of picData" :key="item.id">
<div>
<h1>{{ item.title }}</h1>
<slot :url="item.url" :desc="item.desc" :field="item.field"></slot>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'PicBoard',
data() {
return {
picData: [
{
id: 1,
title: '123',
desc: '12333333',
url: '<https://placekitten.com/100/100>',
field: 0,
},
{
id: 2,
title: '234',
desc: '234444444',
url: '<https://placekitten.com/200/200>',
field: 1,
},
{
id: 3,
title: '345',
desc: '34555555',
url: '<https://placekitten.com/300/300>',
field: 2,
}
]
}
}
}
</script>
<style>
</style>