☰
wp-calypso Reader 关注按钮(ReaderFollowButton)实战指南:带统计埋点的关注/取消关注组件
2026/9/29 3:08:03 网站建设 项目流程
  • 前端
  • CMS

【免费下载链接】wp-calypso

The JavaScript and API powered WordPress.com

项目地址:https://gitcode.com/gh_mirrors/wp/wp-calypso
点击查看免费下载

导读

Reader 是 WordPress.com(wp-calypso)中阅读资讯流的核心界面,而“关注/取消关注站点”是其中出现频率最高的交互之一。ReaderFollowButton 是 wp-calypso 在通用关注按钮之上专门为 Reader 场景封装的组件:它不仅负责关注状态的展示与切换,还在每一次关注行为发生时自动上报 MC 统计、Google Analytics 与 Tracks 事件,并支持 Train Tracks 推荐归因(Railcar)。阅读本文后,你将掌握该组件的完整 Props 用法、统计上报链路、底层关注 API 调用逻辑,以及如何在 Reader 各页面中正确接入它。

组件定位:三层结构中的“统计专用层”

client/reader/follow-button/README.md对它的定位描述得非常简洁:它是通用components/follow-button的一个专化版本(specialization),唯一新增的职责就是发送统计(sends stats)。在 wp-calypso 中,关注按钮实际上由三层组件构成,各司其职:

  1. 纯 UI 按钮层:button.jsx 只负责渲染图标、文案和点击行为,本身不关心任何数据状态;
  2. 状态容器层:index.tsx(FollowButtonContainer)连接 Redux 与订阅类 API,负责“是否已关注”的判断和关注/取关的异步请求;
  3. Reader 统计包装层:ReaderFollowButton 在容器之上再包一层,把关注切换事件转译为统计调用。

需要说明的是,README 中写的是components/follow-button,而仓库中该通用组件的实际路径是 client/blocks/follow-button(含 README.md),Reader 版本也确实是直接import FollowButtonContainer from 'calypso/blocks/follow-button'。这与 wp-calypso 长期演进中组件目录从components迁移到blocks有关,接入时请以calypso/blocks/follow-button为准。

Props 一览:Reader 专属参数与继承参数

README 为 ReaderFollowButton 定义了三个专属 Props:

Prop类型作用
locationstring当前页面的 MC 统计 key,例如following_edit
isButtonOnlyboolean为true时只渲染图标按钮,不渲染文字标签
railcarRailcar(可选)Train Tracks 的 Railcar 字符串,用于推荐归因

其中location对应 README 写作时代的埋点概念;在当前的源码实现中,这一职责已由followSource/followApiSource承接(见下文“统计链路”)。其余参数全部透传给通用容器,二者是“继承 + 扩展”的关系。

源码中的完整接口

从 index.tsx 可以看到ReaderFollowButtonProps的完整定义:

interface ReaderFollowButtonProps { className?: string; disabled?: boolean; feedId?: number; followApiSource?: string; followSource?: string; followIcon?: JSX.Element; followingIcon?: JSX.Element; hasButtonStyle?: boolean; iconSize?: number; isButtonOnly?: boolean; onFollowToggle?: ( isFollowing: boolean ) => void; railcar?: Railcar; siteId?: number; siteUrl: string; }

从通用容器继承的 Props

结合 FollowButtonContainer 的接口与 button.jsx 的 PropTypes/defaultProps,继承来的参数语义如下:

  • siteUrl(必填):要关注/取关的站点 URL,容器内部会据此判断订阅状态;
  • siteId/feedId:站点的 blog ID 与 feed ID,用于构造关注请求的blog_ID/feed_ID数据(仅当值已定义时才会带上,见 index.tsx 中的omitBy过滤);
  • iconSize:默认20,控制关注/已关注图标的尺寸;
  • tagName:默认'button',渲染使用的 HTML 标签,可传入自定义标签名或组件;
  • disabled:默认false,置为true时点击无效(容器还会在关注/取关请求进行中自动置为禁用);
  • followLabel/followingLabel:自定义“关注/已关注”文案,缺省时分别使用翻译后的Subscribe/Subscribed;
  • followIcon/followingIcon:自定义两种状态图标,缺省时使用 Reader 的 feed 图标;
  • hasButtonStyle:默认false,为true时应用带按钮形状/边框的样式;
  • isButtonOnly:默认false,为true时隐藏文字标签;
  • onFollowToggle:关注状态切换后的回调,接收isFollowing布尔值。

实际用法示例:Reader 各页面如何接入

新博客推荐卡片

client/reader/new-blogs/card.tsx 是典型接入方式,同时传入站点、来源与回调:

<ReaderFollowButton siteId={ siteId } feedId={ feedId } siteUrl={ siteUrl } followSource={ READER_DISCOVER_NEW_BLOGS } className="reader-discover-new-blogs__subscribe" onFollowToggle={ onFollowToggle } />

其中READER_DISCOVER_NEW_BLOGS即 README 所述的埋点 key,值为reader-discover-new-blogs(见 client/reader/new-blogs/README.md)。

对话流与列表页

  • client/reader/conversations/stream.jsx 使用followSource="conversations";
  • client/reader/list/views/sites.tsx 使用followSource="reader-list-sites-tab";
  • client/reader/onboarding-rsm/subscribe-modal/index.tsx 在引导弹窗中分别使用reader-onboarding-modal与reader_subscribe_modal区分不同入口。

这些followSource字符串正是“location/MC 统计 key”概念的现代载体,用来区分用户是在哪个页面、哪个推荐策略下触发关注的。

统计链路:recordFollow / recordUnfollow 源码剖析

入口:只有登录用户才记录

ReaderFollowButton 在recordFollowToggle中先用useSelector( isUserLoggedIn )判断登录态,只有已登录用户才触发统计上报,未登录时仅执行onFollowToggle回调(见 index.tsx):

function recordFollowToggle( isFollowing: boolean ): void { if ( isLoggedIn ) { if ( isFollowing ) { recordFollowTracks( siteUrl, railcar, { follow_source: followSource }, pathnameOverride ); } else { recordUnfollowTracks( siteUrl, railcar, { follow_source: followSource }, pathnameOverride ); } } if ( onFollowToggle ) { onFollowToggle( isFollowing ); } }

注意第三个参数{ follow_source: followSource }会作为 Tracks 事件的附加属性上传,而railcar与pathnameOverride分别用于推荐归因与页面路径还原。

recordFollow 的完整上报序列

统计实现位于 client/reader/stats/index.ts,一次“关注”会依次触发四类埋点:

  1. MC 统计:bumpStat( 'reader_follows', source ),source优先取additionalProps.follow_source,缺省时用当前路径推导(getLocation);
  2. Reader 动作统计:recordAction( 'followed_blog' ),内部执行bumpStat( 'reader_actions', action );
  3. Google Analytics:recordGaEvent( 'Clicked Follow Blog', source );
  4. Tracks 事件:recordTrack( 'calypso_reader_site_followed', { url, source, ...additionalProps }, { pathnameOverride } );
  5. Train Tracks:若传入了railcar,再调用recordTracksRailcarInteract( 'site_followed', railcar )。

recordUnfollow(index.ts)与之镜像对称,对应reader_unfollows、unfollowed_blog、Clicked Unfollow Blog与calypso_reader_site_unfollowed、site_unfollowed。

ui_algo 与 pathnameOverride:还原真实来源路径

Tracks 事件中都会带一个ui_algo属性,由buildReaderTracksEventProps(index.ts)计算:

const location = getLocation( pathnameOverride || window.location.pathname + window.location.search );

为什么需要pathnameOverride?源码注释给出了典型场景:例如“打开文章”(calypso_reader_article_opened)这类事件发生在页面切换之后,此时window.location已变成文章页,若不覆盖路径,ui_algo会被错误地记为single_post而非用户真实来源的信息流。ReaderFollowButton 通过useSelector( getPreviousPath )(来自 client/state/selectors/get-previous-path)获取用户上一路径并作为覆盖值——组件注释明确说明:“We use the previous path to detect how the user arrived on the follow button. It is important to understand our post suggestions strategies.”(用于理解推荐策略的效果)。

getLocation内部维护了一张路由映射表(index.ts),把/tag、/discover各子页、/reader/list/、/reader/a8c、/reader/mastodon等路径归一到稳定的 tracking key 上,例如/discover/recommended→discover_recommended,未知路径统一回退为unknown。

Railcar:Train Tracks 推荐归因

railcar来自@automattic/calypso-analytics包的类型,携带推荐链路所需的标识。在recordFollow中,recordTracksRailcarInteract( 'site_followed', railcar )会转成calypso_traintracks_interact事件,并把 railcar 字段展平进事件属性(见 recordTracksRailcar)。与此配套的还有recordTracksRailcarRender(calypso_traintracks_render),用于记录推荐内容的展示;allowedTracksRailcarEventNames集合(index.ts)则枚举了哪些 Reader 事件允许携带 railcar。

底层行为:容器如何真正执行关注/取关

点击发生后,ReaderFollowButton 会把带统计的recordFollowToggle作为onFollowToggle传给容器,容器在 index.tsx 中处理三种分支:

  • 未登录:调用registerLastActionRequiresLogin记录“follow-site”动作(含siteUrl与feed_ID/blog_ID),用户登录后可恢复该意图;
  • 邮箱未验证:弹出errorNotice(“Your email has not been verified yet.”),附带“Resend Email”按钮触发重发验证邮件(useResendEmailVerification,来源标记为wpcom-reader);
  • 正常路径:调用useFollowSite()/useUnfollowSite()发送订阅请求,source取followApiSource ?? getFollowingSource(),随后回调onFollowToggle。

订阅状态本身由useIsSubscribed( { feedUrl, feedId, blogId } )提供(来自 calypso/reader/data/site-subscriptions),因此容器无需自己维护 Redux 订阅状态。请求进行期间isFollowingPending/isUnfollowingPending会把按钮置为disabled,避免重复提交。

按钮渲染细节与测试

在 button.jsx 中,按钮类名由button follow-button has-icon加上可选的状态类组成:已关注时追加is-following tooltip,禁用时追加is-disabled,hasButtonStyle时追加has-button-style。默认文案跟随following状态在Subscribe/Subscribed之间切换;aria-label对应为Subscribe/Unsubscribe,已关注状态还会附带data-tooltip="Unsubscribe"。isButtonOnly为true时整个<span class="follow-button__label">标签都不会渲染。

仓库为按钮层提供了 Jest 测试:client/blocks/follow-button/test/button.js 验证了自定义标签(如followLabel="Follow Tag")能被正确渲染显示。

小结

ReaderFollowButton 是理解 wp-calypso “业务组件 + 埋点” 模式的绝佳范例:UI 渲染(button.jsx)、状态与 API(blocks/follow-button 容器)、统计上报(reader/stats)三层解耦清晰,任何一层都可独立复用。接入新入口时,只需像card.tsx那样传入siteUrl并设置有区分度的followSource,即可自动获得 MC、GA、Tracks 与 Train Tracks 四套完整的关注行为数据,帮助团队评估推荐策略与各页面的关注转化效果。

  • 前端
  • CMS

【免费下载链接】wp-calypso

The JavaScript and API powered WordPress.com

项目地址:https://gitcode.com/gh_mirrors/wp/wp-calypso
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询