• 小程序场景流程化构建


    What is 流程

    第一步我们需要对流程有一个认识,需要知道一个流程的基本形态是怎样的。

    流程案例

    • 使用 APP 第三方支付时,点击选择使用微信支付后会拉起应用,用户可以选择各种银行卡或信用卡进行支付,若密码失败则在微信内继续处理,最终跳回 APP 后确认支付成功后,即可进行后续处理。

    • 使用 APP 第三方登录时,点击选择使用 QQ 登录后会拉起应用,用户可以选择快速登录或输入账号密码进行登录,若密码失败则在 QQ 内继续处理,最终跳回 APP 后确认为登录成功后,即可进行后续处理。

    • 使用微信小程序人脸识别时,点击开始使用人脸识别后会拉起微信的人脸识别,若识别失败则在人脸识别内重新进行人脸识别,最终回到拉起人脸识别前的那个页面,得到是否成功的结果。

    流程抽象

    从上面的案例来看,其实我大部分都是 Copy/Paste 的文案,说明一个流程的基本形态是很固定的,它不仅限于在 APP 之间的跳转,小程序与微信 API 的使用,在任何我们认为属于流程的场景,我们都可以尝试去构建我们自己的流程。

    基础流程

    将刚才说的以流程图来表示就是这样的,各种复杂的处理都在流程页面内,业务页面最终只需要知道成功还是失败即可。如果我们以开发者的角度来看,我们在业务侧只需要这样处理。

    // 对开发者来说,是可以抽象成一个 Promise 来表示的。
    // pending 表示流程未结束
    // resolve 表示流程返回成功
    // reject  表示流程返回失败
    
    sdk.pay(opts).then(successHandler).catch(failHandler)
    sdk.face(opts).then(successHandler).catch(failHandler)
    sdk.login(opts).then(successHandler).catch(failHandler)
    

    对开发者来说,这样的流程调用只能说非常的清爽!当流程结束后,开发者可以在 then 里进行后续处理。若无法正常开启流程或用户主动取消流程,则可以在 catch 内进行处理。

    使用场景

    一般我们会在各种通用场景下,都会需要调用流程。当你发现你的项目,在各种场景下都可能需要某个流程时,你就可以开始考虑将相关内容抽象流程化。

    在我们开发政务服务相关的小程序时,在整个小程序内,我们都需要涉及实名校验,整个流程一环扣一环,远远不是检查一下是否要登录就选择登录这么简单的事情。

    认证流程

    这里是一个政务服务的认证场景,场景的流程是很长的。

    Why we need 流程

    知道大致什么样的形态我们可以称为流程后,就需要思考一下在开发阶段为什么需要抽象流程。

    流程优势

    我在实际使用场景下感觉到的优势:

    1. 在任何需要掉起流程地方都可以调起流程。
    2. 开发者只需关心流程成功还是失败,无需知道内部复杂实现。
    3. 多个简单的流程可自由组合成一个复杂流程。

    如果我们把上面的实名认证进行流程化抽象后,我们可以得到下面这样的流程图。

    抽象认证流程

    当我们把流程拆分出来后,逻辑就简单很多了,每一个流程都是独立的模块。各个模块之间还可以互相调用,来组合出一个更大型的流程。

    流程哲学

    程序应该只关注一个目标,并尽可能把它做好。让程序能够互相协同工作。应该让程序处理文本数据流,因为这是一个通用的接口。 —— Malcolm Douglas McIlroy 道格拉斯·麦克罗伊

    其实这里也符合 unix 哲学,每个流程只做一件事。这样我们只需要维护好单个流程内部的逻辑就可以了,每个流程返回的数据还可以带到下一个流程内进行使用,这非常像一个 Promise 链。

    How to make 流程

    在我们知道是什么、为什么后,就可以看看具体到代码层面上我们如何去构建流程,当然这里的场景是小程序,但只要是 SPA 架构的 web 页面,类似的思路一样是可以尝试使用的。我们先来整理一下所需开发的功能点:

    1. 何时知道要跳回起始页面
    2. 怎么知道起始页面是哪个
    3. 多流程的数据流向是怎样的。

    基本原理

    其实问题很简单,解决方法也是我为什么说需要 SPA 架构进行实现。我们只需要在调用 API 后,记录当前页面栈并全局监听一个事件。当流程结束后,我们再通过事件通知来决定是否需要跳回调用页。我们只需要记录页面栈,配合全局唯一的事件,跨页面通信并进行相应处理。而多数据流向,我们是可以通过前一个流程返回的数据直接带到新流程进行使用。

    以下提供基本代码,除基础库外,示例代码不可直接运行(有许多伪代码)。

    流程基础库

      // sdk.js
    
      interface FlowOpts {
        // 决定是如何开启一个流程
        startType: 'navigateTo' | 'redirctTo',      
        // 流程结束后,是否需要保留当前页面
        finishType: 'keep' | 'pop',
        // 带给页面的参数
        params: Record<string, any>
      }
    
      /**
       * 创建通用流程
       * @param url 跳转参数
       * @param options 创建参数,具体类型参照 FlowOpts
       * @return Promise Response
       */
      function createFlow(url: string, options: FlowOpts = {}) {
        const startPageLength = getCurrentPages().length
        const successEvent = url + '-' + extend.generateGUID()
    
        options.startType = options.startType || 'navigateTo'
        options.finishType = options.finishType || 'pop'
        options.params = options.params || {}
    
        const urlWithOptions = urlJoinParams(url, {
            successEvent,
            ...options.params
        })
    
        return new Promise((resolve, reject) => {
          Event.addEventListener(successEvent, (res: any) => {
            if (options.finishType === 'keep') {
              // 保存到当前页面
              resolve(res.target)
            } else {
              // 如果是弹回流程开始页
              udb.navigatoBackToStart(startPageLength)
                .then(() => {
                  resolve(res.target)
                })
            }
          })
      
          wx[options.finishType]({
            url: urlWithOptions,
            complete() {
              Event.removeEventListener(successEvent)
            }
          })
        })
      },
    
      // 流程跳回开始
      navigatoBackToStart(startPageLength: number) {
        return new Promise((resolve, reject) => {
          const endPageLength = getCurrentPages().length
          const delta = endPageLength - startPageLength
    
          // 回退页面
          wx.navigateBack({ delta })
          delayResolve()
    
          // 确保异步回退成功
          function delayResolve() {
            setTimeout(() => {
              const currentPageLength = getCurrentPages().length
              if (endPageLength > 1 && startPageLength === currentPageLength) {
                resolve()
              } else {
                delayResolve()
              }
            }, 100)
          }
        })
      },
    

    创建流程

    // my-flow.js
    
    // 业务创建流程
    function startFlowOne(options) {
      if(!canStart) {
        // 能否发起流程的业务逻辑判断
        return Promise.reject()
      }
    
      return createFlow('/pages/flow-one/index', options)
    }
    function startFlowTwo(options) {
      return createFlow('/pages/flow-two/index', options)
    }
    // 组合流程
    function startFlowOneTwo(options) {
      return startFlowOne({
        startType: 'keep',
        params: options
      }).then(flowRes => {
        // 多流程数据合并
        return startFlowTwo({
          params: {
            ...flowRes,
            ...options
          }
        })
      })
    }
    

    具体流程页面,处理完业务后发起成功事件

    // page/flow-one/index.js
    
    Page({
      onLoad(parmas) {
        if(params) {
          this.successEvent = params.successEvent
        }
      },
      handleSubmit() {
        request({
          url: 'example.com',
          data: params,
          methods: 'POST'
        }).then((res) => {
          Event.dispatch(this.successEvent, res)
        })
      }
    })
    

    真正提供到给业务开发使用。

    // pages/index/index.js
    
    Page({
      handleTap() {
        // 非常简单!
        return sdk.startFlowOneTwo({ id: 3 })
          .then((res) => {
            // 成功后的业务代码
          })
          .catch(() => {
            // 无法正常调起流程
          })
      }
    }) 
    

    通过上面的代码,我们就可以把业务再进行合理抽象,通过流程化降低业务复杂度。

    遗留问题

    在小程序上使用还有哪些特别要注意的点呢,其中有一个就是要千万注意页面栈的问题。在小程序内是有十层页面栈限制的,如果你的流程特别的长,需要格外注意这一点并进行相应的优化。但如果我们在 web 页面使用,是没有这类问题的。

    Ending

    快去尝试将你的业务场景流程化吧!

    

    

  • 相关阅读:
    Web大前端面试题-Day12
    Web大前端面试题-Day11
    每天刷Web面试题(前10天汇总)
    Web大前端面试题-Day10
    Web大前端面试题-Day9
    Web大前端面试题-Day8
    Web大前端面试题-Day5
    Web大前端面试题-Day7
    Web大前端面试题-Day6
    php获取时间是星期几
  • 原文地址:https://www.cnblogs.com/YikaJ/p/10314697.html
Copyright © 2020-2023  润新知