npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

util-demo-1019

v1.0.2

Published

test

Downloads

5

Readme

技术选型

  • 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>