- 前端
- CMS
【免费下载链接】wp-calypso
The JavaScript and API powered WordPress.com
导读
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 中,关注按钮实际上由三层组件构成,各司其职:
- 纯 UI 按钮层:button.jsx 只负责渲染图标、文案和点击行为,本身不关心任何数据状态;
- 状态容器层:index.tsx(
FollowButtonContainer)连接 Redux 与订阅类 API,负责“是否已关注”的判断和关注/取关的异步请求; - 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 | 类型 | 作用 |
|---|---|---|
location | string | 当前页面的 MC 统计 key,例如following_edit |
isButtonOnly | boolean | 为true时只渲染图标按钮,不渲染文字标签 |
railcar | Railcar(可选) | 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,一次“关注”会依次触发四类埋点:
- MC 统计:
bumpStat( 'reader_follows', source ),source优先取additionalProps.follow_source,缺省时用当前路径推导(getLocation); - Reader 动作统计:
recordAction( 'followed_blog' ),内部执行bumpStat( 'reader_actions', action ); - Google Analytics:
recordGaEvent( 'Clicked Follow Blog', source ); - Tracks 事件:
recordTrack( 'calypso_reader_site_followed', { url, source, ...additionalProps }, { pathnameOverride } ); - 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
相关推荐
CAVA|终端音频可视化,把命令行里的声音画成条形频谱
CAVA|终端音频可视化,把命令行里的声音画成条形频谱 深夜开着终端写代码,耳机里放着音乐,画面却只有黑底光标在闪。你想知道声音的频谱长什么样,却没有现成工具。
音视频桌面应用wp-calypso 中的 ExternalLinkWithTracking:为外链注入可验证的 Tracks 埋点
wp calypso 中的 ExternalLinkWithTracking:为外链注入可验证的 Tracks 埋点 ExternalLinkWithTrack
前端CMSwp-calypso 中的 ButtonGroup 组件:将相关操作按钮组合为一行的方法与实践
wp calypso 中的 ButtonGroup 组件:将相关操作按钮组合为一行的方法与实践 ButtonGroup 是 wp calypso(WordPre
前端CMS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考