从一个 Confirm 组件开始,一步步写一个可插拔式的组件。

处理一个正常的支付流程(比如支付宝购买基金)

  1. 点击购买按钮
  2. 如果风险等级不匹配则:弹确认框(Confirm)
  3. 用户确认风险后:弹出支付方式选择弹窗(Dialog)
  4. 选择好支付方式后:弹窗调用指纹验证(Dialog)
  5. 如果关闭指纹验证:提示是否输入密码(Dialog)
  6. 弹出输入密码的键盘(自定义键盘)
  7. 当然还有密码加班
  8. 如果密码输入错误则弹出修改/重试提示(Confirm)
  9. ...再次弹出键盘

大约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 库,方便大家学习和使用,

React + Hooks 项目实战

欢迎关注公众号「前端进阶课」认真学前端,一起进阶。

# "可插拔式"组件设计,领略组件开发的奥秘的更多相关文章

  1. xmlplus 组件设计系列之零 - xmlplus 简介

    xmlplus 是什么 xmlplus 是博主写的一个 JavaScript 框架,用于快速开发前后端项目. xmlplus 基于组件设计,组件是基本的构造块.评价组件设计好坏的一个重要标准是封装度. ...

  2. admin源码解析以及仿照admin设计stark组件

    ---恢复内容开始--- admin源码解析 一 启动:每个APP下的apps.py文件中. 首先执行每个APP下的admin.py 文件. def autodiscover(): autodisco ...

  3. Vue 组件设计

    Vue 组件设计 Vue 作为 MVVM 框架一员,不管是写业务还是基础服务,都少不了书写组件.本文总结一下书写业务组件的一些心得. 为什么要写组件? 我们知道,只要是组件,就需要在引用的时候与 vi ...

  4. 企业 SOA 设计(2)–组件化产品开发平台

    上一篇<企业 SOA 设计(1)–ESB 设计>中,写到我们的 SOA 设计分为两个层面来进行:一个是系统间的 SOA 设计,主要通过 ESB 来完成:另一方面则是单个应用系统内部的 SO ...

  5. HT图形组件设计之道(四)

    在<HT图形组件设计之道(二)>我们展示了HT在2D图形矢量的数据绑定功能,这种机制不仅可用于2D图形,HT的通用组件甚至3D引擎都具备这种数据绑定机制,此篇我们将构建一个3D飞机模型,展 ...

  6. HT图形组件设计之道(三)

    上篇我们通过定制了CPU和内存展示界面,体验了HT for Web通过定义矢量实现图形绘制与业务数据的代码解耦及绑定联动,这类案例后续文章还会继续以便大家掌握更多的矢量应用场景,本篇我们先切换个话题, ...

  7. 如何优雅的设计 React 组件

    作者:晓冬 本文原创,转载请注明作者及出处 如今的 Web 前端已被 React.Vue 和 Angular 三分天下,一统江山十几年的 jQuery 显然已经很难满足现在的开发模式.那么,为什么大家 ...

  8. 如何优雅的设计React组件

    如何优雅的设计 React 组件 如今的 web 前端已被 React.Vue 和 Angular 三分天下,一统江山十几年的 jQuery 显然已经很难满足现在的开发模式.那么,为什么大家会觉得 j ...

  9. React组件设计

    React组件设计 组件分类 展示组件和容器组件 展示组件 容器组件 关注事物的展示 关注事物如何工作 可能包含展示和容器组件,并且一般会有DOM标签和css样式 可能包含展示和容器组件,并且不会有D ...

  10. React组件设计(转)

    React组件设计 组件分类 展示组件和容器组件 展示组件 容器组件 关注事物的展示 关注事物如何工作 可能包含展示和容器组件,并且一般会有DOM标签和css样式 可能包含展示和容器组件,并且不会有D ...

随机推荐

  1. sleep usleep nanosleep alarm setitimer使用

    sleep使用的是alarm之类的定时器,定时器是使得进程被挂起,使进程处于就绪的状态. signal+alarm定时器 alarm参数的类型为uint, 并且不能填0 #include <st ...

  2. Laravel5使用ElasticSearch

    https://blog.csdn.net/qq_16829085/article/details/80725125 安装elasticsearch和ik插件 (elasticsearch的使用需要配 ...

  3. 三种查看MySQL数据库版本的方法

    https://blog.csdn.net/hj7jay/article/details/51921504 1.使用-V参数 首先我们想到的肯定就是查看版本号的参数命令,参数为-V(大写字母)或者-- ...

  4. PHP两个变量值互换(不用第三变量)

    <?php /**  * 双方变量为数字或者字符串时  * 使用list()和array()方法可以达到交换变量值得目的  */ $a = "This is A"; // a ...

  5. Vue 属性绑定v-bing 事件绑定v-on

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. dot net double 数组转 float 数组

    本文告诉大家如果遇到 double 数组转 float 数组千万不要使用 Cast ,一般都使用 select 强转. 最近在开发Avalonia ,有大神告诉我,下面的代码可以这样写 dashes ...

  7. java 利用反射创建对象

    创建对象: 1.使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法. 2.使用Class对象获取指定的Constructor对象 ...

  8. react 父组件与子组件双向绑定

    在项目中我们可能会遇到类似这样的场景,也就是父子组件的双向数据绑定 首先,先把在head中引入react.js.react-dom.js和可选择的babel.js(这里需要注意引用的顺序,react. ...

  9. 搭建zookeeper出现错误:starting zookeeper... already running process 2853

    今天搭建zookeeper时碰到了starting zookeeper already running process 2853这样一个错误. 上网上查了几个相似的问题都是要删除zookeeper_s ...

  10. Linux 内核总线

    一个总线是处理器和一个或多个设备之间的通道. 为设备模型的目的, 所有的设备都通过 一个总线连接, 甚至当它是一个内部的虚拟的,"平台"总线. 总线可以插入另一个 - 一个 USB ...