Hippy Exposure SDK 是精心打造的企业级 UI 元素曝光解决方案。基于插件化架构设计,为 Hippy 应用提供全方位、高精度的曝光检测能力。
🔌 无 Bridge 架构 - UI 元素的曝光检测发生在 JS 侧,无 Bridge 调用
📊 接口丰富 - 不仅支持了曝光 API,还提供了各种底层接口和事件,来实现更加个性化的业务能力。
🎯 高精度数据 - 提供详细的、精确的曝光数据,例如支持按照曝光比例来决定是否曝光。
⚡ 性能无感知 - 内部针对 ScrollView 相关的检测,做了大量的算法优化,对性能的影响降到最低。
🔧 TypeScript 支持 - 完整的类型定义,优秀的开发体验。
- Hippy-Vue 版本: 3.3.2+
- TypeScript: 5.2.2+ (可选)
npm install --save @mx-hippy/exposure<div v-hippy-exposure="data">- data 为空,不进行曝光
- data 从空值 --变为--> 有值,会自动触发曝光检查
import exposure, { ExposurePluginFunction } from '@mx-hippy/exposure';
// app 根节点 id: root
const exposurePlugin = exposure.createExposurePlugin('root', {
visibleNotify: (el: HippyElement, data: NeedToType) => {
if (data) {
// data 为对应 el 绑定的曝光数据 (来自 v-hippy-exposure="data")
// 当 el 曝光时,visibleNotify 会自动触发调用
tracker.report(data)
}
}
});
app.use(exposurePlugin);visibleNotify: 任何一个元素检测到曝光 (从不可见状态 -> 可见状态),并且 data 有值,均会触发 visibleNotify 调用,这里可以很方便的对接到曝光埋点函数。进行统一处理。
// hippy 的特性,存在整个页面还不可见,但 hippy 容器已经开始运行的情况
// 页面可见需要业务告知,所以,由业务启动曝光检测
nativeEmitter.on(NATIVE_EVENT_ENUM.PAGE_SHOW, () => {
// pageShow 之后,才会开始触发曝光
exposure.setPageShowStatus(true);
if (exposure.isReady()) {
// 每次 pageShow 事件,重新检测并上报需要曝光的组件,业务可选
exposure.forceExposureForAllElement();
} else {
// hippy 无法通过 Native API 获取较为准确的容器大小,需要由业务精确告知
const size = SCREEN_SIZE;
const containerSize = { width: size.width, height: size.height - TABBAR_HEIGHT };
exposure.start(containerSize);
}
});
// 整体页面不可见
nativeEmitter.on(NATIVE_EVENT_ENUM.PAGE_HIDE, () => {
exposure.setPageShowStatus(false);
});重要: 由于 hippy 工作在 Native 的一个 Page 容器中,所以需要严格对齐 Native 的 page_show 和 page_hide,在 page_show 中设置:exposure.setPageShowStatus(true); 让 SDK 知道是否需要执行曝光逻辑,然后调用 start 启动运行(执行一次即可)。在 page_hide 中设置exposure.setPageShowStatus(false);,当页面不可见时,SDK 会自动停止曝光追踪。
start 需要传入整个 Hippy 外层容器的大小作为参数,建议通过 Bridge 查询客户端得到可靠的 Size 并传入,否则曝光可能不准确。
// 下拉刷新,触发强制重新曝光检查
emitter.on(COMMON_EVENTS_ENUM.pageRefresh, triggerType => {
if (triggerType === EFreshTriggerType.pullDown) {
exposure.forceExposureForAllElement();
}
});如果业务需要在下拉刷新等场景,强制执行一次曝光,可以通过调用: exposure.forceExposureForAllElement() 来实现,SDK 内部会自动把当前在屏幕内的需要曝光的 UI 元素全部执行一遍 visibleNotify,触发曝光埋点数据上报。
1.2 中在创建插件的时,createExposurePlugin 支持 options 参数配置,createExposurePlugin 定义如下:
createExposurePlugin: (rootId: string, options?: ExposurePluginParam) => ExposurePluginFunction;其中 rootId 为 dom 根节点的 id。options 为配置参数:
export interface ExposurePluginParam {
// 因为滑动(或者重新布局)引发元素再次可见后,是否重复通知曝光,插件默认给 true
reNotifyWhenReVisible?: boolean;
exposureRatioThreshold?: number;
isDebug?: boolean;
visibleNotify?: ExposureVisibleCallback;
invisibleNotify?: ExposureVisibleCallback;
}reNotifyWhenReVisible?: 一个已经曝光过的元素,又一次从不可见变为可见,是否需要通知曝光。默认: trueexposureRatioThreshold?: 曝光比例门限值,表示 UI 元素漏出多少的面积比例后进行曝光,取值范围 [0.0, 1.0]。默认: 0.001,即少量的漏出,就会触发曝光。isDebug?: 用于调试。默认: falsevisibleNotify?:曝光回调函数,这里可以统一处理曝光埋点上报逻辑。invisibleNotify?:不可见回调函数
普通使用:
// app 根节点 id: root
const exposurePlugin = exposure.createExposurePlugin('root', {
visibleNotify: (el: HippyElement, data: NeedToType) => {
if (data) {
// data 为对应 el 绑定的曝光数据 (来自 v-hippy-exposure="data")
// 当 el 曝光时,visibleNotify 会自动触发调用
tracker.report(data)
}
}
});
app.use(exposurePlugin);设置曝光比例:
// app 根节点 id: root
const exposurePlugin = exposure.createExposurePlugin('root', {
exposureRatioThreshold: 0.2, // 漏出 20% 的时候,进行曝光,即回调 visibleNotify
visibleNotify: (el: HippyElement, data: NeedToType) => {
if (data) {
// data 为对应 el 绑定的曝光数据 (来自 v-hippy-exposure="data")
// 当 el 曝光时,visibleNotify 会自动触发调用
tracker.report(data)
}
}
});
app.use(exposurePlugin);// 普通用法
<div :collapsable="false" v-hippy-exposure="data"></div>
// 高级用法
<div :collapsable="false" v-hippy-exposure:[dataEnable]="data"></div>
// 1 使用 boolean 控制 enable
const dataEnable = ref(false);
// 2 使用 对象控制
const dataEnable = ref({
enable: false
});用于曝光的自定义指令所在的标签,建议使用:collapsable="false",以防止被 hippy 优化掉。
1:支持 boolean 控制曝光开关
v-hippy-exposure:[enable]="data"
// enable 为 boolean 类型2:支持对象类型,其中包含 enable,方便以后扩展
v-hippy-exposure:[obj]="data"
obj: { enable: boolean, ... }其他情况,都不解析,直接返回 true,即默认上报数据。enable 和 data 任何一个发生变化,都会自动触发重新的曝光检查,如果可见且没有曝光过(isExposured 内部标记控制),主动曝光一次。
被动的可见通知,必须满足以下 4 点:
- 元素从不可见状态 --转变为--> 可见状态 (sdk 内部算法自动检测)
- 自定义指令 dataEnable 检查
- 未设置 dataEnable ,默认值为 true,即允许曝光
- 设置了 dataEnable ,则检查 dataEnable 必须使用 boolean 类型,或者对象类型且包含 enable boolean 值。其他情况,不检测,直接放行,即 enable 为 true。
- 需要注意:dataEnable 从 false 变为 true,会强制检测曝光,不考虑上次是否曝光过
<div v-hippy-exposure:[dataEnable]="data"></div>
// 1 使用 boolean 控制 enable
const dataEnable = ref(false);
// 2 使用 对象控制,内部必须包含 enable 属性
const dataEnable = ref({
enable: false
// 其他值后续扩展
});
// 3 其他情况均不解析,直接放行,例如
<div v-hippy-exposure:enable="data"></div>
// sdk 不解析,直接认为是 true,继续进行曝光- data 数据必须有值:
- data 为空,不进行曝光
- data 从空值 --变为--> 有值,会主动触发曝光检查
- SDK 内部会自动进行 isExposured 检查:检查之前是否曝光过,用来防止重复的无效曝光。
- 例如 scrollView 滑动中,sdk 需要持续检查页面元素,有些元素会持续处于可见状态,此时通过 isExposured 避免重复的无效曝光通知。
总结:从不可见状态 --转变为--> 可见状态 (内部会记录状态变化),目前支持
- 元素被滑动出屏幕后,又重新进入页面
- 元素重新布局 或者 其他元素布局后,当前元素被迫挤出屏幕,然后下一次出现在屏幕
export interface ExposureInstance {
createExposurePlugin: (rootId: string, options?: ExposurePluginParam) => ExposurePluginFunction;
setPageShowStatus: (isShow: boolean) => void;
start: (rootSize: Size) => void;
isReady: () => boolean;
setInvalid: (el: HippyElement, needDeepTraverse?: boolean) => void;
trigger: (el: HippyElement, needDeepTraverse?: boolean) => void;
forceExposureForAllElement: (checkEnable?: boolean) => void;
forceExposureForElement: (elementId: number, checkEnable: boolean) => void;
registCustomScrollComponents: (tags: string[]) => void;
queryElementVisible: (nodeId: number) => boolean;
}| 函数 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| createExposurePlugin | 创建 vue 插件 | 1. rootId:传入 app 自己根 div 的 id2. options:ExposurePluginParam 配置参数 |
ExposurePluginFunction |
| setPageShowStatus | 设置页面可见状态,否则 SDK 会认为整个页面不可见,不会触发曝光回调。 - page_show:设置为 true- page_hide:设置为 false |
status: boolean |
void |
| isReady | 判断 SDK 是否准备好 | 无 | boolean |
| setInvalid | 设置某个已经曝光过的节点失效,SDK 内部会把该节点标记为未曝光过,然后通过 trigger 立即触发重新曝光,或者等待下次 SDK 自动检测来曝光。 | 1. el: HippyElement:UI 元素节点,可通过 ref 获得。2. needDeepTraverse:是否深度遍历,默认 false,设为 true 会对当前节点的所有子节点执行相同的失效逻辑。 |
void |
| trigger | 用于主动触发曝光检测 | 1. el: HippyElement:UI 元素节点,可通过 ref 获得。2. needDeepTraverse:是否深度遍历,默认 false,设为 true 会对当前节点的所有子节点执行相同的失效逻辑。 |
void |
| forceExposureForAllElement | 强制触发所有需要曝光的元素重新进行曝光检查,符合曝光条件的都会重新调用 visibleNotify 通知业务。 |
checkEnable:是否需要检查指令开关,默认值为 true。 |
void |
| forceExposureForElement | 指定特定元素进行强制重曝光 | 1. el: HippyElement:元素 ID2. checkEnable: boolean:是否需要检查指令开关,默认值为 true。 |
void |
| registCustomScrollComponents | 暂时空实现,未来可注册自定义滑动组件的标签 | 暂无 | 暂无 |
| queryElementVisible | 主动检查某个元素是否可见,不会触发曝光通知,仅检查可见性。 | el: HippyElement |
boolean |
SDK 除了提供便捷的接入 API 外,额外提供 onHippyVisible \ onHippyInvisible 运行开发者自行监听某个元素的可见性,来实现个性化的曝光逻辑,或者其他能力。
SDK 提供的 API 层,也是基于 onHippyVisible \ onHippyInvisible 做的封装。
<div @hippyVisible="onVisible" @hippyInvisible="onInvisible"></div>
// 元素从不可见 变为 可见 (出现在屏幕范围内)
const onVisible = (e) => {
// ...code
}
// 元素从可见 变为 不可见
const onInvisible = (e) => {
// ...code
}其中的回调参数 e:
e: {
el: HippyElement,
ratioInClipped: number, // 元素和裁剪区域的相交面积比例 (例如 scrollView)
ratioInWindow: number, // 元素和 window 的相交面积比例
}注:如果同时实现了 v-hippy-exposure 自定义指令 和 onHippyVisible,会同时生效。
Swiper 为 Hippy 提供的翻页滚动组件,该组件较为特殊,需要单独走一套曝光接入流程:
<swiper
@swiperVisibleChanged="onSwiperVisibleChanged"
@swiperInvisibleChanged="onSwiperInvisibleChanged"
>
<swiper-slide
class="swiper-slide-item"
v-for="(item, index) in data"
:key="item[props.uniqueKey]"
>
<div></div>
</swiper-slide>
</swiper>SDK 为 swiper 单独提供了:
- onSwiperVisibleChanged 回调事件:swiper内部元素可见性发生变化
- swiperInvisibleChanged 回调事件:swiper内部元素不可见性发生变化
示例代码:
// hippy-exposure sdk 发出的可见性通知
const onSwiperVisibleChanged = (e: SwiperExposureEvent) => {
// 根据索引获取数据
const exposuredData = data[e.index];
tracker.track(exposuredData);
}
// hippy-exposure sdk 发出的不可见性通知
const onSwiperInvisibleChanged = (e: SwiperExposureEvent) => {
// your code
}回调事件:SwiperExposureEvent
export interface SwiperExposureEvent {
$swiperSlide: HippyElement; // 具体的(不)可见的 swiper-slide 元素
isVisible: boolean; // 是否可见
index: number; // swiper-slide 的索引
}我们欢迎社区贡献!请参考以下指南:
- 🐛 fix: 修复 bug
- ✨ feat: 新增功能
- 📝 docs: 文档更新
- 🎨 style: 代码格式调整
- ♻️ refactor: 代码重构
- ⚡ perf: 性能优化
- ✅ test: 测试相关
- 🔧 chore: 构建工具或依赖更新
- 📧 xiaobo.cao
- 🔗 主要贡献:项目架构设计、核心功能开发
本项目采用 MIT 许可证。
- 🌐 Hippy 官网 - Hippy 跨端开发框架
