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上我们能够看到以下的这些徽章。
其中包含了项目持续化集成过程中的构建结果以及代码测试覆盖率,涉及到的具体技术内容有:
- 持续化集成:travis-ci,
- 测试框架:mocha,
- 断言库:assert(Node原生),
- 测试覆盖率:istanbul、coveralls
最后:
目前项目并没有应用在太多的实战中,代码本身和功能上都还需要不断的完善。我会在日后开发中涉及到的埋点的地方全部使用该方案,从而完善更多的功能,并在之前的基础上使项目更加工程化和自动化。
再安利一个58内部媒介上传平台,现已支持图片、视频上传功能。地址:http://hrgnode.58.com/zcm/mediaupload
Github地址:https://github.com/ZBcoder/react-tag-component
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), 所以在此处不建议使用箭头函数