background

In the daily development process, as a RD student at the front end, I would often cooperate with PM to do some business burying points, and some burying points are complicated, and the completion of burying points requires acceptance, which is a very troublesome thing for QA and RD.

Analysis of the

Pain points

  1. A buried point invades the business code
// There are two problems: 1. The business is being invaded; 2. If the clickReport is wrong, the normal business code will be affected
const handleClick = (a)= > {
    clickReport();
    doSomething();
}

Copy the code
  1. Too many service parameters are carried and need to be passed repeatedly between components
// Layer by layer adds complexity to the component
const Main = (a)= > {
    const baseAnalytics = { city: 1.user: 2 };
    return (
        <div>
            <Child1 baseAnalytics={baseAnalytics} />
            <Child2 baseAnalytics={baseAnalytics} />
        </div>
}
Copy the code
  1. Part of the complex burying point requires a lot of additional operations at the business level

The target

  1. Reduce service intrusion
  2. Simplify the process of burying points

plan

  1. To abstract a buried point report, a buried point report must be associated with a DOM node, such as the node being exposed, or the node being clicked. If we put our information in DOM nodes regularly, for example, the common parameters are in the upper layer, so that we can aggregate our data and report it as long as we get the nodes that need to be reported and search for the upper layer layer by layer

Code implementation

const mergeLab = (parentLab: any, childLab: any) = > {
  if(! parentLab) {return childLab;
  }

  if(! childLab) {return parentLab;
  }

  return{... parentLab, ... childLab }; };// Only exposure and clicking an are supported
export const findAnLabAndReport = (
  ele: HTMLElement | null.anData: any,
  eventType = "click",
  anReport
) => {
  const eventTypeId = eventType === "click" ? "anClickId" : "anViewId";

  // Find the 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
  );
};

Copy the code
  1. Handle click event reporting

    Using the event bubble mechanism, bind the click event to the body, take the node that triggered the click from the Event object, and pass it tofindAnLabAndReportSo we don’t need to add to the business codeclickReportThat’s the way it is.

    Note: If the click event is prevented from bubbling, you need to upload it manually here

Implementation effect

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

const APP = (a)= > {
  return (
    <div 
      data-page-id="PageId" 
      data-an-lab={JSON.stringify({ userid: - 1.cityid: - 1 })}
     >
      <ChildA />
    </div>
  );
};

const ChildA = (a)= > {
  return (
    <div 
      data-an-click-id="ClickId" 
      data-an-lab={JSON.stringify({ id: 1.cityid: 2 })}
    >
        ChildA Click
    </div>
  );
};
Copy the code
  1. Handling exposure events

    usingintersection-observerTo do a buried point exposure, the core still has to pass us the nodes to processfindAnLabAndReport, get the node information, and then the data layer by layer together, reported
const viewReportInit = (
  domOrDomList: HTMLElement | Array<HTMLElement>,
  viewReport,
  startObserver = true,
): Boolean= > {
  if(! domOrDomList || (domOrDomListinstanceof 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;
};

Copy the code

Implementation effect

const APP = (a)= > {
  If a dependency needs to be executed more than once, 'multiple exposures' will appear
  useEffect((a)= > {
    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 = (a)= > {
  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 = (a)= > {
  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>
  );
};
Copy the code

conclusion

The specific business burying point can be customized according to its own business needs. The main purpose here is to decouple the burying point from the business code, so as not to cause serious intrusion to the business code, and to simplify the way of burying point. More business customization can be done according to the core method findAnLabAndReport provided above. For example, in business development, the author customized useObserver hook to do the exposure node.

Click to see the code address