利用async/await 關鍵字,咱們能夠優雅的處理異步邏輯。css
好比調用網絡接口能夠直接await fetch(……)vue
事實上,在處理頁面交互邏輯時使用async/await,也能夠起到很好的解耦做用,寫出更清晰的代碼。react
好比獲取Modal彈框的交互結果:git
直接看效果: DEMO on githubgithub
後臺系統在交互設計上常常使用模態彈框。不少UI框架都提供了方便易用的Modal組件,好比Ant design: Antd Modalweb
import { Modal, Button } from 'antd';
class App extends React.Component {
state = { visible: false } // 仍是須要維護一個visible state用來控制Modal的顯示狀態
showModal = () => {
this.setState({
visible: true,
});
}
handleOk = (e) => {
this.setState({
visible: false,
});
}
handleCancel = (e) => {
this.setState({
visible: false,
});
}
render() {
return (
<div> <Button type="primary" onClick={this.showModal}> Open Modal </Button> <Modal title="Basic Modal" visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} > <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Modal> </div>
);
}
}
ReactDOM.render(<App />, mountNode); 複製代碼
以上代碼能夠看出:受制於React一般的寫法,在頻繁使用Modal的場景下Antd的封裝使用起來仍是有些繁瑣element-ui
固然,做爲中後臺系統的優秀解決方案,Antd的小夥伴幫咱們封裝了Modal.methods能夠快速的建立一些簡易的彈窗:bash
Modal.confirm({
title: 'Confirm',
content: 'Bla bla ...',
okText: '確認',
cancelText: '取消',
});
複製代碼
這是一個不錯的思路,可是Antd官方提供的methods功能很是有限。網絡
但願可以實現result = await Modal.open()
這樣的調用方式。antd
一種思路是在實現一個Modal class,上面有static open這樣一個方法,當調用open時,向頁面插入一個modal,相似上面Modal.confirm的作法。
另外一種辦法,能夠先在頁面實例化一個modal,須要open的時候利用ref attribute獲取到已經實例化的modal,並調用它的open 方法。 以下:
import React from 'react';
import { Modal, Input } from 'antd';
export default class AsyncModalDemo extends React.Component {
state = {
visible: false,
text: ''
}
open = () => {
this.setState({
visible: true,
});
return new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
close = () => {
this.setState({
visible: false,
});
if (this.reject && typeof this.reject === 'function') {
this.reject('cancel');
}
}
handleOk = () => {
if (typeof this.resolve === 'function') {
this.resolve(this.state.text);
}
delete this.reject;
this.close();
}
render() {
return <Modal visible={this.state.visible} onCancel={this.close} onOk={this.handleOk} > <div> hello </div> <div> you can check result in devtool </div> <Input onChange={e => { const v = (e && e.target && e.target.value) || ''; this.setState({ text: v, }) }} value={this.state.text} ></Input> </Modal>
}
}
複製代碼
import React from 'react';
import './App.css';
import { Button } from 'antd';
import AsyncModal from './components/async_modal';
class App extends React.Component {
handleOpenModal = async () => {
if (this.refs.asyncModal) {
const result = await this.refs.asyncModal.open();
console.log(result)
}
}
render() {
return (
<div className="App"> <Button onClick={this.handleOpenModal} > Open Modal </Button> <AsyncModal ref="asyncModal"></AsyncModal> </div>
);
}
}
export default App;
複製代碼
這樣,咱們就可使用await 獲取到Modal內到交互結果了,而且作到了較好的解耦。
import React from 'react';
import { Modal, Input } from 'antd';
class AsyncModalBase extends React.Component {
state = {
visible: false,
}
open = () => {
this.setState({
visible: true,
});
return new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
close = () => {
this.setState({
visible: false,
});
if (this.reject && typeof this.reject === 'function') {
this.reject('cancel');
}
}
}
export default class AsyncModalDemo extends AsyncModalBase {
state = {
text: ''
}
handleOk = () => {
if (typeof this.resolve === 'function') {
this.resolve(this.state.text);
}
delete this.reject;
this.close();
}
render() {
return <Modal visible={this.state.visible} onCancel={this.close} onOk={this.handleOk} > <div> hello </div> <div> you can check result in devtool </div> <Input onChange={e => { const v = (e && e.target && e.target.value) || ''; this.setState({ text: v, }) }} value={this.state.text} ></Input> </Modal>
}
}
複製代碼
open 和 close method 在 AsyncModalBase內實現,在AsyncModalDemo內只須要實現handleOk就能夠了。這樣就複用了open和close兩個method。
import React from 'react';
import { Modal, Input, Button } from 'antd';
const ModalHoc = (modalProps = {}) => DefaultComponent => class extends React.Component {
state = {
visible: false,
}
open = () => {
this.setState({
visible: true,
});
return new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
close = () => {
this.setState({
visible: false,
});
if (this.reject && typeof this.reject === 'function') {
this.reject('cancel');
}
}
hanldeResolve = (...params) => {
if (typeof this.resolve === 'function') {
this.resolve(...params);
}
delete this.reject;
this.close();
}
render() {
return <Modal {...modalProps} visible={this.state.visible} footer={null} // 這裏作了一點改動,footer在子組件內實現 closable onCancel={this.close} > <DefaultComponent {...this.props} hanldeResolve={this.hanldeResolve} hanldeClose={this.close} ></DefaultComponent> </Modal>
}
}
class Demo extends React.Component {
state = {
text: '',
}
handleOk = () => {
this.props.hanldeResolve(this.state.text)
}
render() {
return <div> <div> hello </div> <div> you can check result in devtool </div> <Input onChange={e => { const v = (e && e.target && e.target.value) || ''; this.setState({ text: v, }) }} value={this.state.text} ></Input> <div style={{ marginTop: 10, textAlign: 'right' }}> <Button style={{marginRight: 5}} onClick={this.props.hanldeClose}>取消</Button> <Button type="primary" onClick={this.handleOk}>肯定</Button> </div> </div>
}
}
export default ModalHoc()(Demo);
複製代碼
很容易封裝出了一個ModalHOC,同時作到了比較好的代碼複用和解耦。這樣,咱們只須要實現modal內的業務邏輯,如表單、選擇器……。外層的調用方式仍是保持一致。
modal_mixin:
export default {
data() {
return {
visible: false,
}
},
watch: {
visible(val) {
if (!val) {
if (typeof this.handleClear === 'function') {
this.handleClear();
}
if (this.reject) {
this.reject('放棄操做');
}
}
}
},
methods: {
open(...params) {
this.visible = true;
if (typeof this.handleInit === 'function') {
this.handleInit(...params);
}
return new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
},
handleResolve(...params) {
delete this.reject;
this.visible = false;
if (this.resolve) {
this.resolve(...params);
}
},
close() {
this.visible = false;
}
}
}
複製代碼
async modal:
<template>
<el-dialog :visible.sync="visible">
<div>
you can check result in devtool
</div>
<el-input v-model="text"></el-input>
<div style="text-align: right; margin-top: 15px">
<el-button size="small" @click="close">取消</el-button>
<el-button size="small" type="primary" @click="handleOk">確認</el-button>
</div>
</el-dialog>
</template>
<script>
import modal_mixin from './modal_mixin';
export default {
name: 'AayncModal',
mixins: [modal_mixin],
data() {
return {
text: ''
}
},
methods: {
handleOk() {
this.handleResolve(this.text);
}
}
}
</script>
複製代碼
調用:
<template>
<div id="app">
<div>
<el-button @click="openModal">open modal</el-button>
</div>
<HelloWorld ref="asyncModal"/>
</div>
</template>
<script>
import HelloWorld from './components/AsyncModal.vue'
export default {
name: 'app',
components: {
HelloWorld
},
methods: {
async openModal() {
if (this.$refs.asyncModal) {
const result = await this.$refs.asyncModal.open();
console.log(result)
}
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
複製代碼
使用mixin作代碼複用。看起來彷佛比react版本更簡單一些。