前言

最近接到一个需求,需要在一些敏感操作进行前要求输入账号和密码,然后将输入的账号和密码加到接口请求的
header
里面。如果每个页面都去手动导入弹窗组件,在点击按钮后弹出弹窗。再拿到弹窗返回的账号密码后去请求接口也太累了,那么有没有更简单的实现方式呢?

函数式弹窗的使用场景

首先我们来看看什么是函数式弹窗?

函数式弹窗是一种使用函数来创建弹窗的技术。它可以简化弹窗的使用,只需要在需要弹窗的地方调用函数就可以了。那么这里使用函数式弹窗就能完美的解决我们的问题。

我们只需要封装一个
showPasswordDialog
函数,调用该函数后会弹出一个弹窗。该函数会返回一个
resolve
后的值就是账号密码的
Promise
。然后在
http
请求拦截器中加一个
needValidatePassword
字段,拦截请求时如果该字段为
true
,就
await
调用
showPasswordDialog
函数。拿到账号和密码后塞到请求的header里面。这样就我们就只需要在发起请求的地方加一个
needValidatePassword: true
配置就行了。

先来实现一个弹窗组件

这个是简化后
template
中的代码,和Element Plus官网中的demo代码差不多,没有什么说的。

<template>
  <el-dialog :model-value="visible" title="账号和密码" @close="handleClose">
    <!-- 省略账号、密码表单部分... -->
    <el-button type="primary" @click="submitForm()">提交</el-button>
  </el-dialog>
</template>

这个是简化后的
script
代码,大部分和Element Plus官网的demo代码差不多。需要注意的是我们这里将
close
关闭事件和
confirm
确认事件定义在了
props
中,而不是在
emits
中,因为后面函数式组件会通过
props
将这两个回调传入进来。具体的我们下面会讲。

<script setup lang="ts">
interface Props {
  visible: boolean;
  close?: () => void;
  confirm?: (data) => void;
}

const props = defineProps<Props>();

const emit = defineEmits(["update:visible"]);

const submitForm = async () => {
  // 省略validate表单校验的代码
  // 这里的data为表单中输入的账号密码
  props.confirm?.(data);
  handleClose();
};

const handleClose = () => {
  emit("update:visible", false);
  props.close?.();
};
</script>

再基于弹窗组件实现函数式弹窗

createApp
函数和
app.mount
方法

createApp
函数会创建和返回一个
vue

应用实例
,也就是我们平时常说的
app
,该函数接受两个参数。第一个参数为接收一个组件,也就是我们平时写的
vue
文件。第二个参数为可选的对象,这个对象会传递给第一个参数组件的
props

举个例子:

import MyComponent from "./MyComponent"

const app = createApp(MyComponent, {
  visible: true
})

在这个例子中我们基于
MyComponent
组件生成了一个
app
应用实例,如果
MyComponent
组件的
props
中有定义
visible
,那么
visible
就会被赋值为
true

调用
createApp
函数创建的这个
应用实例app
实际就是在内存中创建的一个对象,并没有渲染到浏览器的dom上面。这个时候我们就要调用
应用实例app
暴露出来的
mount
方法将这个组件挂载到真实的dom上面去。
mount
方法接收一个“容器”参数,用于将组件挂载上去,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串。比如下面这个例子是将组件挂载到
body
上面:

app.mount(document.body)

app
提供了很多方法和属性,详见
vue官网

封装一个
showPasswordDialog
函数

首先我们来看看期望如何使用
showPasswordDialog
函数?

我们希望
showPasswordDialog
函数返回一个
Promise

resolve
的值就是弹窗中输入的表单。例如,我们可以使用以下代码使用
showPasswordDialog
函数:

try {
  // 调用这个就会弹出弹窗
    const res: RuleForm = await showPasswordDialog();
    // 这个res就是输入的账号密码
    console.log("res", res);
  } catch (error) {
    console.log(error);
  }

具体如何实现
showPasswordDialog
函数?

经过上面的介绍我们知道了可以调用
createApp
函数传入指定组件生成
app
,然后使用
app.mount
方法将这个组件挂载到指定的dom上面去。那么现在思路就清晰了,我们只需要将我们前面实现的弹窗组件作为第一个参数传递给
createApp
函数。第二个参数传入一个对象给弹窗组件的props,用以控制打开弹窗和注册弹窗关闭和确认的事件回调。下面是实现的
showPasswordDialog
函数

import { App, createApp } from "vue";
import PasswordDialog from "./index.vue";
// 这个index.vue就是我们前面实现的弹窗组件

export async function showPasswordDialog(): Promise<RuleForm> {
  return new Promise((resolve, reject) => {
    let mountNode = document.createElement("div");
    let dialogApp: App<Element> | undefined = createApp(PasswordDialog, {
      visible: true,
      close: () => {
        if (dialogApp) {
          dialogApp.unmount();
          document.body.removeChild(mountNode);
          dialogApp = undefined;
          reject("close");
        }
      },
      confirm: (res: RuleForm) => {
        resolve(res);
        dialogApp?.unmount();
        document.body.removeChild(mountNode);
        dialogApp = undefined;
      },
    });
    document.body.appendChild(mountNode);
    dialogApp.mount(mountNode);
  });
}

在这个
showPasswordDialog
函数中我们先创建了一个
div
元素,再将弹窗组件传递给了
createApp
函数生成一个
dialogApp
的实例。然后将创建的div元素挂载到
body
上面,再调用
mount
方法将我们的弹窗组件挂载到创建的
div
元素上,至此我们实现了通过函数式调用将弹窗组件渲染到body中。

现在我们再来看看传入到
createApp
函数的第二个对象参数,我们给这个对象分别传入了
visible
属性、
close

confirm
回调方法,分别会赋值给弹窗组件props中的
visible

close

confirm

弹窗组件中触发关闭事件时会调用
props.close?.()
,实际这里就是在调用我们传入的
close
回调方法。在这个方法中我们调用了实例的
unmount
方法卸载组件,然后将创建的弹窗组件dom从body中移除,并且返回一个
reject

Promise

当我们将账号和密码输入完成后,会调用
props.confirm?.(ruleForm)
,这里的
ruleForm
就是我们表单中的账号和密码。实际这里就是在调用我们传入的
confirm
回调方法,接下来同样也是卸载组件和移除弹窗组件生成的dom,并且返回一个
resolve
值为账号密码表单的
Promise

总结

这篇文章主要介绍了如何创建函数式弹窗:

  1. 创建一个常规的弹窗组件,有点不同的是
    close

    confirm
    事件不是定义在
    emits
    中,而是作为回调定义在
    props
    中。

  2. 创建一个
    showPasswordDialog
    函数,该函数返回一个
    Promise

    resolve
    的值就是我们弹窗中输入的表单。

  3. 调用
    createApp
    函数将步骤一的弹窗组件作为第一个参数传入,并且第二个对象参数中传入属性
    visible

    true
    打开弹窗和注入弹窗
    close
    关闭和
    confirm
    确认的回调。

  4. 使用者只需
    await
    调用
    showPasswordDialog
    就可以打开弹窗和拿到表单中填入的账号和密码。

如果我的文章对你有点帮助,欢迎关注公众号:【欧阳码农】。你的支持就是我创造的最大动力,感谢感谢!

标签: none

添加新评论