trackEventMixins.js
8.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/* eslint-disable */
/*
* @ description: 埋点上报系统,包含曝光
* @ author: huzhiming
* @ date: 2019-12-09 15:00:27
* @ version: v1.0.0
* 调用示例:
1: 引入相关文件
2-1: 页面曝光配置步骤
2-2: 普通按钮坑位点击
2-3: 列表坑位点击+曝光
====== 1: 引入相关文件及对象 ======
import trackEventMixins from '@/pages/trackEventMixins'
mixins: [trackEventMixins],
data() {
return {
yasList: [...Array(20).keys()].map((item, index) => ({
id: index
}))
}
},
methods: {
buy() {
console.log('我来自于@click点击,在此处负责执行相关业务逻辑代码!!');
},
// 注意:定义同名函数覆盖重写 曝光默认行为
// dispatchExposure(data) {
// console.log(data);
// }
},
====== 2-1: 页面曝光配置步骤 ======
computed: {
// 页面曝光数据配置,名称不可修改 activated钩子触发
pageExposure() {
return {
params: {
appop: '页面曝光上报Api',
param: {
name: `我是页面曝光传参`
}
}
}
},
}
====== 2-2: 普通按钮坑位点击 ======
<div v-trackClick="{ props: 'YAS_BUY_CLICK_DATA' }" @click="buy">购买</div>
computed: {
// 普通按钮坑位点击,数据配置 【名称可自定义,与对应指令 props 保持一致即可】
YAS_BUY_CLICK_DATA() {
return {
params: {
appop: 'yasBuyParamsApi-test',
param: {
name: '我是对象传参测试'
}
}
}
},
}
====== 2-3: 列表坑位点击+曝光 ======
<div
ref="exposureRef"
v-trackExposure="{ props: 'YAS_ITEM_EXPOSURE_DATA', index }"
v-trackClick="{ props: 'YAS_ITEM_CLICK_DATA', index }"
v-for="(item, index) in [1,2,3,4,5]" :key="index"
>{{item}}</index>
其中:
ref="exposureRef" 固定标识 不可修改
两个指令中的 props对应vue组件实例内计算属性 index 坑位位置(必须)
computed: {
// 列表坑位曝光,数据配置 【名称可自定义,与对应指令 props 保持一致即可】
YAS_ITEM_EXPOSURE_DATA() {
return this.yasList.map((el,index)=>({
P_NAME: '',
TYPE_ID: '',
TAB_ID: '',
TAB_NAME: '',
P_PARAM: '',
I_INDEX: index,
PRD_ID: 12233,
PRD_TYPE: '曝光列表项目' + index
}))
},
// 列表坑位点击,数据配置 【名称可自定义,与对应指令 props 保持一致即可】
YAS_ITEM_CLICK_DATA() {
return this.yasList.map((el,index)=>{
return {
params: {
appop: 'YAS_ITEM_CLICK_DATAApi-test',
param: {
name: '点击了列表项目' + index
}
}
}
});
}
}
*/
import { debounce, throttle, get } from 'lodash';
let currentList = []; // 定义曝光记录,防止重复曝光
let offsetTop = 0; // 距离视窗顶部距离
let vm = null; // 供指令内使用
export default {
data () {
return {};
},
created () {
vm = this; // 指令依赖vue实例
},
mounted () {},
activated () {
if (this.$refs.exposureRef) {
this.$refs.exposureRef.forEach((element, index) => {
element.dataset.trackid = `t_${index}`
element.dataset.index = index
});
this.$nextTick(() => {
window.addEventListener('scroll', debounce(throttle(this.handleScroll, 500), 500));
this.handleScroll(); // 首屏触发 曝光上报
});
}
/* 如果实例计算属性有此对象,则进行页面曝光上报 */
if (this.pageExposure && Object.keys(this.pageExposure).length > 0) {
this.$store.dispatch('reportYas', this.pageExposure);
}
},
deactivated () {
window.removeEventListener('scroll', null);
},
// beforeRouteEnter(to, from, next) {},
// beforeRouteUpdate(to, from, next) {},
// beforeRouteLeave(to, from, next) {},
destroyed () {
window.removeEventListener('scroll', null);
currentList = [], vm = null, offsetTop = null;
},
methods: {
handleScroll () {
let prevTop = offsetTop;
let direction = 'up';
let oldList = currentList.map((el) => ({
id: el.id,
visible: el.visible,
index: el.index
}));
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
let exposureRef = this.$refs.exposureRef
currentList = exposureRef.filter((el, index) => {
const Rect = el.getBoundingClientRect();
const top = el.getBoundingClientRect().top;
const elHeight = Rect.height;
const isInviewport = (top > 0 || top + elHeight > 0) ? top <= viewPortHeight : false; // 垂直方向 是否在视窗内
if (index === 0) {
offsetTop = top;
}
el.dataset.visible = isInviewport;
// console.log(`窗口宽度:${viewPortHeight},元素距离可视基准线top值:${top},是否在可视区域内:${top + elHeight < 0 ? false : top <= viewPortHeight}`);
return isInviewport;
});
currentList = currentList.map(el => ({
id: el.dataset.trackid,
visible: el.dataset.visible,
index: Number(el.dataset.index),
exposure: el.YAS_EXPOSURE_DATA // 注意:数据来源于 指令绑定 el.exposure = xxx
}));
// console.log('%c上次值:\n', 'color:#f00;', JSON.stringify(oldList), 'length', oldList.length);
// console.log('%c当前值:\n', 'color:#f00;', JSON.stringify(currentList), 'length', currentList.length);
// 计算滚动方向
if (prevTop!=0) {
if (prevTop >= offsetTop) {
direction = 'up'
} else {
direction = 'down'
}
}
// console.log('滚动方向:', direction);
// 防止重复上报逻辑
let newAddItemList = [];
let _tmp = currentList;
if (direction == 'up') {
// 保留结尾
let endIndex = 0;
if (oldList.length>0) {
endIndex = _tmp.findIndex(el=>{
return el.id === oldList[oldList.length - 1].id
})
endIndex+=1
}
newAddItemList = _tmp.slice(endIndex);
} else {
// 保留开头
let endIndex = 0;
if (oldList.length > 0) {
endIndex = _tmp.findIndex(el => {
return el.id == oldList[0].id
})
}
newAddItemList = _tmp.slice(0, endIndex)
}
// console.log('%c新加入元素节点:\n', 'color:#f00;', JSON.stringify(newAddItemList), currentList.length == oldList.length);
let reportList = newAddItemList.filter(el => el.visible == 'true'); // 筛选出可见的元素组成新集合,需要上报的数据集合
console.log('%c筛选出可见的元素组成新集合,进行曝光事件上报,需要上报的数据元素为:\n', 'color:#f00;', JSON.stringify(reportList))
if (reportList.length > 0) {
reportList = reportList.map(el => el.exposure)
this.dispatchExposure(reportList);
}
},
// 触发曝光事件 数据上报, 可组件内定义 同名函数覆盖
dispatchExposure (reportList) {
this.$store.dispatch('reportYas', {
params: {
param: { DATA: reportList },
appop: 'XY_UFO_SHOW_EVENT'
}
});
},
// 触发点击事件 数据上报
dispatchClick (target) {
if (target.YAS_CLICK_DATA) {
console.log('我只负责点击事件上报:', JSON.stringify(target.YAS_CLICK_DATA));
// this.$store.dispatch('reportYas', this[target.YAS_CLICK_DATA]);
}
}
},
computed: {},
watch: {},
components: {},
directives: {
// 坑位点击 上报数据绑定
trackClick: {
bind(el, binding) {
const { value } = binding;
if (value.props && value.props != '' && typeof value.index !== 'number') {
el.YAS_CLICK_DATA = vm[value.props]
} else {
el.YAS_CLICK_DATA = vm[value.props][value.index]
}
// 加入防抖和节流
el.addEventListener('click', debounce(throttle(vm.dispatchClick.bind(vm, el), 500), 200), false);
},
unbind (el, binding){
el.removeEventListener('click', null);
}
},
// 坑位曝光事件 上报数据绑定
trackExposure: {
bind (el, binding) {
const { value } = binding;
if (value.props && value.props!='') {
el.YAS_EXPOSURE_DATA = vm[value.props][value.index]
}
}
}
}
};