乐趣区

小蝌蚪传记200行代码实现前端无痕埋点

灰色的天

妳的脸

说分手的语气斩钉截铁

小蝌蚪:“能不走吗”

女神:“不能”

小蝌蚪:“那个男人有什么好”

女神:“他说话好听,长得帅,还有钱”

小蝌蚪:“我没房没车没存款,但我有一颗爱妳的心”

高富帅出现:“我有房有车有存款,我也有一颗爱她的心”

小蝌蚪:“我能跑十公里去为她买宵夜”

高富帅:“我开兰博基尼去为她买宵夜”

小蝌蚪:“我一分钟能敲 5000 行代码”

高富帅:“你们公司的老板,是我爸”

小蝌蚪:“这。。。”

在金钱力量面前,一切言语都显得那么苍白无力

小蝌蚪跪在地上,望着高富帅远去的尾灯,消失在地平线

失恋第三十天,小蝌蚪上山拜佛

小蝌蚪:“伟大的佛,为何我感情如此失败”

佛曰:“因为你不够渣,一次只爱一个人,下次同时爱一百个试试?”

小蝌蚪若有所悟

小蝌蚪:“伟大的佛,那我如何才能成为江湖第一的渣男”

佛说:“想要成为顶级渣男,你要闯过三关”

佛:“第一关我们称之为 < 富婆 >”

佛:“一位顶级渣男,他需要有雄厚的资金来源,才能浪迹天涯,而富婆是这资金流的关键”

第一关 < 富婆 >

得到佛主的指点,小蝌蚪来到国际大酒店

楼顶正举行富婆八十大寿生日宴

富婆坐在轮椅上,望着舞池里的妹纸

满眼都是自己十八岁的样子

小蝌蚪出现:“女士,您好,我叫小蝌蚪”

富婆:“有何贵干”

小蝌蚪:“我想要得到妳的包养”

富婆的假牙差点从嘴里喷了出来

富婆:“我的包养,不是你想要,想要就能要”

小蝌蚪就地表演了一段舌头碎大石

富婆压制住内心的狂喜,说道:“这还不够,还差了点”

小蝌蚪:“我一分钟能敲 5000 行代码,手速奇快”

富婆大惊:“你就是我唯一的真爱~!”

第二关 < 渣女 >

成功拿下富婆后,来到第二关

佛曰:“你需要撩到一个名叫 ‘ 夜魔 ’ 的顶级渣女,通过她身上的《绝世婊技》,你才能悟出传说中的《渣男心经》”

夜魔常年混迹于夜店

斡旋在多名富二代之间

夜魔的座右铭:“肾走多了,才明白走心的可贵”

“渣男收割机”、“夜店王中王”、“叱咤级渣皇”

都是她曾用的小名

一位被她玩死的富二代

去世前曾留下遗言:

别爱上她,相信我

你只是寂寞的晚上

她想要缠绵的

小玩具

夜魔位列年度渣女榜榜首

小蝌蚪的任务,猎杀夜魔

凌晨一点,夜店

小蝌蚪:“小姐姐,您好,我叫小蝌蚪”

夜魔爱搭不理

鲁迅说过:“在金钱面前,一切渣女都是纸老虎”

小蝌蚪故意不经意间露出兰博基尼车钥匙(富婆八十大寿赠)

夜魔大喜:“哥哥,请坐!”

夜魔:“哥哥想喝点什么”

小蝌蚪:“想喝点妳的酒窝”

面对搭讪,夜魔故作脸红,假装羞涩

让你觉得她是一个清纯走心的小姐姐

夜魔:“我看哥哥挺有钱,哥哥职业是什么”

小蝌蚪:“我的职业是职业渣男”

一个低端富二代都是炫耀自己多有钱,爸爸多厉害

但一个顶级富二代,从来都是说自己是渣男

夜魔:“哥哥有喜欢的人吗”

小蝌蚪:“有,她在别人的床上”

小蝌蚪开始打感情牌,宣扬自己受过情伤

唤母爱,博同情

夜魔:“哥哥来找我什么事?”

小蝌蚪:“想借用妳的美色和媚术,帮我攻破一个男人”

夜魔:“谁?”

小蝌蚪:“我”

这是一个调情套路

面对渣女,你要表现的比她更渣

妳渣任妳渣,反正都没我渣

小蝌蚪不再周旋,直接强攻

小蝌蚪:“明人不说暗话,我想和妳结婚”

夜魔是远近闻名的渣女,男人们只想和她暧昧

面对突如其来的‘结婚’,开始手无足措

小蝌蚪抓住时机,放大招

谈笑间侧露价值 180 万的金表(富婆八十大寿赠)

夜魔大惊

菊花一紧,虎躯一震

无数道圣光冲击她的天灵盖

夜魔热泪盈眶

满意的点了点头

酒后三巡,意乱情迷之际

小蝌蚪带她去找了妈妈

深藏功与名

第三关:报复

最后一关

佛曰:“第三关,报仇。报复当初甩掉你的女神和高富帅”

“好的”,蝌蚪微笑,召唤出了夜魔

晚八点,高档餐厅

女神和高富帅在激情派对上亲亲我我

夜魔出现,上台拿起话筒:

“台下的小哥哥,请放下女朋友的手,你们被我包围了”

现场一片哗然

夜魔:“不是我针对谁,论美色,在座的各位,都是垃圾”

所有人被夜魔的顶配神颜惊呆

夜魔:“我会随机抽一个男人,明天和我一起起床”

鲁迅说过:“我想和妳睡觉,是耍流氓。我想和妳起床,是徐志摩”

男人们像疯狗一样欢呼和跪舔

夜魔锁定目标

径直走向高富帅:“小哥哥,你长得好像我下一任男朋友”

高富帅惊慌失措:“我我我。。。已经。。。”

夜魔强撩:“谈恋爱吗?二缺一”

高富帅捂住心脏:“糟糕,是心动的感觉”

一旁的女神暴怒:“我 xx 妳个 xx,勾引我男人”

夜魔一副柔弱装纯的样子:“我只是把他当哥哥~”

女神继续:“我 xx 妳个 xx”

夜魔无辜的看着高富帅:“都怪我,害你女朋友生气了”

高富帅沦陷:“不要理会那八婆”

女神:“我 xx 妳个 xx”

夜魔:“她好凶,我好怕”

高富帅:“不要怕,我的小心心,紫薯于妳”

鲁迅说过:“渣女装纯,天下无敌”

高富帅沦为了夜魔的裆下亡魂

女神跪下,掩面痛哭

这一切,都是小蝌蚪的精心策划

佛主出现:“恭喜小蝌蚪,你成为了一位顶级渣男”

小蝌蚪:“佛心四大皆空,贫僧尘念已结”

佛曰:“我现赐予你法号——渣佛”

佛曰:“希望你今后,随老衲去夜店降妖除魔,还人间一片净土”

小蝌蚪:“哦咪陀佛”

小蝌蚪终于成为了江湖第一的渣男

手段虽然残忍

但我们不要怪渣男渣

因为每个渣男背后,都有一段刻骨铭心的虐恋

每一位渣男,都曾是折翼的天使

甩掉女神那天晚上

小蝌蚪的肩膀上靠着富婆

车里循环了一首歌:

i lost myself again

我又一次迷失了自己

but i still remember you

脑海中的妳依然那么深刻

don’t come back

别回头看我,那些伤还未愈合

our love is six feet under

我们的爱已深埋殆尽

i can’t help but wonder

不能自己的我很想知道

if our grave was watered by the rain

滂沱大雨后,埋葬我们爱的地方

would rose bloom

是否会有玫瑰,悄然绽放

————《six feet under》

作者:第一名的小蝌蚪

微信公众号:前端屌丝

github:https://github.com/airuikun/blog

《蝌蚪传记:200 行代码实现前端无痕埋点》

背景

上次公开演讲结束后,很多小伙伴对无痕埋点很感兴趣

那这次就讲讲前端无痕埋点的原理与实现吧。

鲁迅说过:“一切不放源码的技术文章都是耍流氓”

所以无痕埋点源码:smart-tracker

什么是无痕埋点

简单来说,就是当引入无痕埋点的库以后

用户在浏览器里所有行为和操作都会被自动记录下来

并将信息发送到后端进行统计和分析

传统的埋点形式,都是手动埋点

在指定的元素上绑定事件

将用户行为信息发送到服务端进行统计

假设如果有一万个点需要前端狗去埋,惊喜不惊喜,意外不意外

我们为什么要做无痕埋点

提高工作效率,解放双手

屌丝的双手得到解放以后

就有更多的时间拿双手来取悦自己

嘻嘻

无痕埋点原理

原理很简单,这里只讲 click 的无痕埋点原理

当用户点击了页面上某一个元素

我们要把当前元素到 body 之间整个 dom 的路径记录下来,作为这个元素的唯一标识,我们称之为 domPath

这个 domPath 不仅是这个元素唯一标识

还可以通过 document.querySelector(domPath) 去唯一选择和定位到这个元素

当用户点击一次这个元素,就会将埋点数据上传到服务器

服务器上这个 domPath 对应的统计数据加一

无痕埋点代码实现

    document.body.addEventListener('onclick',  (event) => {const eventFix = getEvent(event);
        if (!eventFix) {return;}
        this._handleEvent(eventFix);
    }, false)

首先在 document 的 body 上监听和绑定全局 click 事件,捕获用户所有的点击事件。

const getDomPath = (element, useClass = false) => {if (!(element instanceof HTMLElement)) {console.warn('input is not a HTML element!');
        return '';
    }
    let domPath = [];
    let elem = element;
    while (elem) {let domDesc = getDomDesc(elem, useClass);
        if (!domDesc) {break;}
        domPath.unshift(domDesc);
        if (querySelector(domPath.join('>')) === element || domDesc.indexOf('body') >= 0) {break;}
        domPath.shift();
        const children = elem.parentNode.children;
        if (children.length > 1) {for (let i = 0; i < children.length; i++) {if (children[i] === elem) {domDesc += `:nth-child(${i + 1})`;
                    break;
                }
            }
        }
        domPath.unshift(domDesc);
        if (querySelector(domPath.join('>')) === element) {break;}
        elem = elem.parentNode;
    }
    return domPath.join('>');
}

这段代码是关键,获取元素唯一标识 domPath

getDomPath 函数传入的是用户点击事件的 target 对象: getDomPath(event.target)。

主要思路是找到当前元素 event.target

然后不断的去循环找它的父节点 parentNode

将父节点的 tagName 当做 domPath 路径上的节点

如果当前元素有 id,那就取消所有路径的循环,直接讲 id 赋值给 domPath

    const children = elem.parentNode.children;
    if (children.length > 1) {for (let i = 0; i < children.length; i++) {if (children[i] === elem) {domDesc += `:nth-child(${i + 1})`;
                break;
            }
        }
    }
    domPath.unshift(domDesc);

getDomPath 函数中的这段代码

意思是在同一级上出现了多个相同 tagName 元素

那我们要定位到这个 event.target 这个元素在这一级里的第几个

假设这个 div 是同一级的第三个,那返回的就是 div:nth-child(3)

这样就可以在 document.querySelector(domPath) 里唯一定位到这个元素

    _handleEvent(event) {const domPath = getDomPath(event.target);
        const rect = getBoundingClientRect(event.target);
        if (rect.width == 0 || rect.height == 0) {return;}
        let t = document.documentElement || document.body.parentNode;
        const scrollX = (t && typeof t.scrollLeft == 'number' ? t : document.body).scrollLeft;
        const scrollY = (t && typeof t.scrollTop == 'number' ? t : document.body).scrollTop;
        const pageX = event.pageX || event.clientX + scrollX;
        const pageY = event.pageY || event.clientY + scrollY;
        const data = {domPath: encodeURIComponent(domPath),
            trackingType: event.type,
            offsetX: ((pageX - rect.left - scrollX) / rect.width).toFixed(6),
            offsetY: ((pageY - rect.top - scrollY) / rect.height).toFixed(6),
        };
        this.send(data);
    }

这段代码就是得到用户点击某个元素的相对位置的横向位置和竖向位置比例

得到这个位置的值,就可以反向从埋点数据中得到用户点击元素的具体位置

因为是个比例值,所以在反向推导中还能自适应页面大小的改变

    send(data = {}) {const image = new Image(1, 1);
        image.onload = function () {image = null;};
        image.src = `/?${stringify(data)}`;
    }

得到了用户点击的位置信息和唯一标识 domPath

就可以将数据发送到服务端进行统计了

用 image 的 src,将数据进行传输

用 image 的 src 有个好处就是轻量,并且还支持跨域

打点基本上都用的这个方法进行发送数据

结尾

两个多月没写文章了,因为在忙着晋升

这次晋升最大的感悟就是,如果你只一心专注业务,是很难晋升成功的

需要在日常工作中做一些技术型需求

而无痕埋点,是一个不错的选择

但是这篇文章仅仅只是无痕埋点的一个简单实现

对整个无痕埋点体系来说,这些只是冰山一角

真正的无痕埋点,还需要做统计、分析、差量预测、标记策略、智能降噪、可视化无痕、无痕分桶、反向推导热力图、大数据中台等等
涉及到前端、后端、运维、DBA 和算法

一起干下来,那你就是江湖顶级的前端渣男了

以上就是文章的全部了,谢谢你能全部看完

最后,祝你过上幸福快乐的生活

作者:第一名的小蝌蚪

微信公众号:前端屌丝

github:https://github.com/airuikun/blog

往期文章

  • 小蝌蚪传记:端口转发——夜店传说
  • 小蝌蚪传记:git 时光穿梭机——女神的侧颜
  • 小蝌蚪传记:PNG 图片压缩原理——屌丝的眼泪
退出移动版