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

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

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

大约6个弹窗...

地摊货(精简版)

首先尝试以一个平常的注册组件实现

Confirm 通过 v-model="isShow" 切换展示,通过 @onConfirmonCancel 接收点击事件。

组件代码

  1. <template>
  2. <div v-if="value">
  3. <slot></slot>
  4. <div>
  5. <div @click="cancelHandler">{{cancelTxt}}</div>
  6. <div @click="confirmHandler">{{confirmTxt}}</div>
  7. </div>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. props: {
  13. value: {
  14. type: Boolean,
  15. default: false,
  16. }
  17. },
  18. data() {
  19. return {
  20. content: '',
  21. confirmTxt: '',
  22. cancelTxt: '',
  23. }
  24. },
  25. methods: {
  26. close() {
  27. this.$emit('input');
  28. },
  29. cancelHandler() {
  30. this.$emit('onCancel');
  31. },
  32. confirmHandler() {
  33. this.$emit('onConfirm');
  34. }
  35. }
  36. }
  37. </script>

使用代码

  1. <confirm
  2. v-model="isConfirmShow"
  3. @onCancel="onCancel"
  4. @onConfirm="onConfirm"
  5. >内容部分</confirm>

那么用它来完成上面的需求吧。

  1. openRiskConfirm() {
  2. this.isRiskConfirmShow = true;
  3. },
  4. onRiskCancel() {
  5. this.isRiskConfirmShow = false;
  6. },
  7. onRiskConfirm() {
  8. // something
  9. this.openPaymeList();
  10. },
  11. openPaymeList() {
  12. this.isPaymentListShow = ture;
  13. }
  14. // ... 巴拉巴拉
  15. // ... 大约需要 3*6 = 18 个方法才能完成需求(其他请求类的还不算)

如果你能接受,但是:

那么万一监管放松了,不需要校验风险了呢?或者一开始没有校验风险,监管突然要校验风险了呢?又或者不在 app 上使用,不用调用指纹呢?又或者要添加一个 人脸识别功能了呢?

代码改起来会是一个灾难,因为就算业务代码是你写的,你一段时间后也不一定能记得流程,而且,代码看起来没有任何的连续性,只能一个个方法看。

流行款(精简版)

针对上面的业务流程,尝试使用现在比较流行的的弹窗。

组件:更改接收方法位置,从 props 放到 $data

  1. <template>
  2. <div>
  3. <div>{{content}}</div>
  4. <div>
  5. <div @click="cancelHandler">{{cancelTxt}}</div>
  6. <div @click="confirmHandler">{{confirmTxt}}</div>
  7. </div>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. data() {
  13. return {
  14. content: '',
  15. confirmTxt: '',
  16. cancelTxt: '',
  17. onConfirm: function() {},
  18. onCancel: function() {},
  19. }
  20. },
  21. methods: {
  22. uninstall() {
  23. this.$destroy(true);
  24. this.$el.parentNode.removeChild(this.$el);
  25. },
  26. cancelHandler() {
  27. (typeof this.onCancel === 'function') && this.onCancel()
  28. this.uninstall();
  29. },
  30. confirmHandler() {
  31. (typeof this.onConfirm === 'function') && this.onConfirm()
  32. this.uninstall();
  33. }
  34. }
  35. }
  36. </script>

注册到全局

  1. import confirm from './confirm.vue'
  2. export default {
  3. install: function(Vue) {
  4. const Profile = Vue.extend(confirm);
  5. const PortfolioMsg = (options) => {
  6. let $ele = document.createElement("div");
  7. document.body.appendChild($ele);
  8. new Profile({
  9. data() {
  10. return options;
  11. }
  12. }).$mount($ele);
  13. };
  14. Vue.prototype.$confirm = PortfolioMsg;
  15. }
  16. }

调用

  1. this.$confirm({
  2. content: '内容',
  3. confirmTxt: '确定',
  4. cancelTxt: '取消',
  5. onConfirm: () => {
  6. console.log('确定')
  7. },
  8. onCancel: () => {
  9. console.log('取消')
  10. }
  11. })

哪啊么用它完成上面的需求会如何?

  1. this.$confirm({
  2. content: '风险认证',
  3. cancelTxt: '再看看',
  4. confirmTxt: '同意',
  5. onConfirm: () => {
  6. // something
  7. this.$dialog({
  8. content: '指纹认证',
  9. slot: `<div>指纹认证</div>`,
  10. onFinish: () => {
  11. // 支付 成功? 失败?
  12. // something
  13. },
  14. onCancel: () => {
  15. // something
  16. this.$confirm({
  17. content: '密码认证',
  18. cancelTxt: '取消',
  19. confirmTxt: '确定',
  20. onConfirm: () => {
  21. // something
  22. this.$keyboard({
  23. // 略
  24. onFinish: (password) => {
  25. // 密码加密
  26. // something
  27. if (/* 密码错误? */) {
  28. // 重复了
  29. // 这个代码就可以抽象成一个方法
  30. this.$confirm({
  31. content: '密码认证',
  32. cancelTxt: '取消',
  33. confirmTxt: '确定',
  34. // 略
  35. })
  36. }
  37. }
  38. })
  39. },
  40. onCancel: () => {
  41. // 取消
  42. }
  43. })
  44. }
  45. })
  46. },
  47. onCancel: () => {
  48. // 取消
  49. }
  50. })

这样看起来确实清晰了很多,代码量也少了很多,不需要注册全局的组件可以通过在 methods 中封装一个方法实现,维护起来也方便了很多。但是:回调地狱有木有?也只是稍微轻松一点,可不可以再优化一下呢?

抽象版

ajax 的回调地狱是通过 Promise 实现的,那么上面的组件回调地狱是不是也可以通过 Promise 实现呢?

  1. import confirm from './confirm.vue'
  2. export default {
  3. install: function(Vue) {
  4. const Profile = Vue.extend(confirm);
  5. const PortfolioMsg = (options) => {
  6. let $ele = document.createElement("div");
  7. document.body.appendChild($ele);
  8. const profile = new Profile({
  9. data() {
  10. return options;
  11. }
  12. }).$mount($ele);
  13. return new Promise((resolve, reject) => {
  14. profile.$on('onConfirm', resolve)
  15. profile.$on('onCancel', reject)
  16. })
  17. };
  18. Vue.prototype.$confirm = PortfolioMsg;
  19. }
  20. }

使用一下

  1. this.$confirm({
  2. confirmTxt: '确定'
  3. }).then(res => {
  4. console.log('点击了确定')
  5. }).catch(res => {
  6. console.log('点击了取消')
  7. })

那么回调地狱的问题很轻松的就解决了,可读性很高,中间添加删除逻辑也变的特别方便,维护起来成本大大的降低了。具体代码自己抽象一遍或许更好哦。

大家其他的封装方法吗?请留言哈

最后

译者写了一个 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. 如何用JS和HTML 做一个桌面炒股小插件【原创】

    首先,使用node-webkit 做环境,废话不多说,直接贴HTML <!DOCTYPE html> <html xmlns="http://www.w3.org/1999 ...

  2. laravel使用加载进行优化

    两种方式: 1.使用:with $posts=Post::orderby('created_at','desc')->withCount(['comments','zans'])->wit ...

  3. Laravel实现找回密码及密码重置的例子

    https://mp.weixin.qq.com/s/PO5f5OJPt5FzUZr-7Xz8-g Laravel实现找回密码及密码重置功能在php实现与在这里实现会有什么区别呢,下面我们来看看Lar ...

  4. JS 动态表格

    表格 在script里面使用innerHTML获取标签体的内容,然后进行添加. 删除则是不断的获取父节点,利用父节点删除子节点. 在做这个的时候,要主要获取表格table的对象有两种方式,一是getE ...

  5. jieba分词工具的使用方法

    作为我这样的萌新,python代码的第一步是:#coding=utf-8 环境:python3.5+jieba0.39 一.jieba包安装方法: 方法1:使用conda安装 conda instal ...

  6. js随即数字random实现div点击更换背景色

    需求:点击按钮随机给盒子换背景色 用到的知识点:Math.random    Math.round 文章地址 https://www.cnblogs.com/sandraryan/ <!DOCT ...

  7. Js中“==”和“===”的区别

    从字面上来讲,‘==’代表相等,‘===’代表严格相等. 具体来讲,比较过程如下: 比较过程: ‘==’:      1. 首先判断两个值的类型是否相同,如果相同,进行‘===’判断.      2. ...

  8. el-tree文本内容过多显示不完全问题(解决)

    布局: <span class="custom-tree-node" slot-scope="{ node, data }"> 外层span 树节点 ...

  9. Webstorm 配置 Less编译

    配置less编译

  10. H3C查看系统启动配置文件