前端状态机在vue实际项目中的运用:怎么维护订单类型以及订单状态

背景

有一天,产品大佬找到我说业务变更,需要增加一种订单。我心想:“小意思,不就是增加一种订单嘛,洒洒水啦”。介于需求简单,我就把刚要掏出来的收款码又放了回去。产品大佬满意的点点头,带着他的祖传菜刀走了。

111.jpeg

于是我开开心心的打开编辑器,一看代码,好家伙,我直接好家伙

  computed: {
    priceTitleText() {
      if ([20001, 20002].includes(this.detail.orderStatus)) {
        return '应付金额'
      } else {
        return '实付金额'
      }
    },

复制代码

想加订单类型时郁闷了,这怕不是每种状态都得去判断订单类型,也就是像下面这样

    if (this.detail.orderType === 10001) {
       if ([20001, 20002].includes(this.detail.orderStatus)) {
           return '应付金额'
        } else {
           return '实付金额'
        }
    } else if (this.detail.orderType === 10002) { // 新需求订单类型
       // 因为订单状态不同展示的内容也不同
      if ([20001, 20002, 20003].includes(this.detail.orderStatus)) {
        return '应付金额'
      } else {
        return '实付金额'
      }
    }


复制代码

这么一个computed还好,但是项目中是有十多二十来个的监听函数,以至于我不得不每个都得去加一下订单类型判断。我思索了一会儿,又掏出了我的收款码,草率了......

12.jpg

使用状态机

就拿上面的需求来讲,当前新需求增加了一种订单类型,假设后面还要增加,是不是还得在上面的操作再去增加一层if else。每种订单类型有不同的业务逻辑,很有可能状态1在订单类型1上是一种表现形式,但是在订单类型2上又是另外一种表现形式了,所以不得不想想怎么去整理分类订单,让订单逻辑一眼清晰可见。这时,状态机也就派上了用场。

状态机的四大概念

  • State (状态)。一个状态机至少要包含两个状态。例如上面订单类型,有 订单类型1订单类型2 两个状态。

  • Event (事件)。事件就是执行某个操作的触发条件或者口令。

  • Action (动作)。事件发生以后要执行动作。对于上面的priceTitleText,就是一个动作。

  • Transition (变换)。也就是从一个状态变化为另一个状态。

创建状态机

class StatusMaker {
  constructor(detail) {
    if (!detail) return
    this.state = this.setState(detail.orderType)
    this.detail = detail
  }
}
复制代码

传入订单详情数据

增加状态(state)

class StatusMaker {
  constructor(detail) {
    if (!detail) return
    this.state = this.setState(detail.orderType)
    this.detail = detail
  }
  // ++ start
  setState(status) {
    if (status === 2) {
      return 'orderType1'
    } else if (status === 3) {
      return 'orderType2'
    }
  }
  // ++ end
}
复制代码

状态是控制状态机运行的关键属性,所以首先得控制传入的订单类型

增加动作(action)

知道了当前订单类型,那么可以根据当前类型执行需要的不同的方法。

class StatusMaker {
  constructor(detail) {
    if (!detail) return
    this.state = this.setState(detail.orderType)
    this.detail = detail
  }
  setState(status: OrderStatus) {
    if (status === 2) {
      return 'orderType1'
    } else if (status === 3) {
      return 'orderType2'
    }
  }
  // ++ start
  orderType1 = {
    // that: this, 
    // 上方的that这里使用,在vue中有个bug,当通过this访问实例环境时
    // vue内部依赖收集会不停的循环进行addDep,造成爆栈,
    // 在浏览器上并不会,有点无语,所以造成了下面的方法内部无法获得当前实例。
    // 下方的this我们后续通过call更改this指向。
    priceTitleText() {
       // console.log(this.that.detail) 本可以这么访问实例
      if ([20001, 20002].includes(this.detail.orderStatus)) {
        return '应付金额'
      } else {
        return '实付金额'
      }
    },
  }
  orderType2 = {
    // that: this,
    priceTitleText() {
      if ([20001, 20002, 20003].includes(this.detail.orderStatus)) {
        return '应付金额'
      } else {
        return '实付金额'
      }
    },
  }
  // ++ end
}

复制代码

增加执行事件(event)

简单来讲,就是去执行我们需要执行的动作

class StatusMaker {
  constructor(detail) {
    if (!detail) return
    this.state = this.setState(detail.orderType)
    this.detail = detail
  }
  setState(status: OrderStatus) {
    if (status === 2) {
      return 'orderType1'
    } else if (status === 3) {
      return 'orderType2'
    }
  }
  orderType1 = {
    // that: this, // 这里在vue中有个bug,当通过this访问实例环境时,会造成爆栈,在浏览器上并不会,有点无语,所以造成了下面的方法内部无法获得当前实例。下方的this我们后续通过call更改this指向。
    priceTitleText() {
      if ([20001, 20002].includes(this.detail.orderStatus)) {
        return '应付金额'
      } else {
        return '实付金额'
      }
    },
  }
  orderType2 = {
    // that: this,
    priceTitleText() {
      if ([20001, 20002, 20003].includes(this.detail.orderStatus)) {
        return '应付金额'
      } else {
        return '实付金额'
      }
    }
  }
  // ++ start
  event (method, ...args) {
    const state = this.state
    if (!this[state] || !this[state][method]) {
      return false
    }
    return this[state][method].call(this, ...args) // 通过call让action内部能够获取当前实例this,并且将参数args传入
  }
  // ++ end
}
export default StatusMaker
复制代码

如此,一个状态机便大功告成。

vue中使用


import StatusMaker from './statusFactory'
export default {
  data() {
     return {
         statusMaker: null
     }
  },
  computed: {
     priceTitleText() {
       return this.statusMaker.event('priceTitleText', this.showFormType)
     },
     // ...
  },
  created() {
      this.statusMaker = new StatusMaker(this.detail)
  }
}
复制代码

如此,状态机便可以根据我们传入的订单类型,展示不同的页面逻辑,非常的银杏。

总结

状态机其实也就是状态模式,好处是让整个逻辑更加清晰分明,将不同订单类型数据划片拆分开来。但是个人觉得在使用中,两种订单类型,需要去重复写同样的函数,并且里面大部分内容都相同。不知道有没有其它方式进行优化。欢迎大佬们评论指出,万分感谢。

猜你喜欢

转载自juejin.im/post/7031461564297248798