IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    umami 源码分析

    Yao发表于 2023-11-15 15:55:04
    love 0

    umami 不使用 cookie 、localstorage 实现了 uv 识别,符合最新的隐私规范,代码也比较精简。花了一点时间,研究了这部分的实现,还是很有意思的。

    技术框架:

    next.js + mysql/postgresql
    整体看下来,nextjs 准备了很多的约定,比如说 api 目录在 pages/api/下面,比如说,pages/api/user.js,nextjs 框架有大量这样的约定。

    个人还是不太习惯这样的框架,总有种不伦不类的感觉,一些小项目可以这样搞,大型项目,还是需要明确的代码分成,封装。

    用户识别

    uv 的核心是去做用户识别,一般会本地存储一个随机的id,每次页面路由变化,上报给后台。
    而 umami 为了符合一系列的隐私规范,并没有这么做,没有使用 cookie或者 localStorage 。
    上报路径”pages/api/collect.js”,最终会产生 session 并且通过 session 返回 token 。session 生成方法 getSession

    const session_uuid = uuid(website_id, hostname, ip, userAgent);session = await createSession(website_id, {    session_uuid,    hostname,    browser,    os,    screen,    language,    country,    device,});

    这部分代码就比较清楚了,根据网站id,域名,ip,userAgent 生成 session_uuid,然后和数据库通信创建或者使用 session。
    核心是根据一些列的变量生成一个不变的 uuid,后续用户再次进入页面,根据用户的这些参数,去数据库查询这个 uuid,就实现了用户识别。

    里面也有一些其他的逻辑,比如跨域,忽略本地地址等,但是不影响我们对核心逻辑的理解。

    上报脚本脚本

    上报脚本位置:tracker/index.js
    这个脚本很短,只有短短的225实现了上报功能。

    <script async defer data-website-id="914685a1-8993-4d8c-895b-929c8646e814" src="http://localhost:3000/umami.js"></script>
    • async: async 脚本会在后台加载,并在加载就绪时运行。DOM 和其他脚本不会等待它们,它们也不会等待其它的东西。async 脚本就是一个会在加载完成时执行的完全独立的脚本。
    • defer: 特性告诉浏览器不要等待脚本。相反,浏览器将继续处理 HTML,构建 DOM。脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行。
    • data-website-id: 网站 id

    挂载

    if (!window.umami) &#123;    const umami = eventValue => trackEvent(eventValue);    umami.trackView = trackView;    umami.trackEvent = trackEvent;    window.umami = umami;  &#125;

    方法都挂载到 window 上面,后续可以直接调用。

    记录 pv uv

    if (autoTrack && !trackingDisabled()) &#123;    // 监听 pushState,replaceState 事件    history.pushState = hook(history, 'pushState', handlePush);    history.replaceState = hook(history, 'replaceState', handlePush);    const update = () => &#123;      console.error('update');      if (document.readyState === 'complete') &#123;        console.error('complete');        trackView();        if (cssEvents) &#123;          addEvents(document);          observeDocument();        &#125;      &#125;    &#125;;    document.addEventListener('readystatechange', update, true);    update();  &#125;

    这里最终会在document.readyState === 'complete'时候,去做事件监听绑定等操作,发送第一次页面上报。
    有一点不理解,为什么已经监听了readystatechange,还是又手动执行了一次update()

    监听路由改变

    export const hook = (_this, method, callback) => &#123;  const orig = _this[method];  return (...args) => &#123;    callback.apply(null, args);    return orig.apply(_this, args);  &#125;;&#125;;

    history.pushState hook劫持,为了在原生方法执行前,执行callback。这样实现了对原生 history 的监听。handlePush 方法会执行上报方法 trackView。

    总结

    整体代码比较简单,清晰,无侵入性的实现了网站统计。



沪ICP备19023445号-2号
友情链接