最近在看 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>