Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
@ecomfe/react-track
Advanced tools
A declarative, component based solution to track page views and user events with react & react-router
基于React的声明式PV及用户行为采集框架。
在NPM上有若干个类似的包,但它们存在着一些缺陷,这其中主流的两类是:
props
来进行数据的采集,与React的数据流形式略有不和。react-redux
的思想,使用connect
和Provider
的形式将功能联系起来。但是这种做法更偏向于命令式,从使用的角度来说繁琐之余也不易追踪。除此之外,这些包均没有提供PV采集的能力。而PV采集中,有一个非常关键的问题至今没有得到很好的解决:
当URL中包含参数时,如
/posts/123
与/posts/456
在PV上会被认为是两个页面,但事实上它们对应的路由均是/posts/:id
,是相同的。
这一问题导致如果需要将包含参数的URL进一步的汇总与分组来更精确地计算“页面”的PV,则会需要额外的数据分析成本。因此我们希望从源头,即在数据采集的时候就解决这一问题,这也导致需要与react-router
进行关联。
我们的目标是:
react-router
的配置。类似于redux
或react-router
,react-track
需要一个全局的环境来定义数据的采集过程和数据的记录形式,这些环境由Tracker
组件来定义。Tracker
组件需要以下2个属性:
{Function} collect
:定义如何采集和组装需要记录的数据。{Object} provider
:定义如何将数据记录下来或发送至指定服务。并且支持以下可选配置:
{boolean} reportPageViewOnLeafOnly
:仅让作为叶子节点的TrackRoute
组件报告PV,如果一个TrackRoute
在children
属性,则不作为叶子节点处理。该配置默认关闭。{boolean} warnNestedTrackRoute
:当一个TrackRoute
在另一个TrackRoute
里面时,在控制台打印一个警告信息,用于检查一些不符合预期的路由嵌套。该配置默认打开。以上2个属性也可以直接用于TrackRoute
组件上,以覆盖Tracker
组件配置的默认值。
在Tracker
组件中,collect
属性用来定义“采集哪些数据”以及“数据的最终结构”。connect
是一个函数,其签名如下:
type collectPageView = (type: 'pageView', location: Location) => object;
type collectEvent = (type: 'event') => object;
type collect = collectPageView | collectEvent;
react-track
内置了几种常用的采集函数:
browser()
:添加浏览器相关的信息,包括UA、分辨率、操作系统、浏览器版本、系统语言。这个采集仅在类型为pageView
时才会生效。context(env)
:将固定的env
对象放到采集数据中去,常用于添加当前登录用户名、系统名称、系统版本等信息。session(storageKey)
:跟踪一次用户的访问,为每一次访问生成一个唯一的标识,并存放在sessionStorage
中,这个唯一标识会变为名为session
的属性值。可以通过storageKey
来自定义sessionStorage
中对应的键名。basename(prefix)
:默认采集路径信息是基于location.pathname
的,但它并不包含basename。所以,当history设置basename时,需要使用该collect指定basename才能采集准确的路径当希望同时使用多个collect
函数时,可以通过combineCollects
将它们组合成一个函数。以下代码展示了如何使用多个collect
函数,并通过combineCollects
将它们组合成一个:
import {combineCollects, browser, context, session} from '@ecomfe/react-track';
const app = {
name: 'My App',
version: '1.0.0',
branch: 'stable'
};
const collect = combineCollects(
context(app),
browser(),
session()
);
Tracker
组件的provider
属性用于控制采集到的数据如何使用,通常在生产环境我们会选择将其发送到指定的服务,如百度统计、Google Analysis等,在开发环境中则可以忽略或者显示在控制台中。
provider
是一个对象,其定义如下:
type PageViewData = {
location: Location,
referrer: Location,
[key: string]: any
};
type EventData = {
category: string,
action: string,
label: string,
[key: string]: any
};
interface TrackProvider {
install(): void;
uninstall(): void;
trackPageView(data: PageViewData): void;
trackEvent(data: EventData): void;
}
通常使用install
来做初始化的工作,uninstall
进行清理,而trackPageView
和trackEvent
则会在每一次PV或自定义事件数据采集完成后被调用。
react-track
同样内置了几个常用的处理器:
holmes(site)
:封装了百度统计,接受对应的百度统计id。print()
:用于调试,通过控制台打印对应的数据。empty()
:忽略所有的数据。与collect
相似地,一个应用中我们可能会需要同时将数据进行多重处理,如既发送到百度统计,又打印在控制台中,此时可以使用composeProvider
函数进行组装。以下代码自定义了一个provider
用于将数据通过POST发送到指定的服务器,同时将2个处理器组合为一个,最后仅在生产环境才生效,开发环境仅打印在控制台:
import {holmes, print, composeProvider} from '@ecomfe/react-track';
import axios from 'axios';
const post = url => {
const send = type => data => axios.post(url, {type, ...data});
return {
install: noop,
uninstall: noop,
trackPageView: send('pageView'),
trackEvent: send('event')
};
};
const trackProvider = process.env.NODE_ENV === 'production'
? composeProvider(
post('http://127.88.88.88:8888/v1/log'),
holmes(mySiteID)
)
: print();
在有了collect
和provider
的定义后,将Tracker
组件置于应用的最外层即可以完成全部环境的准备:
import {Tracker} from '@ecomfe/react-track';
import {BrowserRouter} from 'react-router';
import {App} from 'components';
<Tracker collect={collect} provider={trackProvider}>
<BrowserRouter>
<App />
</BrowserRouter>
</Tracker>
对于Web应用,PV是数据采集中的必要信息。传统的PV采集会存在一个问题,假设我们有一个展示用户信息的页面,其路由是/users/:username
,那么对于不同的用户,我们将会得到不同的URL,如/users/Alice
和/users/Bob
。在常见的PV采集方案中,采集工具仅对URL作出反应,因此在数据的统计中,我们会看到2个页面分别有n和m的访问量。
但是在对应用系统的分析时,我们更希望得到这样一个信息:用户信息页被访问了多少次。然而问题是,在采集的数据中,要通过/users/Alice
和/users/Bob
去还原用户信息页被问题的问题(n + m)是相对困难的,当路由规则更加复杂时,甚至可能是无法实现的。
为此,react-track
在PV采集上,提供了将/users/:username
这个URL模块也一并捕获的能力,使用react-router
的定义,称之为path
。由于react-router 4.x
的特征,从全局顶层来获取path
是不可能的,因此为了实现这一功能,使用react-track
的系统不得不在代码上做出一些微小的改变。
react-track
要求用户显式地声明需要采集PV信息的路由位置,提供了TrackRoute
这一组件来进行声明。
TrackRoute
的功能与react-router
的Route
组件完全兼容,因此对于一个已经使用了react-router
的系统,只需要在合适的位置将<Route>
修改为<TrackRoute>
即可:
import {Switch, Route} from 'react-router-dom';
import {CommonHeader, AboutTab, Info, Contact} from 'components';
const App = () => (
<div>
<CommonHeader />
<Switch>
<TrackRoute exact path="/console" component={Console} />
<TrackRoute exact path="/service" render={() => <Service />} />
<Route path="/about" component={AboutMe}>
<AboutTab />
<TrackRoute exact path="/about/info" component={Info} />
<TrackRoute exact path="/about/contact" component={Contact} />
</Route>
</Switch>
</div>
);
需要注意的一点是,TrackRoute
就当仅应用在最底层的路由上。如上述代码中的/about
这一级路由并不是最底层的,其下还有/about/info
和/about/contact
,如果将这一层的<Route>
改为<TrackRoute>
的话,当访问/about/info
时,由于上下两个路由都会触发PV采集,最终将会形成2条数据:
{pathname: "/about/info", path: "/about"}
{pathname: "/about/info", path: "/about/info"}
它们的pathname
是一样的,始终指向当前真实的URL,而path
则不同,指向<TrackRoute>
上的path
属性。这会导致数据的重复。
同时react-track
还提供了trackPageView
高阶组件,可以将任意的组件声明为PV采集点。trackRoute
并不会声明路由信息,因此还需要将组件放置在<Route>
下:
import {trackPageView} from 'react-track';
import {Route} from 'react-route';
const Console = () => (
<div>
...
</div>
);
const ConsoleWithTrack = trackPageView(Console);
<Route exact path="/console" component={ConsoleWithTrack} />
通过事件采集可以分析用户的行为,帮助理解用户的真实需求并进行产品的改进。react-track
提供了简单直接的采集方式,允许通过对事件回调类型的属性进行拦截来采集相关数据。
在常见的自定义事件模型中,一个事件由category
、action
和label
三个属性组成。
react-track
提供了TrackEvent
组件,使用它包裹在对应组件的外层,并通过eventPropName
指定需要拦截的事件名称,用category
、action
、label
声明事件的相关信息。除以上4个属性外,其它的属性会透传给其子元素。
子组件需要支持eventPropName
对应的属性,且必须是函数类型。需要支持多个事件类型时可以嵌套使用:
import {TrackEvent} from '@ecomfe/react-track';
import {NavLink} from 'react-router-dom';
const NavItem = ({name, to}) => (
<TrackEvent eventPropName="onMouseEnter" category="navigation" action="mouseEnter" label={name}>
<TrackEvent eventPropName="onMouseLeave" category="navigation" action="mouseLeave" label={name}>
<li>
<NavLink exact to={to}>{name}</NavLink>
</li>
</TrackEvent>
</TrackEvent>
);
const Item = ({name, onClick}) => {
const trackEvent = useTrackEvent();
const handleClick = useCallback(
e => {
trackEvent({category: 'navigation', action: 'click', label: name});
onClick(e)
},
[name]
)
return <Button onClick={handleClick}>Click Me</Button>
};
react-track
同时提供了trackEvent
高阶组件,用于直接在一个现有组件上添加事件采集的能力。在希望采集多个事件时,与recompose
一起配合能取得更好的代码可读性:
import {trackEvent} from '@ecomfe/react-track';
import {NavLink} from 'react-router-dom';
import {compose} from 'recompose';
const NavItem = ({name, to}) => (
<li>
<NavLink exact to={to}>{name}</NavLink>
</li>
);
const track = action => {
const options = {
eventPropName: 'on' + action[0].toUpperCase() + action.slice(1),
category: 'navigation',
action: action,
label: null
};
return trackEvent(options);
};
const enhance = compose(
track('mouseEnter'),
track('mouseLeave')
);
export default enhance(NavLink);
FAQs
A declarative, component based solution to track page views and user events with react & react-router
The npm package @ecomfe/react-track receives a total of 0 weekly downloads. As such, @ecomfe/react-track popularity was classified as not popular.
We found that @ecomfe/react-track demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 11 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.