技术选型
项目结构介绍
├── 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,
},
getInApp: (uName) => {return boolean},
JsBridge: {
callHandle: ({method, params}, callback) => {},
registerHandle: (method, callback) => {},
},
setCookie: (name, value, days) => {},
getCookie: (name) => {},
delCookie: (name) => {},
VuePlugin,
CatchError,
CatchPerformance
}
安装使用
npm install --save-dev @ued2345/octopus-util
项目中调用
<script src="octopus-util.min.js"></script>
<script>
var browser = window.OcUtil.getBrowser()
</script>
import OcUtil from '@ued2345/octopus-util'
const browser = OcUtil.getBrowser()
import {getBrowser} from '@ued2345/octopus-util'
错误收集
- window.onerror
- 资源加载失败捕获
- Vue组件错误捕获
- Promise中没有catch捕获错误会被捕获
import Vue from 'vue'
import { CatchError} from "@ued2345/octopus-util";
CatchError.getInstance(Vue, {
reportFun: function(data) {
console.log('error:', data)
},
})
CatchError.getInstance(Vue, opts)
性能监控
- 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)
}
})
interface opts {
paintTiming?: boolean;
navigationTiming?: boolean;
resourceTiming?: boolean;
firstInputTiming?: boolean;
networkInformation?: boolean;
dataConsumption?: boolean;
random?: number;
reportFun?: (data) => void;
}
type EventType =
| "navigationTiming"
| "networkInformation"
| "resourceTiming"
| "firstInputTiming"
| "paintTiming"
type Data = {name: EventType, data: any[]}[]
interface PerfumeNavigationTiming {
redirectTime?: number;
dnsCatchTime?: number;
dnsTime?: number;
ttfbTime?: number;
unloadTime?: number;
tcpTime?: number;
reqTime?: number;
domTreeTime?: number,
domAnalyzeTime?: number;
blankTime?: number;
domReadyTime?: number;
loadTime?: number;
}
const getNavigationTiming = () => {
const t = (typeof this.w.PerformanceNavigationTiming === 'function') ? performance.getEntriesByType('navigation')[0] as any : this.wp.timing ;
if (!t) {
return {};
}
return {
redirectTime: t.redirectEnd - t.redirectStart,
dnsCatchTime: t.domainLookupStart - t.fetchStart,
dnsTime: t.domainLookupEnd - t.domainLookupStart,
ttfbTime: t.responseStart - t.domainLookupStart,
unloadTime: t.unloadEventEnd - t.unloadEventStart,
tcpTime: t.connectEnd - t.connectStart,
reqTime: t.responseEnd - t.requestStart,
domTreeTime: t.domInteractive - t.responseEnd,
domAnalyzeTime: t.domComplete - t.domInteractive,
blankTime: t.domInteractive - t.fetchStart,
domReadyTime: t.domContentLoadedEventEnd - t.fetchStart,
loadTime: t.loadEventEnd - t.fetchStart
};
}
return {
name: 'navigationTiming',
data: <PerfumeNavigationTiming>Object
}
type PerformanceEntryInitiatorType =
| 'beacon'
| 'link'
| 'fetch'
| 'img'
| 'other'
| 'script'
| 'xmlhttprequest';
return {
name: 'resourceTiming',
data: {
resourceTiming: <PerformanceEntryInitiatorType>[],
dataConsumption: this.config.dataConsumption ? <PerformanceEntryInitiatorType>Object : null
},
}
type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';
interface PerfumeNetworkInformation {
downlink?: number;
effectiveType?: EffectiveConnectionType;
onchange?: () => void;
rtt?: number;
saveData?: boolean;
}
return {
name: 'networkInformation',
data: <PerfumeNetworkInformation>Object
}
let startTime: number
return {
name: 'paintTiming',
data: [{
name: 'first-paint',
value: startTime,
}, {
name: 'first-contentful-paint',
value: startTime
}]
}
let duration: number
let event: Event
return {
name: 'firstInputTiming',
data: [{
name: event,
value: duration
}]
}
vue相关工具
埋点工具
优化大部分在项目中业务代码中混入埋点代码的情况
可以改善以下三种情况下埋点代码的书写方式:
- 曝光: exposure(当该元素95%以上展示在可视区的时候触发)。
- 挂载: mounted(当该元素的dom生成的时候触发)。
- 点击: click(当该元素被点击的时候触发)。
import {VuePlugin} from '@ued2345/octopus-util'
const bang = (el, binding) => {console.log(`触发上报,类型:${binding.arg},值: ${binding.value}`)}
Vue.use(VuePlugin, {
bang: {
cb: bang
}
})
<div v-bang:exposure="`我曝光啦`"></div> //曝光后输出: "触发上报,类型:exposure,值: 我曝光啦"
<div v-bang:click="`我被点啦`"></div> //点击后输出: "触发上报,类型:click,值: 我被点啦"
<div v-bang:mounted="`我出生了啦`"></div> //dom生成后输出: "触发上报,类型:mounted,值: 我出生了啦"
参数 | 说明 | 类型 | 是否必须 | 默认 |
---|
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 |