@ued2345/octopus-util

1.1.7-1 • Public • Published

技术选型

  • typescript
  • babel
  • webpack

项目结构介绍

├── build
│   ├── build.js                // 执行打包 npm run build
│   └── webpack.conf.js         // webpack配置文件
├── dist                       
│   ├── octopus-util.min.js          // 打包后的文件,也是被其它项目引入的文件
├── src
│   ├── device
│   │   │── getBrowser.ts       // 获取操作系统类型
│   │   └── gettInApp.ts        // 判断是否在APP内
│   ├── cookie
│   │   │── getCookie.ts        // 获取cookie
│   │   │── setCookie.ts        // 设置cookie
│   │   └── delCookie.ts        // 删除cookie
│   ├── jsbrige
│   │   │── init                // 二级目录
│   │   │   │── bridge.ts       // 定义类型
│   │   │   └── brigeInit.ts    // 创建bridge对象
│   │   └── jsBrige.ts          // export jsbridge方法
│   ├── monitor                
│   │   │── catchPerformance.ts // 性能监控上报
│   │   └── catchError.ts       // 错误收集上报
│   ├── vue-plugin
│   │   │── directives          // vue全局指令
│   │   │   └── bang            // 埋点指令
│   │   └── vuePlugin.ts          // export vuePlugin
│   └── index.ts                // webpack打包入口文件 
├── .babelrc                    // babel配置文件
├── tsconfig.config.js          // ts配置文件
├── README.md                   // 项目说明文档

构建与部署

第一步:npm run build
第二步:npm发包

API文档

octopus = {
    // 获取操作系统类型
    getBrowser: () => {
        android: boolean,
        gecko: boolean,
        iPad: boolean,
        iPad: boolean,
        iPhone: boolean,
        ios: boolean,
        mobile: boolean,
        presto: boolean,
        qq: boolean,
        trident: boolean,
        webApp: boolean,
        webKit: boolean,
        weixin: boolean,
    },
    // 通过userAgent 判断是否在APP内
    getInApp: (uName) => {return boolean},
    // JsBridge 与原生通信
    JsBridge: {
        // js调用webview事件
        callHandle: ({method, params}, callback) => {},
        // webView调用JS事件
        registerHandle: (method, callback) => {},
    },
    // cookie 操作
    setCookie: (name, value, days) => {},
    getCookie: (name) => {},
    delCookie: (name) => {},
    VuePlugin, // vue工具(内含insert方法)
    CatchError, // 错误收集
    CatchPerformance // 性能监控
}

安装使用

npm install --save-dev @ued2345/octopus-util

项目中调用

  • 浏览器
<script src="octopus-util.min.js"></script>
<script>
    var browser = window.OcUtil.getBrowser()
</script>
  • webpackregisterError
import OcUtil from '@ued2345/octopus-util'
const browser = OcUtil.getBrowser()

// 或单独引用方法
import {getBrowser} from '@ued2345/octopus-util'

错误收集

  • window.onerror
  • 资源加载失败捕获
  • Vue组件错误捕获
  • Promise中没有catch捕获错误会被捕获
  • vue项目中接入
import Vue from 'vue'
import { CatchError} from "@ued2345/octopus-util";
CatchError.getInstance(Vue, {
  reportFun: function(data) { // 错误上报
    console.log('error:', data)
  },
})

/**
* 上报数据格式(data)
*  data = {
*   type: string;     // 错误类型:ERROR_RUNTIME, ERROR_LOAD__TYPE(SCRIPT, LINK,IMG, AUDIO, VIDEO), ERROR_VUE, ERROR_REJECT
*   level: number;    // error : 1 warning: 2 info: 3
*   message: string;  // 错误详情
*   url: string;      // 错误文件url或当前url
*   col?: number;
*   row?: number; 
*  }
**/
  • 相关配置说明
// 调用方法
CatchError.getInstance(Vue, opts)

/**
* Vue: null 不会捕获vue 错误信息
* opts: {
*  isReportNow: boolean; //是否立即上报(是:delay时间后立即执行多次上报,否:每隔delay时间上报一次)
*  delay: number; // 延迟多长时间上报(默认3000)
*  random: number; // 抽样上报 (0-1)1全量(默认全量)
*  repeatNum: number; // 重复上报次数(默认3次),
*  reportUnhandledRejection: boolean; // 是否捕获promise.reject错误
*  reportJsError: boolean; // 是否上报js运行时错误
*  reportResourceError: boolean; //是否上报资源加载错误
*  reportFun: CallBack; // 上报错误函数
* }
**/

性能监控

  • Navigation Timing API
  • Resource Timing API
  • NetWork Information API
  • Paint Timing API
  • FirstInput Timming API

vue项目接入

import {CatchPerformance} from "@ued2345/octopus-util";
CatchPerformance.getInstance({
    reportFun: (data) => { // 性能上报方法
        console.log('sssss:', data)
    }
})

// CatchPerformance.getInstatnce(opts)
interface opts {
    paintTiming?: boolean; // 渲染时间点信息
    navigationTiming?: boolean; // 客户端收集性能数据
    resourceTiming?: boolean; // 资源信息
    firstInputTiming?: boolean; // 首次页面交互时间
    networkInformation?: boolean; // 网络信息
    dataConsumption?: boolean; // 是否统计资源大小
    random?: number; // 抽样上报(0-1)1全量
    reportFun?: (data) => void; // 上报函数
}
// reportFun: (data: Data) => void
type EventType = 
  | "navigationTiming"
  | "networkInformation"
  | "resourceTiming"
  | "firstInputTiming"
  | "paintTiming"
type Data = {name: EventType, data: any[]}[]
  • Navigation Timing API
interface PerfumeNavigationTiming {
  redirectTime?: number; // 重定向时间
  dnsCatchTime?: number; // dns缓存时间
  dnsTime?: number; // dns查询耗时
  ttfbTime?: number; // 读取第一个字节的时间
  unloadTime?: number; // 卸载页面耗时
  tcpTime?: number; // tcp链接耗时
  reqTime?: number; // request请求耗时
  domTreeTime?: number, // 创建dom树
  domAnalyzeTime?: number; // 解析dom树耗时
  blankTime?: number; // 白屏时间
  domReadyTime?: number; // domReadyTime耗时
  loadTime?: number; // onload耗时
}
const getNavigationTiming = () => {
    const t = (typeof this.w.PerformanceNavigationTiming === 'function') ? performance.getEntriesByType('navigation')[0] as any : this.wp.timing ;
    if (!t) {
      return {};
    }
    // We cache the navigation time for future times
    return {
        // 重定向时间:
        redirectTime: t.redirectEnd - t.redirectStart,
        // DNS缓存时间
        dnsCatchTime: t.domainLookupStart - t.fetchStart,
        // DNS解析时间
        dnsTime: t.domainLookupEnd - t.domainLookupStart,
        // ttfb 读取第一个字节的时间
        ttfbTime: t.responseStart - t.domainLookupStart,
        // 卸载页面耗时
        unloadTime: t.unloadEventEnd - t.unloadEventStart,
        // tcp链接耗时
        tcpTime: t.connectEnd - t.connectStart,
        // request请求耗时,页面下载耗时
        reqTime: t.responseEnd - t.requestStart,
        // dom渲染完成时间
        domTreeTime: t.domInteractive - t.responseEnd,
        //解析dom树耗时
        domAnalyzeTime: t.domComplete - t.domInteractive,
        // 白屏时间
        blankTime: t.domInteractive - t.fetchStart,
        // domReadyTime
        domReadyTime: t.domContentLoadedEventEnd - t.fetchStart,
        // onload耗时、首屏时间
        loadTime: t.loadEventEnd - t.fetchStart
    };
}

return {
  name: 'navigationTiming',
  data: <PerfumeNavigationTiming>Object
}
  • Resource Timing API
type PerformanceEntryInitiatorType =
  | 'beacon' // 源于 sendBeacon 方法
  | 'link'
  | 'fetch' // 来源于 fetch 方法
  | 'img'
  | 'other'
  | 'script'
  | 'xmlhttprequest';
return {
  name: 'resourceTiming',
  data: {
    resourceTiming: <PerformanceEntryInitiatorType>[],
    dataConsumption: this.config.dataConsumption ? <PerformanceEntryInitiatorType>Object : null
  },
}
  • NetWork Information API
type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';
interface PerfumeNetworkInformation {
  downlink?: number; // 下行速度/带宽 Mb/s为单位的有效带宽
  effectiveType?: EffectiveConnectionType; // 有效网络连接类型
  onchange?: () => void; // 监听网络变化
  rtt?: number; // 估算的往返时间
  saveData?: boolean; // 打开/请求数据保护模式
}
return {
  name: 'networkInformation',
  data: <PerfumeNetworkInformation>Object
}
  • Paint Timing API
let startTime: number
return {
  name: 'paintTiming',
  data: [{
    name: 'first-paint',
    value: startTime,  
  }, {
    name: 'first-contentful-paint',
    value: startTime
  }]
}
  • FirstInput Timming API
let duration: number // 从用户点击到响应时间
let event: Event // mousedown, pointerdown
return {
  name: 'firstInputTiming',
  data: [{
    name: event,
    value: duration  
  }]
}

vue相关工具

埋点工具
  • 目的

优化大部分在项目中业务代码中混入埋点代码的情况

  • 适用场景

可以改善以下三种情况下埋点代码的书写方式:

  1. 曝光: exposure(当该元素95%以上展示在可视区的时候触发)。
  2. 挂载: mounted(当该元素的dom生成的时候触发)。
  3. 点击: click(当该元素被点击的时候触发)。
  • 使用示例
// main.js 或单页应用入口js

import {VuePlugin} from '@ued2345/octopus-util'
// 模拟上报函数
// 1。若cbDataType为默认的情况下,参数同vue自定义指令
const bang = (el, binding) => {console.log(`触发上报,类型:${binding.arg},值: ${binding.value}`)}
// 2、若cbDataType为'array'的情况下, 参数为 [{el, binding, vnode, oldVnode}]
// const bang = (dataArr) => {
//   dataArr.forEach(item => {
//     console.log(`触发上报,类型:${item.binding.arg},值: ${item.binding.value}`)
//   })
// }
Vue.use(VuePlugin, {
  bang: {
    // 自定义配置
    cb: bang
  }
})

// test.vue
<div v-bang:exposure="`我曝光啦`"></div> //曝光后输出: "触发上报,类型:exposure,值: 我曝光啦"
<div v-bang:click="`我被点啦`"></div> //点击后输出: "触发上报,类型:click,值: 我被点啦"
<div v-bang:mounted="`我出生了啦`"></div> //dom生成后输出: "触发上报,类型:mounted,值: 我出生了啦"
  • 低版本兼容 因为低版本机型不支持intersectionObserver需要安装相应polyfill,具体步骤如下:

    1. npm i intersection-observer --save
    2. 在单页应用入口文件处顶部引入,如vue-cli里的main.js
      import 'intersection-observer'
    
  • 支持配置

参数 说明 类型 是否必须 默认
cb 触发上报后的回调函数:当cbData为default时默认返回数据类型为:(el: HTMLElement, binding: DirectiveBinding, vnode: vnode, oldVnode: vnode) => void 详见官网。当cbDataType为'array'时会返回[{el, binding, vnode, oldVnode}]
clickThrottleInterval 点击事件上报节流的间隙时间 number 500
exposureMulti 元素未销毁时是否可以多次发送曝光统计事件 boolean false
imgErrExposure 图片加载出错的时候是否算曝光成功(曝光事件绑定在img元素上时) boolean false
cbDataType 回调函数收到的参数类型 'default'/'array' 'default'
exposureUseCache 是否对曝光事件使用缓存, cacheLen:缓存的消息条数, cacheTime: 缓存的时间(ms) {cacheLen: number, cacheTime: number} false
exposureRatio 元素在可视区中展示超过多少比例时触发曝光 number 0.95
  • 注意事项
    • 在给不定高图片绑定曝光事件时,由于网络原因可能出现后面的图片先加载完曝光的情况,造成未在可视区内但是却曝光的假象。
      • 原因:其实该图确实已经曝光,但是因为时间过短(前面图加载完之后该图就被挤出可视区了)肉眼难以察觉而已。
      • 解决方法: 1.还是定高吧 2.这可能不能算个bug,因为它确实曝光过了(后期可能会加曝光最短时长的功能,能解决该问题)。
    • 若同时给v-if,v-else元素绑定mounted埋点事件时,在两者tagName相同的情况下,切换显示时会造成后一个埋点事件未触发。
      • 原因: 这是因为在切换显示时由于v-if和v-else的标签一样,而且没有key等唯一标识,在dom diff过程中默认该元素未发生变化,所以此时后面元素的指令insert钩子函数不会触发。
      • 解决方法: 给两个元素设置不同的tagName或者给元素绑定唯一key属性, 如下
        <div v-if="show" v-bang:mounted="`显示1`" key="显示1">显示1</div>
        <div v-else v-bang:mounted="`显示2`" key="显示2">显示2</div>

Readme

Keywords

Package Sidebar

Install

npm i @ued2345/octopus-util

Weekly Downloads

26

Version

1.1.7-1

License

ISC

Unpacked Size

66.7 kB

Total Files

22

Last publish

Collaborators

  • ued2345com
  • cssmagic