最近在看vue组件库 Plain UI 时,发现一个比较有趣的异步弹框组件写法,操作如下:
<div class="dialog">
<input type="text" name="message">
<button type="button">cancel</button>
<button type="button">confirm</button>
</div>
(async ()=>{
let message = await openDialog();
console.log("弹窗信息",message)
})()
在异步函数中打开弹窗 openDialog
方法,当用户点击 confirm
按钮后,弹窗关闭,返回输入框信息。
openDialog
方法可以很方便的通过promise实现,不过在看组件库源码时,发现对方是用defer 实现的,在promise兼容性还不是很好的时代 JQuery 就已经有 deferred.promise() 方法了,这里顺便也做了温习。
defer
方法:
const defer = () => {
const def = {}
def.promise = new Promise((resolve, reject) => {
def.resolve = resolve
def.reject = reject
})
return def
}
defer
方法其实返回的也是一个promise,并且将 resolve
和 reject
方法拆开,这样我们就可以选择在适当的时机调用 resolve
或者 reject
方法了。
const dialogController = () => {
let dfd = null
const confirmBtn = document.getElementById('confirm')
// 点击确定按钮
confirmBtn.addEventListener('click', () => {
// 隐藏弹窗
dialogEl.hide()
// resolve输入框信息给用户
dfd.resolve(inputEl.value)
})
return () => {
dfd = defer()
dialogEl.show()
return dfd.promise
}
}
获得打开弹窗promise方法:
const openDialog = dialogController()
控制弹窗的打开,在异步函数中如果用户点击了弹窗确定按钮,关闭弹窗,获得输入信息。
const controlBtn = document.getElementById('control')
controlBtn.addEventListener('click', async () => {
const message = await openDialog()
console.log("弹窗输入框信息:",message)
})
这种方式可以方便我们封装常用的业务组件,之前在看 axios.cancel
源码时里面也是使用这种套路,灵活且实用。
通过 defer
方式实现的弹窗代码:
<html>
<head>
<title>defer promise</title>
<style>
.dialog {
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
position: fixed;
align-items: center;
pointer-events: none;
justify-content: center;
}
.dialog .mask {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
opacity: 0;
transition: 0.3s;
background-color: rgba(0, 0, 0, 0.4);
}
.dialog-content {
padding: 20px;
transition: 0.2s;
opacity: 0;
transform: scale(0.95);
background-color: #fff;
}
.dialog.visible {
pointer-events: all;
}
.dialog.visible .mask {
opacity: 1;
}
.dialog.visible .dialog-content {
opacity: 1;
transform: scale(1);
}
</style>
</head>
<body>
<div class="container">
<button id="control">显示弹窗</button>
<div class="dialog" id="dialog">
<div class="mask" onclick="this.parentNode.classList.remove('visible')"></div>
<div class="dialog-content">
<input type="text" id="content" />
<button id="confirm">确定</button>
</div>
</div>
</div>
<script>
const defer = () => {
const def = {}
def.promise = new Promise((resolve, reject) => {
def.resolve = resolve
def.reject = reject
})
return def
}
;(() => {
const inputEl = document.getElementById('content')
const dialogEl = document.getElementById('dialog')
dialogEl.show = () => dialogEl.classList.add('visible')
dialogEl.hide = () => dialogEl.classList.remove('visible')
const dialogController = () => {
let dfd = null
const confirmBtn = document.getElementById('confirm')
confirmBtn.addEventListener('click', () => {
dialogEl.hide()
dfd.resolve(inputEl.value)
})
return () => {
dfd = defer()
dialogEl.show()
return dfd.promise
}
}
const openDialog = dialogController()
const controlBtn = document.getElementById('control')
controlBtn.addEventListener('click', async () => {
const message = await openDialog()
console.log('弹窗输入框信息:', message)
})
})()
</script>
</body>
</html>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。