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

@firesoon/log-sdk

v3.0.0-beta.14

Published

监听点击事件进行埋点

Downloads

17

Readme

@firesoon/log-sdk

Usage

安装

yarn add @firesoon/log-sdk
// 使用
import track from '@firesoon/log-sdk';

const autoClickHandle = (e) => {
  const activeTabKey = sessionStorage.getItem('activeTabKey');
  const menuUuid = sessionStorage.getItem('uuid');
  // 获取单击数据
  const log: boolean | object = track.getLogData(e);
  if (log) {
    track.sendBeacon('/beacon', {
      ...log,
      menuNo: activeTabKey,
      menuUuid,
    });
  }
};

// 绑定单击事件监听
track.listener.on(document.body, 'click', autoClickHandle);

在 umi 搭建的项目中使用

global.ts

import track from '@firesoon/log-sdk';

const autoClickHandle = (e) => {
  const activeTabKey = sessionStorage.getItem('activeTabKey');
  const menuUuid = sessionStorage.getItem('uuid');

  // 获取单击数据
  const log = track.getLogData(e);
  try {
    if (log) {
      const logData = {
        ...log,
        menuNo: activeTabKey,
        menuUuid,
      };
      // 上报数据或做缓存
      track.sendBeacon('/beacon', logData);
    }
  } catch (e) {
    // 错误处理
  }
};

// 绑定单击事件监听
track.listener.on(document.body, 'click', autoClickHandle);

app.ts

import { generateUUID } from '@/utils/utils';

// 生成UUID
let uuid = generateUUID();
let lasePathName;
export function onRouteChange(params) {
  const {
    action,
    location: { pathname },
  } = params;

  // query变化时不重新生成
  if (action != null && pathname !== lasePathName) {
    uuid = generateUUID();
    sessionStorage.setItem('uuid', uuid);
    lasePathName = pathname;
  }
}

models/global.ts

import track from '@firesoon/log-sdk';

* queryUserInfo(_, { ... }) {
// 可以在获取用户信息的回调里配置通用埋点属性
    track.config({
      baseInfo: {
        hospitalCode,
        appVersion,
        sid,
        roleType,
        userId,
        departmentName,
        appCode: 'drgsmsfl',
      },
    })
}

utils.ts

export function generateUUID() {
  let d = new Date().getTime();
  if (window.performance && typeof window.performance.now === 'function') {
    d += performance.now(); //use high-precision timer if available
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = (d + Math.random() * 16) % 16 | 0;
    d = Math.floor(d / 16);
    return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
  });
}

采集规则

  • 所有的button点击都会自动采集
  • 采集通过classConfig配置的classname的节点及其子节点

个性化

data-auto-track

是否自动上报的节点(优先级最高)

  • Type: boolean
  • Default: 无值, 全部自动上报

只要点击元素或其父元素包含此属性为 false,则都不会自动埋点。设为 true 没有意义,不会采集无法进入 classname 或者 xpath 的 dom 节点。 该属性存在的意义即对复杂组合做个性化的埋点需求时,关闭自动埋点规则。

data-name

需要记录名称的操作

获取当前节点或其父节点第一个出现的 data-name 的值,保存到 value 中.

  • Type: string
// 普通节点使用
<button data-name="下载科室病例明细">下载</button>
// {..., value: '下载科室病例明细', ... }

// 在下拉框中使用, 组件库需封装一层
// CustomSelect.tsx
<div data-name={props.label}>
  <label>{props.label}</label>
  <Select {...props}></Select>
</div>

// index.tsx
<CustomSelect label="出院科室" />
// { ..., value: '出院科室', content: '妇科' }

select 等浮窗需要设置挂载到 trigger 或 trigger.parentNode 下, 否则 data-name 不在其父元素上无法获取

 <ConfigProvider
-  getPopupContainer={triggerNode => triggerNode.parentNode}
+  getPopupContainer={node => {
+    if (node) {
+      return node.parentNode;
+    }
+    return document.body;
+  }}
 >
   <App />
 </ConfigProvider>

config

全局配置

配置获取

type IConfigName = 'baseInfo' | 'autoTrackCls' | 'useInnerTextCls';
// 获取baseInfo的配置
track.config('baseInfo');
track.config({
  classname: 'my-custom-class',
  canUseInnerTextCls: 'my-custom-inner-text',
  route: {
    api: '/api/track/report',
    onChange: (data) => console.log(data),
    exclude: ['/'],
  },
  baseInfo: {
    app: 'mrbp',
  },
});

classname

允许埋点的节点 classname(对点击元素和其父元素们判断)

  • Type: string[] | string;
  • Default
[
  'ant-input-suffix', // Search的suffix
  'ant-calendar-cell', // DatePicker
  'ant-calendar-footer-btn',
  'ant-calendar-month-panel-cell', // MonthPicker
  'ant-tabs-tab', // Tabs
  'ant-select-dropdown-menu-item', // Select下拉框
  'ant-checkbox-wrapper', // CheckBox
  'ant-radio-wrapper', // Radio
  'ant-radio-button-wrapper', // RadioButton
  'fs-select-dropdown-menu-item', // ant-ui QuarterPicker'
  'auto-track', // 需要自动埋点元素使用
  'auto-track-inner-text', // 需要自动埋点且获取innerText作为value的元素使用
];

建议普通点击元素都使用 button 触发自动采集。若有必要配置 classname, 可配置一个通用的(如'auto-track'), 所有需要自动采集的节点都使用该 classname.

使用 classname 注意事项

1. 使用配置 classname 作为数据自动采集过滤条件时,为避免采集到无效点击,必须保证添加有点击事件的节点 classname,而不是父容器的

2. 普通元素由于数据敏感性,不会获取 innerText,只有 button 获取。所以普通标签配置了 classname 如果不自动埋点,需要配置 data-name(或 title)

// 去掉所有默认的 传入空数组或者空字符串
track.config({
  classname: 'my-custom-xx',
});

canUseInnerTextCls

使用 innerText 作为 value 的元素 class 配置(并不是设置了就一定取 innerText, 有优先级)

  • Type: string[] | string;
  • Default
[
  'ant-tabs-tab', // Tabs
  'track-inner-text', // 获取innerText作为value的元素使用
  'auto-track-inner-text', // 需要自动埋点且获取innerText作为value的元素使用
];
// { value: '兄弟萌埋它' }
<p className="auto-track-inner-text">
兄弟萌埋它
</p>

// 或许会有这种使用场景(有更好的使用方式, 只是为了举例)
<div className="auto-track">
    <p>这个点击不会埋</p>
    <p className="track-inner-text">
     不用auto-track埋不了
    </p>
     <p className="track-inner-text">
      需要搭配auto-track使用
    </p>
</div>

baseInfo

通用的上报数据配置

  • Type: object;
  • Default: { version };

~~默认不包含操作系统相关信息, 可通过使用{ includePlatform: true }添加相关信息~~

操作系统相关信息统一使用 Nginx 去获取

route (测试功能)

路由变化时触发, 不使用则不需要配置

apionChange二选一, 没有 onChange 会根据 api 自动上报{pathname, time}

type IData = {
  pathname: string;
  stamp: number;
};

interface IRouteChange {
  api?: string; // 上报地址
  onChange?: (data: IData) => void; // 路由变化回调
  exclude?: string[]; // 不需要采集的pathname
}
track.config({
  route: {
    api: '', // 上报地址
    exclude: ['/'],
  },
});

优先级

自动采集

  1. data-auto-track=false
  2. button, track.classConfig

数据获取

value

  1. data-name, Elememt.getAttribute('placeholder')

  2. Elememt.getAttribute('title')

  3. Element.getInnerText

待改进

  1. 数据采集
  • radio, checkbox 的文字获取采用nextElementSibling.innerText获取,依赖 dom 结构

  • 考虑是够支持data-auto-track=true则自动埋点. (考虑可以通过 classname 配置, 如果支持此功能会有重复)

  1. 数据上报
  • 普通的数据上报采用 Image Beacon, unload 和 beforeUnLoad 使用track.sendBeacon

  • 上报接口服务端返回'204 No Content'作为响应,节省服务器资源

LICENSE

ISC

FAQs

  1. 为什么配置了 classname 不生效?

参见 classname 使用注意事项