面试官:用Vue3.0写过组件吗?实现一个Modal你会怎么设计?

文摘   2024-11-13 10:46   山西  

你碰到“用Vue3.0写过组件吗?如果想实现一个Modal你会怎么设计?”这种面试问题,直接说不会确实不妥。但其实这类题的核心不是实现难度,而是考察我们对组件化、模块化思维的理解。今天我就分享一下如何从需求分析到具体实现,构建一个Vue3的Modal组件。

首先,我们分析一下这个Modal的常见需求:它需要弹窗的基本元素,比如遮罩层、标题、主体内容,以及“确定”和“取消”按钮。Modal在UI设计中是很常见的,典型的需求就是点击弹出一个内容窗口。

我们还会遇到新增和编辑功能类似、只是细节不同的情况,这种时候完全可以使用一个组件,通过传参控制内容,而不是重复写两个组件,降低冗余,提升代码复用性。

在技术实现上,我们还需考虑几个点:内容展示的多样性,比如能接收字符串、插槽内容,甚至整个html结构;弹窗的独立性,通常会挂载在body之上,避免受到父级结构的限制。

为达到这些需求,我们将使用Vue3的Teleport功能,让Modal的内容在DOM中脱离当前的Vue实例,直接渲染在body节点上。

通常情况下,我会把组件按功能分成独立模块。在这里可以设计如下目录结构:

├── plugins
│ └── modal
│ ├── Content.tsx // 用于渲染Modal的动态内容
│ ├── Modal.vue // Modal组件主结构
│ ├── config.ts // 全局默认配置
│ ├── index.ts // 组件入口文件
│ ├── locale // 国际化支持
│ │ ├── index.ts
│ │ └── lang
│ │ ├── en-US.ts
│ │ └── zh-CN.ts
│ └── modal.type.ts // TS类型声明

我们把Modal组件放到plugins/modal目录下,方便后续通过app.use(Modal)形式注册成为全局插件。

在实现Modal时,最外层可以用Vue3的Teleport组件,传送Modal到body上。Modal的基本结构包括遮罩层、标题栏、内容区域和按钮区域。代码如下:

<Teleport to="body" :disabled="!isTeleport">
    <div v-if="modelValue" class="modal">
        <div class="mask" :style="style" @click="maskClose && !loading && handleCancel()"></div>
        <div class="modal__main">
            <div class="modal__title line line--b">
                <span>{{ title || t("r.title") }}</span>
                <span v-if="close" class="close" @click="!loading && handleCancel()"></span>
            </div>
            <div class="modal__content">
                <Content v-if="typeof content === 'function'" :render="content" />
                <slot v-else>{{ content }}</slot>
            </div>
            <div class="modal__btns line line--t">
                <button :disabled="loading" @click="handleConfirm">{{ t("r.confirm") }}</button>
                <button @click="!loading && handleCancel()">{{ t("r.cancel") }}</button>
            </div>
        </div>
    </div>
</Teleport>

遮罩层与弹窗内容分开处理,点击遮罩层时调用handleCancel,关闭弹窗。内容展示区域则通过判断内容类型(字符串或插槽)选择不同的展示方式。Modal的显示和隐藏通过modelValue控制,Vue3中这种状态可以用v-model绑定到父组件。

Modal内部内容的展示方式有多种,可以是插槽,也可以通过字符串传递内容。以下是通过默认插槽和字符串传递内容的示例:

<!-- 默认插槽 -->
<Modal v-model="show" title="演示 slot">
    <div>hello world~</div>
</Modal>

<!-- 字符串形式 -->
<Modal v-model="show" title="演示 content" content="hello world~" />

这种灵活的内容传递方式非常适合需要定制化的场景,让Modal组件能够轻松处理多种内容需求。

在一些情况下,我们希望Modal通过API调用,而不是直接引用组件标签。Vue2中可以用Vue.extend创建Modal实例,然后挂载到DOM上,但Vue3移除了这个API,我们可以用createVNode实现相同效果。

import { createVNode, render } from 'vue';
import Modal from './Modal.vue';

const container = document.createElement('div');
const vnode = createVNode(Modal);
render(vnode, container);
document.body.appendChild(container);
const instance = vnode.component;

API形式下,我们还可以通过app.config.globalProperties把Modal实例挂载到全局对象上,让其他组件通过$modal进行调用。这种做法适合全局的、临时的Modal需求。

export default {
    install(app) {
        app.config.globalProperties.$modal = {
            show(options) {
                /* 调用 Modal 实例 */
            }
        };
    }
}

Modal的“确定”和“取消”是最基本的交互事件。Vue3的Composition API让事件处理更直观。在setup函数中,我们可以通过getCurrentInstance获取当前组件实例,从而绑定特定事件的处理逻辑。下面的代码展示了事件处理的基本实现:

setup(props, { emit }) {
    const handleConfirm = () => {
        emit('on-confirm');
    };
    const handleCancel = () => {
        emit('on-cancel');
    };

    return { handleConfirm, handleCancel };
}

通过emit函数,我们可以触发父组件监听的事件,比如“on-confirm”和“on-cancel”。API形式调用中,我们还可以为每个Modal实例添加事件监听方法,比如通过_hub来管理事件的回调函数:

app.config.globalProperties.$modal = {
    show({
        onConfirm,
        onCancel
    }) {
        /* 监听 确定、取消 事件 */
        instance._hub = {
            'on-confirm': onConfirm || (() => {}),
            'on-cancel': onCancel || (() => {})
        };
    }
};

实际开发中,Modal组件可能还会涉及国际化和TypeScript支持。国际化可以通过配置文件和locale目录实现,在Vue实例的根组件中设定当前语言环境。TypeScript方面,为组件设置类型声明可以增强代码的健壮性和可读性。

那如果面试官问你这个问题,你可以根据下面的思路回答。

是的,我用Vue3写过组件。关于Modal组件的设计,我通常会把需求分解为几个部分:遮罩层、标题、内容展示区和交互按钮(如确定、取消)。实现时可以用Vue3的Teleport组件将内容挂载到body上,避免被父级DOM结构影响。

内容区设计上可以根据内容的传入类型(字符串、HTML结构、或插槽)来灵活渲染。我还会提供一个API形式的调用方法,让Modal能够在Vue实例之外被独立调用,便于在项目的不同模块中快速使用。

目前,对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。

虎哥私藏精品 热门推荐

虎哥作为一名老码农,整理了全网最前端资料合集

资料包含了《前端面试题PDF合集》、《前端学习视频》、《前端项目及源码》,总量高达108GB。

全部免费领取全面满足各个阶段程序员的学习需求!

web前端专栏
回复 javascript,获取前端面试题。分享前端教程,AI编程,AI工具,Tailwind CSS,Tailwind组件,javascript教程,webstorm教程,html教程,css教程,nodejs教程,vue教程。
 最新文章