深入了解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>`
});

参考:

原文地址:https://www.cnblogs.com/gzhjj/p/11778265.html