You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js模态框slotContent传值为null问题求助

模态窗口slotContent无法传递至Vue.js模板

中心类型模态框的所有参数均可正常传递,但向slotContent传递对象或纯文本时,在Modal.vue模板中该值始终为null。调试ModalPlugin的show()方法时能确认内容已传入,但在以下代码片段中slotContent显示为null:

<template v-else-if="type === 'bottom'">
    <component v-if="slotContent" :is="slotContent" />
</template>

相关代码

ModalPlugin.js

import { reactive } from "vue";
import Modal from "@/views/components/Modal.vue";

export default {
  install(app) {
    const state = reactive({
      visible: false,
      type: "center",
      title: "",
      message: "",
      icon: "",
      hasAction: false,
      actionText: "",
      callback: null,
      slotContent: null,
    });

    app.component("Modal", Modal);

    app.config.globalProperties.$modal = {
      show(options) {
        Object.assign(state, options, { visible: true });
      },
      hide() {
        Object.assign(state, {
          visible: false,
          slotContent: null,
          callback: null,
        });
      },
    };

    app.provide("modalState", state);
  },
};
<template>
    <div
        v-if="visible"
        :class="['modal', type]"
        @click.self="close"
    >
        <div class="modal__content">

            <button class="modal__close" @click="close">
                <img src="~@/assets/images/modal/close.png" alt="">
            </button>

            <template v-if="type === 'center'">
                <div class="modal__image">
                    <img :src="icon" alt="" />
                </div>
                <p class="title">{{ title }}</p>
                <p class="text" v-html="message"></p>

                <button v-if="hasAction" class="btn btn__primary" @click="action">
                    {{ actionText }}
                </button>
            </template>

            <template v-else-if="type === 'bottom'">
                <component v-if="slotContent" :is="slotContent" />
            </template>

        </div>
    </div>
</template>


<script>
import '../../styles/components/Modal.scss';

export default {
    name: "Modal",
    props: {
        visible: { type: Boolean, default: false },
        type: { type: String, default: "center" },
        title: { type: String, default: "" },
        message: { type: String, default: "" },
        icon: { type: String, default: "" },
        hasAction: { type: Boolean, default: false },
        actionText: { type: String, default: "OK" },
        slotContent: { type: [String, Object, Function], default: null },
    },
    emits: ["close", "action"],
    methods: {
        close() {
            this.$emit("close");
        },
        action() {
            this.$emit("action");
        },
    }
};
</script>

ShopView.vue

<template>
    <div class="shop">
        <div class="shop__list">
            <button
                class="shop__list__btn glitter"
                @click="openModal('boost', 'Boost')">
                <img src="~@/assets/images/shop/boost.png" alt="">
                <p>Прокачаться</p>
            </button>

            <button
                class="shop__list__btn glitter"
                @click="openModal('exchange', Exchange)">
                <img src="~@/assets/images/shop/exchange.png" alt="">
                <p>Обменник</p>
            </button>

            <button
                class="shop__list__btn glitter"
                @click="openModal('buyDiamonds', BuyDiamond)">
                <img src="~@/assets/images/shop/diamond.png" alt="">
                <p>Купить алмазы</p>
            </button>

            <button
                class="shop__list__btn glitter"
                @click="openModal('withdraw', Withdrawal)">
                <img src="~@/assets/images/shop/withdraw.png" alt="">
                <p>Вывести деньги</p>
            </button>
        </div>
    </div>
</template>

<script>
import '../styles/components/Shop.scss';
import Boost from './components/shop/Boost.vue';
import BuyDiamond from './components/shop/BuyDiamond.vue';
import Exchange from './components/shop/Exchange.vue';
import Withdrawal from './components/shop/Withdrawal.vue';

export default {
    name: "ShopView",
    components: {
        Boost,
        BuyDiamond,
        Exchange,
        Withdrawal
    },
    methods: {
        openModal(type, slotContent) {
            this.$modal.show({
                type: 'bottom',
                slotContent,
            });
        }
    },
};
</script>

问题根源与修复方案

问题核心

Modal组件通过props接收参数,但插件中仅将状态存入reactive对象,未将状态与Modal组件的props绑定,导致slotContent等参数无法同步到组件内部。中心类型能工作是因为可能手动传递了对应props,但slotContent被遗漏。

修复步骤

  1. 修改Modal.vue,通过inject接收全局状态
    移除props,直接注入插件提供的modalState,利用响应式特性同步状态:

    <template>
        <div
            v-if="modalState.visible"
            :class="['modal', modalState.type]"
            @click.self="close"
        >
            <div class="modal__content">
    
                <button class="modal__close" @click="close">
                    <img src="~@/assets/images/modal/close.png" alt="">
                </button>
    
                <template v-if="modalState.type === 'center'">
                    <div class="modal__image">
                        <img :src="modalState.icon" alt="" />
                    </div>
                    <p class="title">{{ modalState.title }}</p>
                    <p class="text" v-html="modalState.message"></p>
    
                    <button v-if="modalState.hasAction" class="btn btn__primary" @click="action">
                        {{ modalState.actionText }}
                    </button>
                </template>
    
                <template v-else-if="modalState.type === 'bottom'">
                    <component v-if="modalState.slotContent" :is="modalState.slotContent" />
                </template>
    
            </div>
        </div>
    </template>
    
    <script>
    import '../../styles/components/Modal.scss';
    import { inject } from 'vue';
    
    export default {
        name: "Modal",
        emits: ["close", "action"],
        setup() {
            const modalState = inject('modalState');
            return { modalState };
        },
        methods: {
            close() {
                this.$modal.hide();
            },
            action() {
                this.modalState.callback?.();
                this.$modal.hide();
            },
        }
    };
    </script>
    
  2. 确认全局挂载Modal的方式
    在入口文件或App.vue中直接挂载Modal组件,无需传递props:

    <template>
      <div id="app">
        <!-- 其他业务组件 -->
        <Modal />
      </div>
    </template>
    
  3. ShopView.vue逻辑保持不变
    原有的openModal调用逻辑无需修改,$modal.show()会自动将参数合并到全局响应式状态中,Modal组件会同步更新。

内容的提问来源于stack exchange,提问作者Андрей Дмитрук

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:04:55