前端方案

数据埋点方案、监控方案

1、埋点分析

1、流程

数据采集 -> 上报 -> 数据分析 -> 监控
产品与数据分析:埋点名称、埋点信息、什么时候触发埋点
前端:确认埋点是否可行,是否可写
产品:GrowingIO、神策、诸葛IO、Heap

2、埋点数据

公用字段:
1、埋点的标识信息:eventId、eventType:click、pv、uv等
pv,page view 页面浏览次数,A用户访问页面两次,2pv
uv,user page view,按用户去重,A用户访问页面两次,2uv
2、业务自定义的信息,电商网站sku,eg:鞋子,颜色,size组合,红+42为一条sku
3、通用设备信息/用户信息:userId(登录),deviceId(未登录),useragent(Andriod/iOS/Huawei)、timestamp、location
formatTime:方便数据分析、errorStack、errorMsg(错误堆栈、信息)、level日志基本、traceId(错误标记,可将错误标记在区块中,便于错误分析)

3、埋点上报

1、实时上报:调用report之后立即发送请求
2、延时上报:sdk内部统一收集业务方要上报的信息,依托于防抖或者在浏览器空闲时间或者在页面卸载前统一上报,上报失败做补偿。
3、埋点补偿

4、埋点方案

1、代码埋点:自定义属性和事件,对服务器的压力小
2、无埋点:无代码,框架自动采集全部事件及埋点数据,再由后端进行数据筛选和埋点分析,问题:性能不佳、无法个性化
1、实现:监听所有事件,上报所有点击事件以及对应的事件所在的元素,最后通过后台去分析数据
2、监听window事件,获取元素唯一标识id(getXPath)以及位置
3、可视化埋点:将业务代码与埋点代码分离,并通过搭建的可视化平台在输入的业务代码中添加埋点事件,最后输出的代码为业务代码与埋点代码耦合而成。

2、埋点系统

  • 原理:get请求1*1像素的git图片:体积最小、能完成完整的http请求、比xmlHttpRequest对象发送get请求性能好、跨域好
  • 埋点数据:埋点标识信息、设备信息、用户信息
  • 实时上报
  • 使用非侵入式埋点代码埋点:h5使用装饰器、小程序重写page和app对象,对生命周期及相关事件添加埋点hook。
  • 后端:pv服务器记录web日志,保存为非机构化数据
    • bdp通过ftp方式每日获取pv日志,进行结构化处理,然后入湖
    • pv服务器描述pv日志通过kafka异步消息推送至bdsp并实时入湖
  • 反哺:入湖数据分析,客户画像描述。
  • 扩展:
    • 异步上报:防抖、上报失败补偿机制
    • 无埋点:使用sdk封装好逻辑,业务侧使用,监听页面所有事件,上报点击事件、事件所在元素,发送后台分析
      • 监听所有事件:捕获机制,监听window元素
      • 获取元素唯一标识:xPath
      • 获取元素位置:offsetX、offsetY
    • 小程序和H5两端代码设计,采用设计模式基类复用,实现不同。
      • 数据采集八色Logger基类:
        • 小程序MPlogger:小程序api
          • getCurrentPages
          • wx.getSystemInfoSync
          • wx.onError
          • wx.onUnhandlerRejection
        • H5端H5Logger:浏览器API
          • window.location.href
          • window.navigator.userAgent
          • window.addEventListener
      • 用户访问页面路径
        • H5端:根组件watch route
        • 小程序:onLoad Mixin
      • 使用axios库全局监听请求,记录日志进行分析

3、低代码平台设计

背景:标准化工程解决前端工程问题;随着组件的标准化完善,使用区块和组件像积木一样快速搭建页面可极大提升研发效能。
架构设计:

  • 组件拆分为:元组件(标准化组件实现)和布局组件,布局组件可嵌套元组件及复合组件
  • 整体布局:物料区、渲染引擎、配置面板
    • 物理区:元组件和布局组件
      • 元组件和布局组件通过require.context自动导入
      • 添加对应的渲染组件用于组件逻辑封装和解耦,后续调用render进行渲染,可使用jsx模板
    • 渲染引擎:
      • 输入:树形的jsonSchema结构,循环调用上述渲染组件进行渲染。
      • 拖拽:是哦也能够h5的拖拽事件,内部可使用draggable事件包裹进行拖拽
  • 配置面板:不同类型的组件定义对应类型的配置面板
  • 组件输出:vue原文件,通过拼接字符串实现,可借助服务端实现
    • 区块依赖组件:可使用标签替代在vue主文件中,服务端配置源代码路径
    • 前端发送对应的vue组件服务端解析后最终打包返回前端。

4、无限列表滚动方案

1、下拉到底,继续加载数据并拼接
2、数据太多,做虚拟列表展示:
1、首屏加载的时候,只加载可视区域内需要的列表项
2、滚动时,动态计算,获得可视区域内的列表项,并且将非可视区域内存在的列表项删除。
3、虚拟列表

  • 计算当前可视区域开始数据索引startIndex
  • 计算当前可视区域结束索引endIndex
  • 计算当前可视区域的数据,并渲染在页面上
  • 计算开始startIndex在总体列表中的位置偏移位置startOffset,并且设置到列表上

4、滚动
由于只是对可视区域内的列表进行渲染,为了保证列表容器的高度并可正常的触发滚动
容器:infinite-list-container,相对定位
需要一个元素来撑开高度保证滚动:infinite-list-phantom,绝对定位,z-index=0
需要一个元素展示真正渲染的数据:infinite-list,绝对定位,z-index=-1

  • 监听滚动:监听infinite-list-container的滚动事件,获取scrollTop
    • 可视区域的高度:screenHeight
    • 列表项的高度:itemSize
    • 列表数据:listData
    • 当前滚动位置:scrollTop
  • 最终想要的数据
    • 列表总高度:listHeight=listData.length*itemSize
    • 可显示的列表项:visibleCount=Math.ceil(screenHeight/itemSize)
    • 数据的起始索引:startIndex=Math.floor(scrollTop/itemSize)
    • 数据的结束索引:endIndex=startIndex+visibleCount
    • 列表真正显示数据:visibleData=listData.slice(startIndex, endIndex)
    • 偏移量:startOffest=scrollTop-(scrollTop%itemSize),当滚动后,由于渲染区域相对于可视区域已经发生了偏移,此时需要获取一个偏移量startOffset,通过样式控制将渲染区域偏移至可视区域中。
  • 无限滚动:当滚动触底,就加载新一批数据,拼接到原来的数据上
<template>
  <div class="infinite-list-container" ref="list" @scroll="scrollEvent">
    <!-- 撑起列表总高度 -->
    <div class="infinite-list-phantom" :style="{ height: listHeight + 'px'}"></div>
    <!-- 实际渲染列表 -->
    <div class="infinite-list" :style="{ transform: getTransform }">
      <div class="infinite-list-item" v-for="item in visibleData" :key="item.id">
        <div class="left-section">
          {{ item.title[0] }}
        </div>
        <div class="right-section">
          <div class="title">{{ item.title }}</div>
          <div class="desc">{{ item.content }}</div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
  // @Component
  import Vue from 'vue'
  interface Data {
    id: '',
      title: '',
      content: '',
  }
  export default class VirtualList extends Vue {

    public listData: Data[] = [];
    public itemSize: number = 50;
    // 可视区域高度
    public sceenHeight: number = document.documentElement.clientHeight || document.body.clientHeight;
    // 可显示的列表项数
    public visibleCount: number = Math.ceil(this.sceenHeight / this.itemSize);
    // 偏移量
    public startOffset: number = 0;
    // 起始索引
    public start: number = 0;
    // 结束索引
    public end: number = this.start + this.visibleCount;

    public $refs: {
      list: any;
    };

    // 列表总高度
    get listHeight() {
      return this.listData.length * this.itemSize;
    }

    // 偏移量对应的style
    get getTransform() {
      return `translate3d(0, ${this.startOffset}px, 0)`
    }

    // 获取真实显示列表数据
    get visibleData() {
      return this.listData.slice(
        this.start,
        Math.min(this.end, this.listData.length)
      )
    }

    getTenListData() {
      if(this.listData.length >= 200) {
        return [];
      }
      return new Array(10).fill({}).map(item => ({ id: Faker.random.uuid(), title: Faker.name.title(), content: Faker.random.words() }))
    }

    created() {
      this.listData = this.getTenListData();
    }

    scrollToTop() {
      this.$refs.list.scrollTo({
        top: 0,
        left: 0,
        behavior: 'smooth'
      })
    }

    public scrollEvent(e: any) {
      // 当前滚动位置
      const scrollTop = this.$refs.list.scrollTop;
      // 此时的开始索引
      this.start = Math.floor(scrollTop / this.itemSize);
      // 此时的结束索引
      this.end = this.start + this.visibleCount;
      if(this.end > this.listData.length) {
        this.listData = this.listData.concat(this.getTenListData());
      }
      // 此时的偏移量
      this.startOffset = scrollTop - (scrollTop % this.itemSize); 
    }


  }
</script>
<style>
  .infinite-list-container {
    margin-top: 10px;
    height: 99%;
    overflow: scroll;
    position: relative;
    -webkit-overflow-scrolling: touch;
  }
  .infinite-list-phantom {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    z-index: -1;
  }
  .infinite-list {
    left: 0;
    right: 0;
    top: 0;
    position: absolute;
    text-align: center;
  }
  .infinite-list-item{
    background-color: white;
    box-shadow: 0 0 10px rgba(144, 144, 144, 0.15);
    border-radius: 5px;
    display: flex;
    align-items: center;
    justify-content: center;
      margin-top: 10px;
      }
      </style>

5、监控系统

1、异常类型

1、JS代码异常:运行错误
2、Promise异常
3、静态资源加载异常
4、api请求异常
5、跨域script异常

2、捕获方法

1、单点捕获:try/catch
2、全局捕获
1、js error:window.addEventListener('error')
2、resource error:window.addEventListener('error')
3、Promise异常:window.addEventListener('rejectionhandled | unhandlerejection')
4、重写XMLHttpRequest:添加捕获api请求异常
5、vue框架
1、打开所有日志和警告:Vue.config.silent=true
2、errorHandler函数:指定组件的渲染和观察期间未捕获错误的处理函数。
6、react框架
1、componentDidCatch:如果render()函数抛出错误,则会触发该函数,该函数包含错误堆栈的info。这个生命周期也会在后代组件抛出错误时被调用,但是不会捕获事件处理器和异步代码的异常。它会在【提交】阶段被调用,所以允许出现副作用
2、getDerivedStateFromError:自带的捕获所有子组件中错误的方法,这个生命周期会在后代组件抛出错误时被嗲用。注意这个是在渲染阶段调用的,所以不允许出现副作用

posted @ 2023-02-28 10:41  中亿丰数字科技  阅读(178)  评论(0)    收藏  举报