用vue设计一个数据采集器

场景

在业务上现在有一个场景,当发生业务行为变化时,需要对各个模块的行为进行数据收集,数据用途可以用作回顾,也可以是例如监控这样的场景。

核心问题

说白了这个需求就是需要对各个模块状态变更进行记录,然后再格式化上传到服务端。
解题思路有两种一种是状态监听,第二主动收集。

状态监听

状态监听优势

快速实现利用状态管理和wacth的机制很快就知道不同模块的状态变更,然后就可以获取数据,再格式化数据,发送给服务端

状态监听劣势

  • wacth的重复监听,只要使用了wacth,不管是不是你所需要的数据,只要状态变更就会触发改变,监听行为
  • 重复依赖,比如说全局有个开始结束的状态,在使用wacth的时候就需要在不同的wacth中都去判断这个状态,或者有全局的时间模块等等
  • 重复书写,在不同的监听中需要实践相同的数据格式化方法
  • 数据分布混乱,虽然控制了全局使用同一管道上传,但是对于同一个管道内的数据想做合并去重,或者其他自定义的操作,在不同类型数据,同一管道的这个场景下面支持很弱
  • 场景区分困难,正常流程触发的监听是没有问题,如果是异常场景触发恢复的监听就会导致判断的复杂性
  • 描述的还是比较抽象看下代码示例
function useA(){
 wacth(new,old){
  if(start){
    if(new.type =='need')
     const a = {
      a:new.a
     }
     const aa = {
      aa:new.aa
     }
     upload(a)
     upload(aa)
  }
 }
}
// 多处数据散落
function useB(){
 // 重复监听
 wacth(new,old){
 // 全局判断
  if(start){
    // 不同状态判断
    if(new.type =='need')
     const b = {
      b:new.b
     }
     //重复数据格式
     const aa = {
      b:new.aa
     }
     upload(b)
     upload(aa)
  }
 }
}

重构实现思路

  • 依赖收集(监听者模式)
  • 状态统一
  • 数据自治(策略模式)

依赖收集

  • 核心思想:希望使用同一个采集器解决整个业务流程,数据变更在各个变更方,通过采集器提供的标准的格式化方法去处理数据,再把数据传递到采集器,采集器收到数据后根据不同的数据格式插入到不同的缓存通道,缓存通道缓存成功,通知业务处理的监听者,根据不同的数据类型进行不同的处理方式,最后发送到服务端。
  • 具体代码如下
/*
 * @Description: 采集公共类
 * @version: 1.0.0
 * @Author: 吴文周
 * @Date: 2021-04-20 19:44:35
 * @LastEditors: 吴文周
 * @LastEditTime: 2021-04-22 15:20:50
 */
/**
 * @name: Dep
 * @msg: 依赖收集对象
 */
class Dep {
  private subs: any = []
  // 添加观察者
  public addSub(sub: any) {
    if (sub && sub.update) {
      this.subs.push(sub)
    }
  }
  // 发送通知
  public notify(content: any) {
    this.subs.forEach((sub: any) => {
      sub.update(content)
    })
  }
}
/**
 * @name: Watcher
 * @msg: 观察者对象
 */
class Watcher {
  private cb!: (arg: any) => void
  constructor(cb: (arg: any) => void) {
    // 回调函数负责更新
    this.cb = cb
  }
  // 当数据发生变化的时候更新
  update(content: any) {
    this.cb(content)
  }
}
/**
 * @name: Channel
 * @msg: 缓存消息管道
 */
class Channel {
  // 管道存储数组
  private queue: any = []
  // 管道大小
  private limitSize = 1
  // 管道名称
  public name: string
  constructor(name: string, limitSize = 1) {
    this.name = name
    // 最小尺寸是1
    limitSize = limitSize >= 1 ? limitSize : 1
    this.limitSize = limitSize
  }
  /**
   * @name: push
   * @msg: 添加的数据
   */
  push(item: any) {
    // 如果超出限制尺寸移除第一个
    if (this.limitSize == this.queue.length) {
      this.queue.shift()
    }
    this.queue.push(item)
  }
  /**
   * @name: getLast
   * @msg: 获取最后添加的数据
   */
  getLast() {
    if (this.queue.length > 0) {
      return this.queue[this.queue.length - 1]
    } else {
      throw new Error('no item return')
    }
  }
  /**
   * @name: getLastIndex
   * @msg: 获取最后倒数的数据
   */
  getLastIndex(index: number) {
    try {
      return this.queue[this.queue.length - index - 1]
    } catch (error) {
      throw new Error('no item return')
    }
  }
  /**
   * @name: isEmpty
   * @msg: 管道是否为空
   */
  isEmpty() {
    return this.queue.length == 0
  }
}
export class Collection {
  // 依赖收集对象
  private dep = new Dep()
  // 各个数据频道分类
  private dataQueue = ['A', 'B', 'C']
  // 频道集合
  private channelMap = new Map()
  // 上传队列
  private MQ!: LiveCollectionMQ
  // 策略模式:数据类型不同对应不同的处理机制
  private strategies = {
    A: () => {
      // 可以在不同的管道中获取相对应的数据进行不同逻辑的处理
    },
    B: () => {

    },
    C: () => {
    },
  } as Record<NotifyType, any>
  constructor() {
    this.init()
  }
  private init() {
    // 初始化watcher
    this.intWatcher()
    // 初始化频道
    this.initChannel()
    // 初始化数据
    this.initData()
    // 初始化队列
    this.initMQ()
  }
  /**
   * @name:intWatcher
   * @msg:初始化监听器
   */
  private intWatcher() {
    this.dep.addSub(
      new Watcher((type: NotifyType) => {
        const handlerBack = this.getHandler(type)
        handlerBack()
      }),
    )
  }
  /**
   * @name: initChannel
   * @msg: 初始化频道
   */
  private initChannel() {
    this.dataQueue.forEach(item => {
      this.channelMap.set(item, new Channel(item, 3))
    })
  }
  /**
   * @name: initData
   * @msg: 初始化频道数据
   * @param {*}
   */
  private initData() {

  }
  /**
   * @name: initMQ
   * @msg: 初始化上传队列
   */
  private initMQ() {

  }
  /**
   * @name: getMQ
   * @msg:获取消息队列
   */
  public getMQ() {
    return this.MQ
  }
  /**
   * @name:getChannel
   * @msg:根据频道名称获取频道实例
   * @param {name}频道名称
   */
  private getChannel(name: NotifyType) {
    if (this.channelMap.get(name)) {
      return this.channelMap.get(name)
    } else {
      throw new Error('no channel')
    }
  }
  /**
   * @name:notify
   * @msg:依赖通知方法
   * @param {NotifyType} type
   * @param {any} mes
   */
  public notify(type: NotifyType, mes: any) {
    // 设置管道缓存
    this.setChannel(type, mes)
    // 全局统一判断状态判断是否要分发数据
    if (state.value.type) {
      this.dep.notify(type)
    }
  }
  /**
   * @name: setChannel
   * @msg: 设置频道缓存
   * @param {NotifyType} name
   * @param {any} mes
   */
  private setChannel(name: NotifyType, mes: any) {
    const channel = this.getChannel(name)
    channel.push(mes)
  }
  /**
   * @name:getHandler
   * @msg: 获取
   * @param {NotifyType} name
   */
  private getHandler(name: NotifyType) {
    return this.strategies[name]
  }
  /**
   * @name: getChannelLast
   * @msg: 获取指定管道中的最新的数据
   * @param {NotifyType} name
   * @return {*}
   */
  public getChannelLast(name: NotifyType) {
    try {
      const channel = this.getChannel(name)
      return channel.getLast()
    } catch (error) {
      throw new Error(error)
    }
  }
  /**
   * @name: getChannelLast
   * @msg: 获取指定管道中的倒序数据
   * @param {NotifyType} name
   * @param {number} index
   */
  public getChannelItemByLastIndex(name: NotifyType, index: number) {
    try {
      const channel = this.getChannel(name)
      return channel.getLastIndex(index)
    } catch (error) {
      throw new Error(error)
    }
  }
  /**
   * @name: generateA
   * @msg: 生成A数据方法
   */
  public generateA() {

  }
  /**
   * @name: generateB
   * @msg: 生成B数据方法
   */
  public generateB() {

  }
  /**
   * @name: generateC
   * @msg: 生成C数据方法
   */
  public generateC() {

  }
}

export const CollectionHelper = new Collection()

总结

  • 我觉得去了解一个框架的一个好的思路就是在运用它的核心原理去解决一个原理,正如之前使用webpack的插件机制一样,这次使用的是vue的依赖收集
  • 状态自治,职责统一是个代码封装的好习惯

以上就是用vue设计一个数据采集器的详细内容,更多关于vue 设计数据采集器的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue 数据(data)赋值问题的解决方案

    总结一下我遇到的一个纠结很久的问题. 在项目中需要用到后台的数据对前端渲染,使用到了vue整合的axios,使用vue中的钩子函数在页面组件挂载完成之后向后台发送一个get请求然后将返回后的数据赋值data()中定义的属性: 执行后前端报错: 原因: 在请求执行成功后执行回调函数中的内容,回调函数处于其它函数的内部this不会与任何对象绑定,为undefined. 解决方案: 一)将指向vue对象的this赋值给外部方法定义的属性,然后在内部方法中使用该属性 二)使用箭头函数 补充:解决 vue

  • 手写Vue2.0 数据劫持的示例

    一:搭建webpack 简单的搭建一下webpack的配置.新建一个文件夹,然后init一下.之后新建一个webpack.config.js文件,这是webpack的配置文件.安装一下简单的依赖. npm install webpack webpack-cli webpack-dev-server -D 在同级目录下新建一个public/index.html和src/index.js,作为出口文件和入口文件. j简单配置一下webpack, 在webpack.config.js文件中: cons

  • 关于vuex强刷数据丢失问题解析

    vuex-persistedstate 核心原理:在本地存储中存入所有的vuex数据,页面刷新时到缓存中取数据,放到vuex中 下载:$ npm install vuex-persistedstate -S 在store中引入插件 import persistedState from 'vuex-persistedstate' const store = new Vuex.Store({ // ... plugins: [persistedState()] }) vuex-persistedst

  • Vue组件传值过程中丢失数据的分析与解决方案

    前言 在上一篇文章 JavaScript 中的两种数据类型中,分别介绍了基本类型和引用类型,以及引用类型的浅拷贝与深拷贝.这里需要注意的是,该文章中深拷贝引用类型值的方法,并不是完美的,引用类型值中的某些属性值,仍不能完整地复制到新的变量中.比如函数值,在深拷贝过程中,就会丢失. 问题 在实际项目中,假如使用了二次封装的组件,并且封装的组件内部做了一些属性值的深拷贝操作,就有极有可能因为传入的属性值是引用类型的值,导致丢失部分数据. 举例 以基于 el-table 封装的 ak-table 组件

  • Vue中避免滥用this去读取data中数据

    前言 在Vue中,data选项是个好东西,把数据往里一丢,在一个Vue组件中任何一个地方都可以通过this来读取data中数据.但是要避免滥用this去读取data中数据,至于在哪里要避免滥用,如果滥用会导致什么后果,本专栏将会一一揭晓. 一.用this读取data中数据的过程 在Vue源码中会把data中数据添加getter函数和setter函数,将其转成响应式的.getter函数代码如下所示: function reactiveGetter() { var value = getter ?

  • Vue 如何追踪数据变化

    背景 工作中时不时就会遇到这种情况:页面上显示的数据不对,需要前端同事进行定位. 在一个使用 Vue 搭建的 SPA 应用上,页面最终展示的数据,从初始化,到最后的展示,过程可能很简单,也可能很复杂.遇到复杂的数据流,没有合适的方法,排查起来会很头疼. 如果能够看到数据发生变化时的调用栈,就可以知道在错误的数据生成前,发生了什么,是哪一步的错误,导致了最终的错误.顺着调用栈给出的线索找下去,就能够快速定位到问题. 例子 <template> <div> <!-- 预期输出:h

  • Vue2.0/3.0双向数据绑定的实现原理详解

    Vue2.0/3.0 双向数据绑定的实现原理 双向数据绑定简意 即数据的改变能让页面重新渲染 Vue2.0 ES5的原理: Object.defineProperty 对数据进行拦截 简单小案例 <body> 姓名: <span id="name"></span> <br /> <input type="text" id="inputName" /> </body> 改变in

  • Vue 重置data的数据为初始状态操作

    在某些情况下,需要重新使用data中的数据,但是data中的数据已经被各种表单.变量等赋值,那么怎么重置data的值呢? 1. 逐个赋值 ... data() { return { name: '', sex: '', desc: '' } } ... // 逐个赋值 this.name = '' this.sex = '' this.desc = '' 这个方法比较笨,当然也可以实现效果,但是一个一个去重新赋值比较麻烦而且代码看起来也会比较乱. 下面这个方法肯定是你喜欢的,一行代码搞定- 2.

  • vue 数据双向绑定的实现方法

    1. 前言 本文适合于学习Vue源码的初级学者,阅读后,你将对Vue的数据双向绑定原理有一个大致的了解,认识Observer.Compile.Wathcer三大角色(如下图所示)以及它们所发挥的功能. 本文将一步步带你实现简易版的数据双向绑定,每一步都会详细分析这一步要解决的问题以及代码为何如此写,因此,在阅读完本文后,希望你能自己动手实现一个简易版数据双向绑定. 2. 代码实现 2.1 目的分析 本文要实现的效果如下图所示: 本文用到的HTML和JS主体代码如下: <div id="ap

  • vue+canvas实现数据实时从上到下刷新瀑布图效果(类似QT的)

    话不多说了,先上一张Demo图,实现的功能有:左侧图例.右侧瀑布图.鼠标移入弹出当前坐标对应的数据信息(有优化的空间,大家自由发挥). 图例使用到的插件 这里推荐使用安装npm插件colormap 瀑布图主体内容 这里不多做解释了,都是一些原生标签还有vue绑定的事件,可以根据实际项目情况自己封装成组件,我这里是写在一起的. <template> <div> <div class="content"> <div class="neir

  • SpringBoot+Vue实现数据添加功能

    一.添加代码生成器 用来自动为数据库映射类建立:mapper.service.controller 注:代码生成器的写法,参考官方文档:https://mp.baomidou.com/ package com.hanmh.utils; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomido

随机推荐