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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jafish/stat

v1.0.3

Published

埋点工具

Readme

@jafish/stat

npm

埋点工具

注意事项

  • onElement 使用了节点实例,需要注意及时的释放
  • 使用 useStat 时,需注意回调函数内是否有外部引用
  • 除去 useWatchRoute 及 element 相关的方法,stat本身可用在非web端(小程序之类)
  • 一般来说需要在 useUpload 这个统一埋点出口做一些符合需求的优化
  • 节点相关的埋点在 react 可以封装成一个组件,在 vue 可以封装成一个指令,便于使用

使用

import * as $stat from '@jafish/stat'
import * as watchRoute from '@jafish/watch-route'

/**
 * @description 统一的上传入口
 * @param {Object} data 发起埋点传入的数据
 * @param {any} params 发起埋点传入的其他参数,用于自定义操作,如上报记录缓存
 */
$stat.useUpload((data, ...params) => {
    console.log(data, ...params)
})

/**
 * @description 发起埋点
 * @param {Object} data 埋点参数
 * @param {any} params 自定义参数
*/
$stat.onStat({
    target: '页面展示',
    action: 'view',
    actionParams: {
        bbb: 'cc'
    }
}, ...params)

/**
 * @description 用于处理埋点数据的方法
 * @param {Function} 在回调中处理数据,需返回处理后的数据
  */
$stat.usePipe((data, ...params) => {
    // 处理传入的数据,最终数据会到达 useUpload
    const newData = { ...data }
    ...
    return newData
})

// 当使用了 watchRoute 时为设置当前页面默认参数
// 否则设置为持久的默认参数,直至下一次 useStat 替换
// 默认参数会与发起埋点传入的参数做一次 merge
$stat.useStat(() => {
    return {
        page: '首页',
        pageParams: {
            aaa: this.aaa
        }
    }
})

// 重写 useStat
$stat.overrideUseStat(callback => {
    // callback 为 useStat 传入的回调
    // 调用该方法后,默认的 merge 会被取消
    // 需要自己调用 usePipe 进行自定义的数据操作
    // 使用方式可参考源码中的 watch-route.ts 文件
    ...
})

/**
 * @description 使用页面级的数据缓存,可选
 * @description watchRoute 能获取到路由相关信息,具体见 @jafish/watch-route
 * @description watchRoute 即 @jafish/watch-route 模块
 * @param {Object} options
 * @param {Boolean} options.routeChangeClearUseStat 在路由切换的时候默认会移除上一个页面使用的 useStat,防止内存泄漏
  */
$stat.useWatchRoute(watchRoute, { routeChangeClearUseStat: true })

// 使用 useWatchRoute 之后,可以获得一个获取上一个页面数据的便捷方法
$stat.useWatchRoute.getPrevPageStatData()

/**
 * @description 用于处理节点数据的入口
 * @param {Object} param
 * @param {string} param.action 节点上报时的来源 'view' -> 节点展示, 'click' -> 节点被点击
 * @param {Object} data 节点绑定时传入的数据
 * @param {any} params 发起埋点传入的其他参数
*/
$stat.useElement((param, data, ...params) => {
    console.log(param, data, ...params)

    // 在这里走统一入口上报,因为数据只会在 onStat 处理
    $stat.onStat({
        ...data,
        action: param.action
    }, ...params)
})

/**
 * @description 添加需要展示及点击埋点的节点
 * @param {HTMLElement} el 节点实例,因需要使用 el.getBoundingClientRect 判断节点在屏幕中的位置
 * @param {Object} data 埋点参数
 * @param {any} params 自定义参数
 * @return {Function} removeElement 移除埋点节点
  */
const removeElement = $stat.onElement(el, {
    target: 'button',
    actionParams: {
        dd: 'ee'
    }
}, ...params)

// 释放节点绑定
removeElement()

/**
 * @description 移除埋点节点,以另一种方式移除
 * @param {HTMLElement} el 节点实例
  */
$stat.removeElement(el)

// 触发节点的埋点,用于节点展示
// 通常用作于 onscroll 事件,一般来说使用下面的 onScrollAutoView 足够
$stat.onElementView()

// 监听全局 onscroll ,在滚动结束的时候自动的节点展示埋点
$stat.onScrollAutoView()

实践

// main.js
import * as $stat from '@jafish/stat'
import * as watchRoute from '@jafish/watch-route'

$stat.useUpload((data, { cache = '' } = {}) => {
    // 最终结果
    console.log('useUpload', data, cache)
    // 获取上个页面的数据,如果需求需要的话
    console.log('parent', $stat.useWatchRoute.getPrevPageStatData())

    // 拿到结果后,可以进行缓存、防抖等一系列优化,然后进行上传给服务器
})

$stat.usePipe((data) => {
    // 添加公共的默认参数
    if (!data.target) data.target = '页面'
    if (!data.action) data.action = 'view'
    return data
})

// 可选
$stat.useWatchRoute(watchRoute)

// 节点埋点
$stat.useElement((param, data, ...params) => {
    console.log('useElement', param, data, ...params)

    $stat.onStat({
        ...data,
        action: param.action
    }, ...params)
})

// 页面滚动,自动触发节点展示埋点
$stat.onScrollAutoView()


// home.js
componentDidMount() {
    // 当前页面需要使用的默认参数
    $stat.useStat(() => ({
        page: '首页',
    }))

    // 页面展示埋点
    $stat.onStat()

    // 节点展示及点击埋点
    const removeElement = $stat.onElement(this.div.current, {
        target: 'div'
    })

    // 在特定情况下移除节点绑定
    setTimeout(() => removeElement(), 5000)
}

componentWillUnmount() {
    // 页面销毁时,移除节点绑定
    $stat.removeElement(this.div.current)
}

节点埋点封装

react

import React, { Component, createRef } from 'react'
import * as $stat from '@jafish/stat'

export default class StatBox extends Component {
    constructor({ stat }) {
        super()

        this.box = createRef()
    }

    componentDidMount() {
        const { stat } = this.props

        $stat.onElement(this.box.current, stat)
    }

    componentWillUnmount() {
        $stat.removeElement(this.box.current)
    }

    render() {
        const { children } = this.props

        return (
            <div ref={this.box}>
                {children}
            </div>
        )
    }
}

// 使用
<StatBox stat={{ target: 'box' }}>
    xxx
</StatBox>

vue

import Vue from 'vue'
import * as $stat from '@jafish/stat'

// 绑定
const onElement = ( 
    el, 
    { 
        value = {}
    } = {},
) => $stat.onElement(el, value)

// 移除绑定
const removeElement = (el) => $stat.removeElement(el)

Vue.directive('stat', {
    inserted: onElement,
    componentUpdated: onElement,
    unbind: removeElement,
})

// 使用
<div v-stat="{ target: 'div' }"></div>