新睿云

> 知识库 > h5埋点指北针

h5埋点指北针

作者/来源:新睿云小编 发布时间:2020-04-09

在日常的开发过程中,作为前端的 RD 同学,会经常的去配合 PM 去做一些业务埋点,而且有些埋点还贼复杂,埋点完成还要验收,对于 QA 跟 RD 来说都是一件很麻烦的事。 

h5

h5

埋点入侵了业务代码

// 这里有两个问题:1. 入侵了业务 2. 如果 clickReport 报错了,会影响正常的业务代码

const handleClick = () => {

    clickReport();

    doSomething();

}

携带业务参数过多,在组件间需要重复传递

// 层层传递无形之中增加了组件的复杂度

const Main = () => {

    const baseAnalytics = { city: 1, user: 2 };

    return (

        <div>

            <Child1 baseAnalytics={baseAnalytics} />

            <Child2 baseAnalytics={baseAnalytics} />

        </div>

}

部分复杂的埋点,需要在业务层面做很多额外的操作

目标

减少对业务的入侵

简化埋点的过程

方案

将埋点上报抽象出来 埋点的上报必然跟某个dom节点有关联,比如这个节点曝光了,或者这个节点被点击了。我们如果将我们的信息有规律的放在dom节点里,比如约公共的参数在上层,这样我们只要得到需要上报的节点,在一层一层的向上层查找,就可以将我们的数据聚合,并上报了

代码实现

const mergeLab = (parentLab: any, childLab: any) => {

  if (!parentLab) {

    return childLab;

  }

 

  if (!childLab) {

    return parentLab;

  }

 

  return { ...parentLab, ...childLab };

};

 

// 只支持 曝光 跟 点击 an 为 analytics 简称

export const findAnLabAndReport = (

  ele: HTMLElement | null,

  anData: any,

  eventType = "click",

  anReport

) => {

  const eventTypeId = eventType === "click" "anClickId" "anViewId";

 

  // 查找到 body 算结束

  if (!ele || ele === document.body) {

    if (!anData.eventId || anData.eventId === "null") {

      return;

    }

    anReport(anData);

    return;

  }

 

  const data: any = ele.dataset || {};

  let newLab: any;

  let newEventId: string | undefined = anData.eventId;

  let newPageId: string | undefined = anData.pageId;

 

  if (data.anLab) {

    try {

      newLab = JSON.parse(data.anLab);

    catch (e) {

      console.error(e);

    }

  }

  if (!anData.eventId && data[eventTypeId]) {

    newEventId = data[eventTypeId];

  }

 

  if (!anData.pageId && data.pageId) {

    newPageId = data.pageId;

  }

 

  findAnLabAndReport(

    ele.parentElement,

    {

      eventId: newEventId,

      pageId: newPageId,

      anLab: mergeLab(newLab, anData.anLab)

    },

    eventType,

    anReport

  );

};

 

 

处理点击事件上报

利用事件冒泡机制,在body上面绑定点击事件,再从event对象中拿到触发点击的节点,将其传递给 findAnLabAndReport即可,这样我们就不在需要再业务代码中增加 clickReport 这样的方法了。

注意:如果click事件被阻止冒泡了,这里就需要手动上传一下

实现效果

 

 

document.body.addEventListener(

    'click'

    (e: any) => findAnLabAndReport(e.target, {}, 'click', clickReport),

);

 

const APP = () => {

  return (

    <div 

      data-page-id="PageId" 

      data-an-lab={JSON.stringify({ userid: -1, cityid: -1 })}

     >

      <ChildA />

    </div>

  );

};

 

const ChildA = () => {

  return (

    <div 

      data-an-click-id="ClickId" 

      data-an-lab={JSON.stringify({ id: 1, cityid: 2 })}

    >

        ChildA Click

    </div>

  );

};

 

处理曝光事件

利用 intersection-observer 去做埋点曝光,核心还是要将处理的节点传递给我们的 findAnLabAndReport,拿到节点信息后,再将数据一层层聚拢,上报

const viewReportInit = (

  domOrDomList: HTMLElement | Array<HTMLElement>,

  viewReport,

  startObserver = true,

): Boolean => {

  if (!domOrDomList || (domOrDomList instanceof Array && !domOrDomList.length) || !startObserver) {

    return false;

  }

 

  let listerCount = domOrDomList instanceof Array ? domOrDomList.length : 1;

 

  const io = new IntersectionObserver(

    (IntersectionObserverEntryList: Array<IntersectionObserverEntry>) => {

      IntersectionObserverEntryList.forEach(

        (IntersectionObserverEntry: IntersectionObserverEntry) => {

          if (IntersectionObserverEntry.isIntersecting) {

            const targetEle = IntersectionObserverEntry.target;

            io.unobserve(targetEle);

            listerCount -= 1;

            findAnLabAndReport(targetEle as HTMLElement, {}, 'view', viewReport);

            if (listerCount <= 0) {

              io.disconnect();

            }

          }

        },

      );

    },

  );

 

  if (domOrDomList instanceof Array) {

    domOrDomList.forEach((ele: HTMLElement) => {

      io.observe(ele);

    });

  else {

    io.observe(domOrDomList);

  }

  return true;

};

 

 

实现效果

 

const APP = () => {

  // 这里只执行一次,如果有依赖的需要执行多次的话,`会出现多次曝光`

  useEffect(() => {

    viewReportInit(

      Array.prototype.slice.call(

        document.querySelectorAll(".view-observer") || []

      ),

      data => console.log(data)

    );

  }, []);

 

  return (

    <div

      data-page-id="PageId"

      data-an-lab={JSON.stringify({ userid: -1, cityid: -1 })}

    >

      <ChildA />

      <ChildB />

    </div>

  );

};

 

const ChildA = () => {

  return (

    <div

      className="view-observer"

      style={{ height: "200px", backgroundColor: "#f0f0f0" }}

      data-an-view-id="viewIdA"

      data-an-lab={JSON.stringify({ id: 1, cityid: 2 })}

    >

      ChildA

    </div>

  );

};

 

const ChildB = () => {

  return (

    <div

      className="view-observer"

      style={{ height: "200px", backgroundColor: "#red" }}

      data-an-view-id="viewIdB"

      data-an-lab={JSON.stringify({ id: 2, cityid: 3 })}

    >

      ChildB

    </div>

  );

};

总结

笔者这里只是抛转引玉,具体的业务埋点可以根据自己的业务需求去定制,这里主要是想将埋点与业务代码去解耦,不再对业务代码造成严重的入侵,并且简化埋点的方式。 可以根据上面提供的核心方法 findAnLabAndReport 去做更多的业务定制,比如笔者在业务开发中,定制了 useObserver 这样一个 hook 去做曝光节点。

热门标签
new year
在线咨询
咨询热线 400-1515-720
投诉与建议
{{item.description}}

—您的烦恼我们已经收到—

我们会将处理结果发送至您的手机

请耐心等待