×

Vue中如何传递模板内容?

独孤求败 独孤求败 发表于2026-06-18 12:43:01 浏览34 评论0

抢沙发发表评论

在实际开发中,我们通常使用 Props 来完成父组件向子组件的数据传递。Props 非常适合传递字符串、数字、对象、数组以及函数等数据。

但有些场景下,父组件希望传递给子组件的不只是数据,而是一段完整的模板内容,例如按钮、表单、图片,甚至其他组件。对于这类需要传递界面结构的需求,单纯使用 Props 往往不够灵活。

此时,Vue 提供了另一种内容分发机制 —— Slot(插槽)

示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue 3 Slot 示例 (Options API)</title>
    <script src="https://unpkg.com/vue@3.5.17/dist/vue.global.js"></script>
    <style>
        .card {
            border1px solid #e0e0e0;
            border-radius8px;
            width320px;
            margin20px auto;
            box-shadow04px6pxrgba(0,0,0,0.1);
            font-family: sans-serif;
        }
        .card-header {
            background-color#f5f5f5;
            padding10px15px;
            border-bottom1px solid #e0e0e0;
            font-weight: bold;
        }
        .card-body {
            padding15px;
            color#333;
            line-height1.5;
        }
        .card-footer {
            background-color#fafafa;
            padding10px15px;
            border-top1px solid #e0e0e0;
            text-align: right;
        }
    
</style>
</head>
<body>

    <div id="app">
        <h2 style="text-align: center;">Vue 3 插槽演示 (Options API)</h2>
        
        <my-card>
            <template v-slot:header>
                <span>自定义卡片标题</span>
            </template>

            <p>这是通过<b>默认插槽</b>传递进来的正文内容。没有使用 setup 语法,完全采用传统的 data 和 methods 结构。</p>

            <template #footer>
                <button @click="handleClick">点击触发父组件事件</button>
            </template>
        </my-card>
    </div>

    <script>
        // 从全局对象中获取 createApp
        const { createApp } = Vue;

        // 1. 定义子组件 (使用 Options API)
        constMyCard = {
            name'MyCard',
            template`
                <div class="card">
                    <div class="card-header">
                        <slot name="header">默认标题</slot>
                    </div>
                    
                    <div class="card-body">
                        <slot></slot>
                    </div>
                    
                    <div class="card-footer">
                        <slot name="footer">默认页脚</slot>
                    </div>
                </div>
            `

        };

        // 2. 创建根应用 (使用 Options API)
        constApp = {
            // 注册局部组件
            components: {
                'my-card'MyCard
            },
            // 数据源
            data() {
                return {
                    message'来自父组件的数据'
                }
            },
            // 方法
            methods: {
                handleClick() {
                    alert('点击了插槽中的按钮!提示信息:' + this.message);
                }
            }
        };

        // 3. 挂载应用
        createApp(App).mount('#app');
    
</script>

</body>
</html>

如上面的例子所示,子组件 MyCard 一共定义了三个插槽:

<slot name="header">默认标题</slot>
<slot></slot>
<slot name="footer">默认页脚</slot>

分别对应:

插槽
类型
header
具名插槽
默认插槽
匿名(默认)插槽
footer
具名插槽

具名插槽

当 <slot> 通过 name 属性指定名称时,这种插槽称为具名插槽

例如:

<slot name="header">默认标题</slot>

父组件向该插槽传递内容时,需要使用:

<template v-slot:header>
    自定义标题
</template>

其中 v-slot:header 表示将模板内容插入到名称为 header 的插槽中。

由于 v-slot 使用频率较高,Vue 提供了简写语法:

<template #header>
    自定义标题
</template>

因此v-slot:header 等价于 #header

默认插槽

没有指定 name 的插槽称为默认插槽(也叫匿名插槽)。

<slot></slot>

或者

<slot name="default"></slot>

两者表示的都是默认插槽。

父组件使用默认插槽时有两种写法。

方式一:显式指定 default

<my-card>
        <!--v-slot:default 也可以-->
    <template #default>
        <p>正文内容</p>
    </template>
</my-card>

方式二:直接写内容(最常用)

如果只有默认插槽,可以直接省略 template

<my-card>
    <p>正文内容</p>
</my-card>

Vue 会自动将这部分内容放入默认插槽中。

作用域插槽

普通插槽:父组件只能传模板,拿不到子组件内部数据

作用域插槽:子组件传数据,父组件使用这些数据自定义渲染内容

作用域插槽允许父组件在传递插槽内容时,直接访问子组件暴露的数据。子组件通过插槽 Props 将内部数据传递给父组件,而父组件则使用这些数据来自定义插槽内容的渲染方式。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue 3 作用域插槽 (无 Setup 选项式 API)</title>
    <script src="https://unpkg.com/vue@3.5.17/dist/vue.global.js"></script>
    <style>
        body { font-family: sans-serif; padding20pxbackground#f5f5f5; }
        .card { background: white; padding15pxborder-radius8pxmargin-bottom20pxbox-shadow0 2px 4px rgba(0,0,0,0.1); }
        .user-item { padding12pxborder-bottom1px solid #eeedisplay: flex; justify-content: space-between; align-items: center; }
        .admin-tag { background#52c41acolor: white; padding2px 6pxborder-radius4pxfont-size12px; }
        .guest-tag { background#bfbfbfcolor: white; padding2px 6pxborder-radius4pxfont-size12px; }
    
</style>
</head>
<body>

    <div id="app" class="card">
        <h2>👥 父组件(用户管理面板)</h2>
        <p>当前系统状态:正常</p>
        <hr>

        <user-list :users="userList">
            <template #default="slotProps">
                <div>
                    <strong>{{ slotProps.data.name }}</strong>
                    <span v-if="slotProps.data.role === 'admin'" class="admin-tag">管理员</span>
                    <span v-else class="guest-tag">访客</span>
                </div>
                <div>联系方式:{{ slotProps.data.email }}</div>
            </template>
        </user-list>
    </div>

    <script>
        // 1. 定义子组件 (纯 Options API 风格)
        constUserList = {
            props: ['users'],
            // 在 template 中,通过 :data="user" 把当前循环的用户对象传给外层
            template`
                <div style="margin-transform: translateY( 20px;">
                    <h3>📦 我是子组件(负责循环和布局)</h3>
                    <ul>
                        <li v-for="user in users" :key="user.id" class="user-item)">
                            <slot :data="user">
                                {{ user.name }}
                            </slot>
                        </li>
                    </ul>
                </div>
            `

        };

        // 2. 创建根实例(父组件,同样使用传统的 data 选项)
        Vue.createApp({
            components: {
                'user-list'UserList
            },
            // 使用传统的 data 选项返回响应式数据
            data() {
                return {
                    userList: [
                        { id1name'张三'role'admin'email'zs@example.com' },
                        { id2name'李四'role'guest'email'ls@example.com' },
                        { id3name'王五'role'admin'email'ww@example.com' }
                    ]
                }
            }
        }).mount('#app');
    
</script>

</body>
</html>

在这个示例中,父组件通过 Props 将 userList 传递给子组件,子组件负责遍历用户数据和整体布局。

子组件在作用域插槽中通过 :data="user" 将当前用户对象作为插槽参数暴露出去,父组件则通过 slotProps 获取这些数据,并决定最终的 UI 渲染方式。

作用域插槽的意义在于:它为子组件向父组件提供数据提供了一种机制,使父组件既能访问子组件内部产生的数据,又能保持对渲染结构的完全控制。


群贤毕至

访客