從一個 Confirm
組件開始,一步步寫一個可插拔式的組件。
處理一個正常的支付流程(比如支付寶購買基金)
- 點擊購買按鈕
- 如果風險等級不匹配則:彈確認框(Confirm)
- 用戶確認風險后:彈出支付方式選擇彈窗(Dialog)
- 選擇好支付方式后:彈窗調用指紋驗證(Dialog)
- 如果關閉指紋驗證:提示是否輸入密碼(Dialog)
- 彈出輸入密碼的鍵盤(自定義鍵盤)
- 當然還有密碼加班
- 如果密碼輸入錯誤則彈出修改/重試提示(Confirm)
- ...再次彈出鍵盤
大約6個彈窗...
地攤貨(精簡版)
首先嘗試以一個平常的注冊組件實現
Confirm
通過 v-model="isShow"
切換展示,通過 @onConfirm
和 onCancel
接收點擊事件。
組件代碼
<template>
<div v-if="value">
<slot></slot>
<div>
<div @click="cancelHandler">{{cancelTxt}}</div>
<div @click="confirmHandler">{{confirmTxt}}</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: false,
}
},
data() {
return {
content: '',
confirmTxt: '',
cancelTxt: '',
}
},
methods: {
close() {
this.$emit('input');
},
cancelHandler() {
this.$emit('onCancel');
},
confirmHandler() {
this.$emit('onConfirm');
}
}
}
</script>
使用代碼
<confirm
v-model="isConfirmShow"
@onCancel="onCancel"
@onConfirm="onConfirm"
>內容部分</confirm>
那么用它來完成上面的需求吧。
openRiskConfirm() {
this.isRiskConfirmShow = true;
},
onRiskCancel() {
this.isRiskConfirmShow = false;
},
onRiskConfirm() {
// something
this.openPaymeList();
},
openPaymeList() {
this.isPaymentListShow = ture;
}
// ... 巴拉巴拉
// ... 大約需要 3*6 = 18 個方法才能完成需求(其他請求類的還不算)
如果你能接受,但是:
那么萬一監管放松了,不需要校驗風險了呢?或者一開始沒有校驗風險,監管突然要校驗風險了呢?又或者不在 app
上使用,不用調用指紋呢?又或者要添加一個 人臉識別功能了呢?
代碼改起來會是一個災難,因為就算業務代碼是你寫的,你一段時間后也不一定能記得流程,而且,代碼看起來沒有任何的連續性,只能一個個方法看。
流行款(精簡版)
針對上面的業務流程,嘗試使用現在比較流行的的彈窗。
組件:更改接收方法位置,從 props
放到 $data
中
<template>
<div>
<div>{{content}}</div>
<div>
<div @click="cancelHandler">{{cancelTxt}}</div>
<div @click="confirmHandler">{{confirmTxt}}</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
content: '',
confirmTxt: '',
cancelTxt: '',
onConfirm: function() {},
onCancel: function() {},
}
},
methods: {
uninstall() {
this.$destroy(true);
this.$el.parentNode.removeChild(this.$el);
},
cancelHandler() {
(typeof this.onCancel === 'function') && this.onCancel()
this.uninstall();
},
confirmHandler() {
(typeof this.onConfirm === 'function') && this.onConfirm()
this.uninstall();
}
}
}
</script>
注冊到全局
import confirm from './confirm.vue'
export default {
install: function(Vue) {
const Profile = Vue.extend(confirm);
const PortfolioMsg = (options) => {
let $ele = document.createElement("div");
document.body.appendChild($ele);
new Profile({
data() {
return options;
}
}).$mount($ele);
};
Vue.prototype.$confirm = PortfolioMsg;
}
}
調用
this.$confirm({
content: '內容',
confirmTxt: '確定',
cancelTxt: '取消',
onConfirm: () => {
console.log('確定')
},
onCancel: () => {
console.log('取消')
}
})
哪啊么用它完成上面的需求會如何?
this.$confirm({
content: '風險認證',
cancelTxt: '再看看',
confirmTxt: '同意',
onConfirm: () => {
// something
this.$dialog({
content: '指紋認證',
slot: `<div>指紋認證</div>`,
onFinish: () => {
// 支付 成功? 失敗?
// something
},
onCancel: () => {
// something
this.$confirm({
content: '密碼認證',
cancelTxt: '取消',
confirmTxt: '確定',
onConfirm: () => {
// something
this.$keyboard({
// 略
onFinish: (password) => {
// 密碼加密
// something
if (/* 密碼錯誤? */) {
// 重復了
// 這個代碼就可以抽象成一個方法
this.$confirm({
content: '密碼認證',
cancelTxt: '取消',
confirmTxt: '確定',
// 略
})
}
}
})
},
onCancel: () => {
// 取消
}
})
}
})
},
onCancel: () => {
// 取消
}
})
這樣看起來確實清晰了很多,代碼量也少了很多,不需要注冊全局的組件可以通過在 methods
中封裝一個方法實現,維護起來也方便了很多。但是:回調地獄有木有?也只是稍微輕松一點,可不可以再優化一下呢?
抽象版
ajax 的回調地獄是通過 Promise
實現的,那么上面的組件回調地獄是不是也可以通過 Promise
實現呢?
import confirm from './confirm.vue'
export default {
install: function(Vue) {
const Profile = Vue.extend(confirm);
const PortfolioMsg = (options) => {
let $ele = document.createElement("div");
document.body.appendChild($ele);
const profile = new Profile({
data() {
return options;
}
}).$mount($ele);
return new Promise((resolve, reject) => {
profile.$on('onConfirm', resolve)
profile.$on('onCancel', reject)
})
};
Vue.prototype.$confirm = PortfolioMsg;
}
}
使用一下
this.$confirm({
confirmTxt: '確定'
}).then(res => {
console.log('點擊了確定')
}).catch(res => {
console.log('點擊了取消')
})
那么回調地獄的問題很輕松的就解決了,可讀性很高,中間添加刪除邏輯也變的特別方便,維護起來成本大大的降低了。具體代碼自己抽象一遍或許更好哦。
大家其他的封裝方法嗎?請留言哈
最后
譯者寫了一個 React + Hooks 的 UI 庫,方便大家學習和使用,
歡迎關注公眾號「前端進階課」認真學前端,一起進階。