react埋点插件整理

1. react-tag-component

react埋点组件,持续集成埋点方式

  • 描述: react-tag-component将会持续集成各种埋点方式,简化开发中的埋点需求,为埋点提供统一标准,旨在降低项目中埋点事件对代码的侵入以及埋点数据的混乱。
  • 核心: 1.数据仓库:自定义埋点数据格式。2.埋点事件:定义数据仓库中数据的读取,打埋点事件的注入。3.埋点组件:嵌入Tag事件,用于挂载相应数据。
  • 集成: 项目中加入了travis-ci、mocha、coveralls使得项目集成更加工程化,同时加入demo对UI部分进行测试。

安装

$ npm install --save react-tag-component

示例

import React from 'react'
import { init, TD } from 'react-tag-component'

//埋点数据
const data = const data = {
    'mount': 'componentdidmount 生命周期埋点',
    'click': 'click 事件埋点'
}

/**
 *  埋点事件
 * @param {Object} bd 埋点数据,由内部注入
 * @param {Object} tag 组件上Tag对象,由内部注入
 */
function log(bd, tag) {
    const ld = bd[tag.key]  //获取埋点数据仓库对应值
    ...    //相应处理
}

//数据、事件注入
init(data)(log)

//埋点组件
export default class App extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>
            <TD onClick={() => console.log('点击')} Tag={{ key: 'click', params: 'c', type: 'click' }}>
                点击时输出
            </TD>
        </div>
    }
}

更多示例

说明

1.引入接口:

描述 使用
init 初始换函数,数据注入 init(数据)(方法)
injectData 埋点数据注入(追加) injectData(数据)
injectFunction 埋点方法注入(替换) injectFunction(方法)
TD 埋点组件 <TD Tag={...}>

2.组件参数

描述
type[必填] 埋点方式。
pid[type=='scroll'] 祖先节点id(用于滑动展现容器节点)。
... 扩充参数可在埋点事件中获取。

3.type类型

描述
render render时执行
click click时执行
mount componentDidMount生命周期执行
scroll 上滑/左滑展现时执行
  • 减少对代码的侵入性:不允许任何埋点事件出现在业务代码中。
  • 统一埋点标准:方便团队对于项目的维护,同时便于迅速定位埋点位置。
  • 管理埋点数据:埋点静态数据储存在统一仓库中,随时可以增添。
  • 使用灵活:可以自由注入打埋点的相关事件。
  • 快速应对各类埋点需求:持续集成众多埋点方式,解决各类需求,加快项目的开发速度。 根据现有需求,已集成的埋点方式有:react组件生命周期中埋点,click事件中埋点,上滑时展现埋点,左滑动时展现埋点。

如果在我们的react项目中处处充斥着埋点事件,或许我们要因为埋点而将无状态组件提升为状态组件、在没有click事件的Dom上绑定onClick,或许我们也要为埋点写出更多的页面交互。那么我们不妨做出更多的尝试,将问题汇总,使其可以统一解决。

核心:

该方案的核心由三部分组成,首先是建立数据存储,其次是自定义方法注入,最后是组件上参数挂载。

在需求开发中,埋点数据可以分为两类。第一类是静态数据,第二类是动态数据。静态数据是一开始就确定好,不会存在修改或追加的可能,而动态数据是在静态数据的基础上依赖于ajax请求或者页面相应节点参数的绑定。

建立数据存储是对静态数据的统一管理,而动态的数据则是在组件上注入,二者的处理方法由注入事件决定。在整个组件中,必要的参数只有type,他决定了我们埋点的输出环境,而当其为滑动展现埋点时,pid也成选填项,其为展现时相对的祖先节点id,具体使用请参考GitHub文档。以下为整个方案的主要流程:
集成:
该项目也是我对前端项目工程化的一次完整尝试,在github上我们能够看到以下的这些徽章。


image

其中包含了项目持续化集成过程中的构建结果以及代码测试覆盖率,涉及到的具体技术内容有:

  • 持续化集成:travis-ci,
  • 测试框架:mocha,
  • 断言库:assert(Node原生),
  • 测试覆盖率:istanbul、coveralls

最后:
目前项目并没有应用在太多的实战中,代码本身和功能上都还需要不断的完善。我会在日后开发中涉及到的埋点的地方全部使用该方案,从而完善更多的功能,并在之前的基础上使项目更加工程化和自动化。

再安利一个58内部媒介上传平台,现已支持图片、视频上传功能。地址:http://hrgnode.58.com/zcm/mediaupload
Github地址:https://github.com/ZBcoder/react-tag-component

参考:一个不优雅的React埋点方案

2. trackpoint-tools

埋点逻辑往往是侵入性的,我们需要将这块代码拆分出去。 幸运的是es6,es7 给我们提供了可能。

npm i trackpoint-tools --save

使用trackpoint-tools你可能会用下面的方式写埋点信息, 完全不侵入原有逻辑

class SomeComponent {
  @track(composeWith(ms => (element) => ajax.post(url, {ms, name: element.name}), time))
  onClick (element) {
    return element.someMethod()
  }
}

示例(React 全): https://codesandbox.io/s/wqxr0j2qj5 示例(Vue 演示):https://codesandbox.io/s/oxxw580yz5

API 列表

所有的API都满足curryable, 所有的trackFn 都不会影响正常逻辑执行。

trackFn 指实际执行逻辑的跟踪函数, fn为普通的业务函数。

before(trackFn, fn)

import { before } from 'trackpoint-tools'

class SomeComponent {
    onClick = before((name) => console.log('seed some ', name))((name) => {
       // normal
       console.log('normal click ', name)
    })
}

onClick('me')

->

  seed some me
  normal click me

after(trackFn, fn)

import { after } from 'trackpoint-tools'

class SomeComponent {
  onClick = after(() => console.log('send after'))(() => {
    // normal
    console.log('normal click')
  })
}

onClick

->

    normal click
    send after

Using Promise

import { after } from 'trackpoint-tools'

class SomeComponent {
    onClick = after(() => console.log('send after'))(() => {
         return ajax.post(...).then(() => {
             console.log('normal click')
         })
    })
}

onClick

->

    normal click
    send after

once(fn)

same as lodash/once lodash/once

track(fn)

借助es7的decorator提案可以让我们以一种非常优雅的方式使用高阶函数, track用来将普通的class函数包装成decorator 使用起来非常简单

babel plugin: https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy

class SomeComponent {
  @track(before(() => console.log('before')))
  onClick () {
    console.log('click')
  }

  @track(after(() => console.log('after')))
  onClickAjax () {
    return ajax.get(...').then(() => {
        console.log('request done')
    })
  }
}

->

 before
 click

->

 request done
 after

nop()

do nothing , empty function

composeWith(convergeFn, [ops])

composeWith 类似after, 主要执行收集执行期间性能的操作, 并将参数传给普通trackFn更高一阶函数

ops会被展开为 fn -> (...args) -> {}, 执行顺序为从右到左,如果只有一项操作 可省略数组直接传入ops函数

class SomeComponent {
  @track(composeWith(m => (...args) => console.log(m + 'ms'), [time]))
  onClick () {
     ...
     ...
     return 0
  }
}

->

 somecomponent.onClick() // return 0 . output 100ms

evolve(evols)

evols是一个求值对象,value为实际求值操作(例如time, identity). 与composeWith结合使用.

注意,evolve中每个操作都有可能跟踪fn,但是fn只能执行一次,所以只有fn第一次执行才能进行有效的性能计算。 所以需要将性能计算写在evols的第一行(但其实顺序并不能保障 ref)。

例如

const evols = {
  timeMs: trackpoint.time,
  value: trackpoint.identity
}

const trackFn = ({timeMs, value}) => (...args) => {
  console.log('timeMs ', timeMs)
  console.log('value ', value)
}

const evolve = trackpoint,evolve

class SomeComponent {
  @track(composeWith(trackFn, evolve(evols)))
  onClick() {
    // some sync operation, about 300ms
    return 101
  }
}

output->

timeMs 301
value 101

time(fn) -> (...) -> ms

测量普通函数与thenable函数执行时间, 单位毫秒

 time(() => console.log('out'))() // return 1

identity(fn) -> (...) -> value

输出fn的执行结果

createCounter() -> (fn) -> (...) -> value

创建一个计数器,可以用来统计fn函数被调用的次数

const trackFn = ({count}) => (...args) => console.log('count is:', count)
const fn = () => { console.log('why always click me?')}

const composeFn = composeWith(trackFn, evolve({count: createCounter()}))(fn)

composeFn()
// why always click me?
// count is 1
composeFn()
// why always click me?
// count is 2
...
...

关于 this

使用

class SomeComponent {
  @track(before(function () {
  }))
  onClick () {
  }
}

会自动将this绑定到before的函数体中。

注意: JS中此处如果有箭头函数会绑定到全局的this(null), 所以在此处不建议使用箭头函数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • 本文将开始详细分析如何搭建一个React应用架构。 一. 前言 现在已经有很多脚手架工具,如create-reac...
    字节跳动技术团队阅读 4,263评论 1 23
  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,406评论 1 33
  • 独自外出的想法由来已久,自高考提前就有了十几天的假期,和同事约定自由行,在慈溪论坛上选别人现成的攻略,最终由于这样...
    一路向南qm阅读 170评论 0 0
  • 今天是年休假后第一天上班,因为休假期间我和孩子爸爸全身心地陪着她,所以临走,孩子哭的撕心裂肺~从来没见过的场面...
    歆然麻麻阅读 242评论 0 0