设计模式在vue中的应用(六)

3,985 阅读5分钟

前言

目录整理:
设计模式在vue中的应用(一)
设计模式在vue中的应用(二)
设计模式在vue中的应用(三)
设计模式在vue中的应用(四)
设计模式在vue中的应用(五)
设计模式在vue中的应用(六)
设计模式在vue中的应用(七)

为什么要写这些文章呢。正如设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结(来自百度百科)一样,也是想通过分享一些工作中的积累与大家探讨设计模式的魅力所在。
在这个系列文章中为了辅助说明引入的应用场景都是工作中真实的应用场景,当然无法覆盖全面,但以此类推也覆盖到了常见的业务场景



观察者模式对我们来说应该不陌生,对vue原理稍微有点了解的都知道通过Object.defineProperty 拦截数据的 get/set ,在get中收集依赖Watcher,在set中触发更新Watcher.notify(),这里就是观察者模式的应用

观察者(Observer)模式与发布(Publish)/订阅(Subscribe)的关系
他们的行为方式相似add,notify/trigger,最明显的区别是观察者add一个具体观察者对象,发布/订阅add一个订阅事件

// 观察者
class Observer {
  update () {}
}

const Observer1 = new Observer()
Subject.add(Observer1)
Subject.notify()

// 发布订阅
Publish.add('event1',function Subscribe() { 
  ...do something 1
})
Publish.add('event1',function Subscribe() { 
  ...do something 2
})
Publish.add('event1',function Subscribe() { 
  ...do something 3
})
Publist.trigger('event1')

一、场景

  • 两个form表单——发票信息和邮寄信息
  • 邮寄信息表单只在选中增值税专用发票时才需要
  • 提交按钮需在所有存在的表单(一个或者两个)验证通过后才有效,也就是在点击提交按钮后获取表单的验证结果和输入框的值

二、分析

大多数情况我们遇到的场景是一个表单对应一个提交按钮,现在的场景是一个提交按钮控制N个(大于1)且数量不可控的表单,我们可以借助观察者模式解决这个问题

将表单对象(组件)作为观察者,点击提交按钮notify所有观察者(表单)获取值

三、设计

// invoiceForm.vue
<template>
  ... do something
</template>
<srcipt>
  export default {
    methods: {
      // 所有form组件提供统一的获取值接口
      getValue () {
        // 需返回一个promise,目前大多数vue表单验证都是异步的
        return new Promise(() => {
          // 表单验证,返回值
          ...do something
        })
      }
    }
  }
</script>
// postForm.vue
<template>
  ... do something
</template>
<srcipt>
  export default {
    methods: {
      // 统一的获取值接口
      getValue () {
        // 需返回一个promise
        return new Promise(() => {
          ...do something
        })
      }
    }
  }
</script>
// stage.vue
<template>
  <div>
    <!-- ref获取组件对象 -->
    <invoice-form ref="invoice" />
    <post-form v-if="isNeedPost" ref="post" />
    <button @click="handleSubmit">提交</button>
  </div>
</template>
<script>
  export default {
    data () {
      isNeedPost: true
    }
    methods: {
      async handleSubmit () {
        // 已知有invoice和post两个观察者
        let invoice = await this.$refs.invoice.getValue()
        let post = {}
        if (this.$refs.post) {
            post = await this.$refs.post.getValue()
        }
        this.$axios.post({
            invoice,
            post
        })
      }
    }
  }
</script>

四、更复杂的场景

每次点击新增联系人按钮页面上会增加一个这样的form,点击保存按钮时所有联系人表单分别做验证,所有表单验证通过后一并提交。与上面的场景类似,大家可以尝试本文的思路。
上面的场景是已知有invoiceFormpostForm,而这个场景下联系人form可以无限制的添加

当 ref 和 v-for 一起使用的时候,你得到的引用将会是一个包含了对应数据源的这些子组件的数组(来自vue文档)

设计实现

<template>
  <div>
    <div>
      <contact-form
        v-for="(item, index) in contactGroup"
        ref="contacts"
        :key="index"
      />
    </div>
    <button @click="handleAdd">添加联系人</button>
    <button @click="handleSave">保存</button>
  </div>
</template>
<script>
  export default{
    data () {
      return {
        contactGroup: [true]
      }
    },
    methods: {
      handleAdd () {
        this.contactGroup.push(true)
      },
      async handleSave () {
        // 伪代码
        try {
          // this.$refs.contacts是一个数组
          const promises = this.$refs.contacts.map(contact => contact.getValue())
          const contacts = await Promise.all(promises)
          this.$axios.post({
            contacts
          })
        } catch (error) {
          // 表单验证不通过
          console.dir(error)
        }
      }
    }
  }
</script>

结尾

本文的内容相对来说比较简单,对于上面例举的场景你也可能会有更好的解决方式,不过个人觉得这种需求场景对于理解观察者模式还是蛮不错的。


写到这一篇觉得有必要搞个小总结:

1,在这一篇为了使用观察者模式我们需要获取观察对象(组件)——ref
2,本系列第一篇我们用工厂模式生产input组件,为了透传type、status工厂组件设计的也需要接受
这两个props,如果这时要透传更多的prop并且还有事件那怎么办呢,最好的方案——$attrs$listeners
3,设计模式的一个主要特性能够动态的组合和决定使用哪个对象(组件)——动态组件component
4,本系列第四篇为能自定义一个算法(组件渲染)的某一步——slot

相信大家都已经了解了一个框架API的设计背后都是有一定考量的,当然上面例举的4个都只是基于本系列
的内容不代表全部
有人说不会这样用是因为对vue的文档不熟悉,我身边有朋友没事就把vue文档从头看到尾,他是不熟?
个人觉得应该是代码的设计需求驱动你去深入了解框架,大多时候框架已经提供了满足你要求的API直
接用就行了(查文档),如果框架没有提供现成的能不能通过其它API hack一下(这个时候可能就需要深入
源码研究),如果依旧满足不了你,在自己技术能力允许的情况下给框架源码提个PR?


本文实现同样适用于react,为什么文章以vue做题?vue的template让我们在理解一些概念的时候可能会有点不适应,而react的jsx可以看做就是在写JavaScript对各种概念实现更灵活
友情提示:设计模式在vue中的应用应该会写一个系列,喜欢的同学记得关注下