vue.js前端网页弹框异步行为示例分析

目录
  • 1. 序
  • 2. 找两个弹框组件看看
  • 3. 自己肝一个
    • 3.1. 封装 Promise
    • 3.2. 确定时允许异步等待
    • 3.3. 细节完善
    • 3.4. 改革

1. 序

网页弹框是个很常见的功能,比如需要告知用户消息的时候 (Alert),需要用户进行确认的时候 (Confirm),需要用户补充一点信息的时候 (Prompt) …… 甚至可以弹框让用户填写表单 (Modal Dialog)。

弹框之后,开发者需要知道这个弹框是什么时候关闭以便进行接下来的操作。

在比较古老的 UI 组件中,这个事情是通过事件回调来进行的,大概长这样:

showDialog(content, title, {
    closed: function() { console.log("对话框已关闭"); }
})

不过对话框的行为。你看,它弹出来了,但它不会阻塞后面的代码,而且开发者并不知道什么时候关闭,因为这是用户行为。既然是异步,封装成 Promise 使用 await 语法来调用会更舒服一些。简单的封装大概可以这样:

async function asyncShowDialog(content, title, options) {
    return new Promise(resolve => {
        showDialog(content, title, {
            ...options,
            closed: resolve
        });
    });
}

(async () => {
    await asyncShowDialog(content, title);
    console.log("对话框已关闭");
})();

弹框的基本的异步行为就是这么简单,就这么结束?心有不甘,再研究研究!

2. 找两个弹框组件看看

Ant Design Vue 使用了事件的形式,点击“确定”按钮会触发 ok 事件,点击“取消”或者右上角的关闭按钮会触发 cancel 事件。

这两个事件处理函数通过参数对象的 onOk 和 onCancel 属性挂载进去。看起来平淡无奇,但如果处理事件返回的是一个 Promise 对象,点击按钮之后会出现加载动画并等待直到 Promise 对象完成之后才会关闭对话框。这种设计把异步等待动画组合到弹框当中,简洁直观,代码写起来也很方便。以 confirm 对话框为例:

Modal.confirm({
    ...
    onOk() {
        // 点击「确定」按钮后,会显示加载动画,并在一秒后关闭对话框
        return new Promise(resolve => {
            setTimeout(resolve, 1000);
        });
    }
    ...
});

而 Element Plus 使用了 Promise 形式,打开对话框时,并不是把确定或取消的处理函数以参数的形式传入,而是直接返回一个 Promise 对象,供开发者通过 .then()/.catch() 或者 await 处理。示例:

try {
    await ElMessageBox.confirm(...);
    // 按下确定按钮在这里处理
} catch(err) {
    // 按下取消按钮在这里处理
}

Element Plus 的这种处理方式,要在对话框关闭之后才能处理业务。这也是使用 Promise 的局限 —— 对于一个已经封装好的 Promise 对象,很难在其中插入新的逻辑。

如果使用 ElMessageBox 的时候也想像 Ant Design 那样在关闭前进行一些异步操作,只能去找找看它是否提供了关闭前的处理事件。一找还真找到了,它有 beforeClose 事件。该事件的处理函数签名是 beforeClose(action, instance, done)

action 表示按了哪个按钮,取值可能是 "confirm""cancel" 和 "close"(不用解释了吧)。

instance 是 MessageBox 实例,可以使用它来控制一些界面效果,比如

instance.confirmButtonLoading = true 会在“确定”按钮上显示加载动画,instance.confirmButtonText 可以用来改变按钮文本 …… 这些操作在进行异步等待时可以提供更好的用户体验。

done 是一个函数,调用它表示 beforeClose() 的异步处理完成,对话框现在可以关闭了!

所以类似 Ant Design 的处理可以这样写:

try {
    await ElMessageBox.confirm({
        ...
        beforeClose: async (action, instance, done) => {
            await new Promise(resolve => setTimeout(resolve, 1000));
            done();
        }
    });
    // 按下确定按钮在这里处理
} catch(err) {
    // 按下取消按钮在这里处理
}

3. 自己肝一个

分析了两个弹框组件的行为处理,我们已经知道,一个体验良好的弹框组件应该具备如下特征:

  • 提供基于 Promise 的异步控制能力(Ant Design Vue 虽然没有提供,但是像“序”中那样封装一下就可以)。
  • 允许在关闭前进行一些操作,甚至是异步操作。
  • 提供异步加载过程中的界面反馈,而且最好不需要开发者来控制(从这点来说 Ant Design 比 Element Plus 方便)。

接下来,我们自己写一个,看看是如何实现上述特征的。不过,既然我们主要研究的是行为而不是数据处理,所以不用 Vue 框架,直接用 DOM 操作,然后引入 jQuery 来简化 DOM 处理。

对话框的 HTML 骨架也比较简单:下面一层蒙板,上面一个固定大小的 <div> 层,内部再用 <div> 划分成标题、内容、操作区三块:

<div class="dialog" id="dialogTemplate">
  <div class="dialog-window">
    <div class="dialog-title">对话框标题</div>
    <div class="dialog-content">对话框的内容</div>
    <div class="dialog-operation">
      <button type="button" class="ensure-button">确定</button>
      <button type="button" class="cancel-button">取消</button>
    </div>
  </div>
</div>

这里把它定义成一个模板,希望每次都从它克隆一个 DOM 出来呈现,关闭即毁。

样式表的内容较长,可以从后面的示例链接去获取。代码及代码的进化过程才是本文的重点。

最简单的呈现是利用 jQuery 克隆一个显示出来,但显示前一定要记得删除掉 id 属性,并把它添加到 <body> 中去:

$("#dialogTemplate").clone().removeAttr("id").appendTo("body").show();

把它封装成一个函数,并且添加对「确定」和「取消」按钮的处理:

function showDialog(content, title) {
    const $dialog = $("#dialogTemplate").clone().removeAttr("id");
    // 设置对话框的标题和内容(简单示例,所以只处理文本)
    $dialog.find(".dialog-title").text(title);
    $dialog.find(".dialog-content").text(content);
    // 通过事件代理(也可以不用代理)处理两个按钮事件
    $dialog
        .on("click", ".ensure-button", () => {
            $dialog.remove();
        })
        .on("click", ".cancel-button", () => {
            $dialog.remove();
        });
    $dialog.appendTo("body").show();
}

弹框的基本逻辑就出来了。现在做两点优化:① 把 $dialog.remove() 封装成函数,便于对关闭对话框进行统一处理(代码复用) ② 使用 .show() 呈现太过生硬,改为 fadeIn(200);同理,应该在 .remove() 之前先fadeOut(200)

function showDialog(...) {
    ...
    const destory = () => {
        $dialog.fadeOut(200, () => $dialog.remove());
    };
    $dialog
        .on("click", ".ensure-button", destroy)
        .on("click", ".cancel-button", destroy);
    $dialog.appendTo("body").fadeIn(200);
}

3.1. 封装 Promise

到这一步,弹框已经可以正常弹出/关闭了,但是没办法注入「确定」或「取消」的逻辑代码。前面提到可以通过事件或 Promise 两种形式来提供接口,这里使用 Promise 的方式。如果点「确定」就 resolve,点「取消」就 reject。

function showDialog(...) {
    ...
    const promise = new Promise((resolve, reject) => {
        $dialog
            .on("click", ".ensure-button", () => {
                destroy();
                resolve("ok");
            })
            .on("click", ".cancel-button", () => {
                destroy();
                reject("cancel");
            });
    });
    $dialog.appendTo("body").fadeIn(200);
    return promise();
}

封装好了,但有个问题:destroy() 是个异步过程,但代码并没有等它结束,所以 showDialog() 完成异步处理之后还在进行 fadeOut() 操作和 remove() 操作。要解决这个问题,只能封装 destory()。当然调用的时候也别忘了加 await,而加 await 就要把外层函数声明为 async

function showDialog(...) {
    ...
    const destory = () => {
        return new Promise(resolve => {
            $dialog.fadeOut(200, () => {
                $dialog.remove();
                resolve();
            });
        });
    };
     const promise = new Promise((resolve, reject) => {
        $dialog
            .on("click", ".ensure-button", async () => {
                await destroy();
                resolve("ok");
            })
            .on("click", ".cancel-button", async () => {
                await destroy();
                reject("cancel");
            });
    });
    ...
}

3.2. 确定时允许异步等待

不管「确定」还是「取消」都可以保持弹框显示,进行异步等待。但作为示例,这里只处理「确定」的情况。

这个异步等待过程要注入到从弹窗中,只能采用参数注入的形式。所以需要为 showDialog() 添加一个 options 参数,允许注入一个处理函数给 onOk 属性,如果这个处理函数返回 Promise Like,就进行异步等待。

先修改 showDialog() 接口:

function showDialog(conent, title, options = {}) { ... }

然后再处理 $dialog.on("click", ".ensure-button", ...) 事件:

$dialog
    .on("click", ".ensure-button", async () => {
        const { onOk } = options;
        // 从 options 中拿到 onOk,如果它是一个函数才需要等待处理
        if (typeof onOk === "function") {
            const r = onOk();
            // 判断 onOk() 的结果是不是一个 Promise Like 对象
            // 只有 Promise Like 对象才需要异步等待
            if (typeof r?.then === "function") {
                const $button = $dialog.find(".ensure-button");
                // 异步等待过程中需要给用户一定反馈
                // 这里偷懒没有使用加载动画,只用文字来进行反馈
                $button.text("处理中...");
                await r;
                // 因为在完成之后,关闭之前有 200 毫秒的渐隐过程,
                // 所以把按钮文本改为“完成”,给用户及时反馈是有必要的
                $button.text("完成");
            }
        }
        await destroy();
        resolve("ok");
    })

现在这个弹框的行为基本上处理完了,调用的示例:

const result = await showDialog(
    "你好,这里是对话框的内容",
    "打个招呼",
    {
        onOk: () => new Promise((resolve) => { setTimeout(resolve, 3000); })
    }
).catch(msg => msg);  // 这里把取消引起的 reject 变成 resolve,避免使用 try...catch...

console.log(result === "ok" ? "按下确定" : "按下取消");

3.3. 细节完善

都有对话框了最后还用 console.log(...) 实在有点不妥,直接弹框提示消息不更好?

但是现在的 showDialog() 只处理了 Confirm 弹框,没有处理 Alert 弹框 …… 问题不大,在 options 里加个 type 好了。如果 type 是 "alert" 就把「取消」按钮干掉。

async function showDialog(content, title, options = {}) {
    ...

    if (options.type === "alert") {
        $dialog.find(".cancel-button").remove();
    }
    ...
}

然后,最后的 console.log(...) 可以进化一下:

showDialog(result === "ok" ? "按下确定" : "按下取消", "提示", { type: "alert" });

3.4. 改革

如果不喜欢在 options 中注入处理函数,还可以换个法子,在返回的 Promise 对象中注入。先在 .ensure-button 的事件中把 const { onOk } = options 改为 const { onOk } = promise,也就是从 promise 中获取注入的 onOk。然后改调用部分:

const dialog = showDialog("你好,这里是对话框的内容", "打个招呼");
// 把处理函数注入到 promise 的 onOk
dialog.onOk = () => new Promise((resolve) => { setTimeout(resolve, 3000); });
const result = await dialog.catch(msg => msg);
showDialog(result === "ok" ? "按下确定" : "按下取消", "提示", { type: "alert" });

这里有几点要注意:

dialog 必须只能是 showDialog() 直接返回的。如果调用了 .catch() 将会得到另一个 Promise 对象,此时再注入 onOk 就注入不到 showDialog() 里面产生的那个 Promise 对象上了。

showDialog() 不能声明为 async 的,否则返回出来的 Promise 对象也不是里面产生的那一个。

别忘了 await

以上就是vue.js前端网页弹框异步行为示例分析的详细内容,更多关于vue.js前端异步网页弹框的资料请关注我们其它相关文章!

(0)

相关推荐

  • 一些常用弹出窗口/拖放/异步文件上传等实用代码

    久不出技术类文章,我都忘了自己是一程序员啦......今天写一点工作中遇到的东西,大家共同学习,反正也比较浅显了. 弹出窗口 我们在工作中,经常会碰到弹出窗口类应用,有时候还需要一点遮盖层: 这类圆角弹出框其实用得还是很广泛的,用CSS3可以很容易的出现,但是考虑到浏览器兼容问题,这类还是需要用图片实现了 主要代码如下: 复制代码 代码如下: //弹出层剧中 function popup(popupName) { var _scrollHeight = $(document).scrollTop

  • JS实现在网页中弹出一个输入框的方法

    本文实例讲述了JS实现在网页中弹出一个输入框的方法.分享给大家供大家参考.具体分析如下: 习惯了使用早期给电脑文件夹设置密码的朋友一定还记得那种弹出一个输入框,然后让你输入密码在登录查看的效果吧,这个JS代码就是实现这个在当前页面中弹出一个输入框,当然你可以输入任何东西 <html> <head> <title>js输入对话框</title> </head> <body> <script language="javas

  • javascript 异步的innerHTML使用分析

    当然,这个分时加载技术只是一个辅助技术,本身没有添加节点的能力.如今,另一种更奇特的技术Asynchronous innerHTML又被开发出来了,不能不赞一下外国人在这方面研究是非常超前的. 复制代码 代码如下: function asyncInnerHTML(HTML, callback) { var temp = document.createElement('div'), frag = document.createDocumentFragment(); temp.innerHTML =

  • jQuery+Ajax+PHP弹出层异步登录效果(附源码下载)

    弹出层主要用于展示丰富的页面信息,还有一个更好的应用是弹出表单层丰富交互应用.常见的应用有弹出登录表单层,用户提交登录信息,后台验证登录成功后,弹出层消失,主页面局部刷新用户信息.本文我们将给大家介绍如何使用jQuery+Ajax+PHP弹出层异步登录的应用. 效果展示        源码下载 HTML 由于本例UI部分使用了Bootstrap,以及使用jQuery插件,因此需要事先载入相关文件,推荐大家使用相关的CDN资源. <link rel="stylesheet" hre

  • javascript制作的网页侧边弹出框思路及实现代码

    到周末了,明天该总结一下了,感觉学到了些东西,又感觉没学多少东西,具体明天再分析吧,先来看看今天要分析的问题. 这样的图片相比大家都很熟悉: ------------ 今天我们就来分析制作一下,先来介绍下这种弹出框的特点: * 始终依附在页面边框上 * 不随页面的上升下降而改变位置 * 鼠标经过时,会弹出详细信息,离开时,恢复最初状态 这样我们就能大概想到几个可能会用到的功能:postion的绝对定位:鼠标经过离开的监听和方法:这些是肯定会用的,但是,除了这些还用到了哪些呢,究竟又是怎么是实现的

  • vue.js前端网页弹框异步行为示例分析

    目录 1. 序 2. 找两个弹框组件看看 3. 自己肝一个 3.1. 封装 Promise 3.2. 确定时允许异步等待 3.3. 细节完善 3.4. 改革 1. 序 网页弹框是个很常见的功能,比如需要告知用户消息的时候 (Alert),需要用户进行确认的时候 (Confirm),需要用户补充一点信息的时候 (Prompt) -- 甚至可以弹框让用户填写表单 (Modal Dialog). 弹框之后,开发者需要知道这个弹框是什么时候关闭以便进行接下来的操作. 在比较古老的 UI 组件中,这个事情

  • vue教程之toast弹框全局调用示例详解

    本文实例为大家分享了vue toast弹框全局调用示例,供大家参考,具体内容如下 1.首选新建一个toast.vue模板文件: <template> <transition :name="fadeIn"> <div class="alertBox" v-show="show"> <div class="alert-mask" v-show="isShowMask"&

  • vue.js实现只弹一次弹框

    核心代码是 getCookie()部分,控制弹框的显示隐藏则在 created()中. <template> <div v-if="isShow"> <!--最外层背景--> <div class="popup_container"> <!--居中的容器--> <img @click="noPopup" src="delete.png" alt="&q

  • Vue.js 前端路由和异步组件介绍

    目录 文章目标 P6 P6+ ~ P7 一.背景 二.前端路由特性 三.面试!!! 四.Hash 原理及实现 1.特性 2.如何更改 hash 3.手动实现一个基于 hash 的路由 五.History 原理及实现 1.HTML5 History 常用的 API 2.pushState/replaceState 的参数 3.History 的特性 4.面试!!! 5.手动实现一个基于 History 的路由 六.Vue-Router 1.router 使用 2.动态路由匹配 3.响应路由参数的变

  • Vue.js前端框架之事件处理小结

    1. v-on 事件监听 监听DOM事件使用 v-on 指令.该指令通常在模板中直接使用,在触发事件时会执行一些 JavaScript 代码. v-on 指令的基本用法 (1)在 HTML 中使用 v-on 指令,后面可以是所有的原生事件名称.基本用法如下: <button v-on:click="show">显示</button> 将 click 单击事件绑定到 show 方法中,单击"显示"按钮时,执行 show() 方法,show()

  • 基于Vue.js实现简单搜索框

    在github上看到的练习,看个遍代码后自己再练一遍,先放原址:https://github.com/lavyun/vue-demo-search 主要用到的知识很简单,简单的vuejs2.0的知识就够了.源码用了.vue构建和ES6,用了webpack打包等等.我资历还浅,先用一个简单的.js的写. 先看效果 这里有两个组件,一个组件是logo部分的,一个是搜索框部分的. html html很简单,就是引用两个组件. <div id="app"> <logo-pic

  • C#实现在前端网页弹出警告对话框(alert)的方法

    本文实例讲述了C#实现在前端网页弹出警告对话框(alert)的方法.分享给大家供大家参考.具体如下: 通常我们通过JS生成警告对话框,下面的代码可以帮助你在点击runat=server的按钮时从服务器端生成alert警告对话框 private void MessaegBox(String msg) { Page.Controls.Add(new LiteralControl("window.alert('"+msg+"')")); } protected void

  • 使用 Vue.js 仿百度搜索框的实例代码

    整理文档,搜刮出一个使用 Vue.js 仿百度搜索框的实例代码,稍微整理精简一下做下分享. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue demo</title> <style type="text/css"> .bg { background: #ccc; } </style> <s

  • vue.js select下拉框绑定和取值方法

    最近在做mui+vue.js的移动项目,遇到了这个解决了,所以记录一下: 1.绑定select下拉框的代码很简单sendlist就是下拉框的集合,这个可以去看vue.js的文档: 地址:https://cn.vuejs.org/v2/api/ :value绑定的值就是这个下拉框对应的value值 <select id="sendSybol" v-model="searchDto.sendSymbolId"> <option v-for="

  • 详解Vue.js 可拖放文本框组件的使用

    可拖放文本框允许用户通过拖动备选项至文本框来确定输入,其实也可以说是 combobox 的一种变形. 与 combobox 相比,这种组件能让用户更加直观的看到所有备选项,并且可以是多个输入共用一组备选项. 类似的组件也曾用在 3D Windrose App,Graph Maker App 等多个 app 里. 注册组件 注册可拖放文本框组件(其实就是将封装好的这部分代码 Ctrl+C and Ctrl+V). <script type="text/x-template" id=

随机推荐