GoogleChrome / web-vitals

Essential metrics for a healthy site.
https://web.dev/vitals
Apache License 2.0
7.47k stars 410 forks source link

LoAF Performance entry script sourceFunctionName and sourceURL logged values #489

Closed jinja12 closed 3 months ago

jinja12 commented 3 months ago

I have a very simple page where I provide an option to add an item via 2 buttons: button-1 and button-2, the onClick handlers for both buttons have a small difference, one has an empty while loop running for 3s while the other runs for 4s.

The code for the page is below for reference. Now when I click any of the buttons and see the console, where I am interested in the Long Animation Frames entry inside the logged object, the source function and the source url is different from the expected output. The page can be found here (deployed on vercel, the image below shows output on localhost): Deployed Page Link

Code:

"use client";
import { useState, useEffect, useRef } from "react";
import { onINP, onCLS } from "web-vitals/attribution";
import { FadeInAnimation } from "./animation.js";

interface Item {
  id: number;
  name: string;
  category: string;
}

export default function INPPage() {
  const [items, setItems] = useState<Item[]>([]);
  const [show, setShow] = useState(false);

  function Welcome() {
    const ref = useRef(null);

    useEffect(() => {
      const animation = new FadeInAnimation(ref.current);
      animation.start(1000);
      return () => {
        animation.stop();
      };
    }, []);

    return (
      <h1
        ref={ref}
        style={{
          opacity: 0,
          color: "white",
          padding: 50,
          textAlign: "center",
          fontSize: 50,
          backgroundImage:
            "radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)",
        }}
      >
        Welcome
      </h1>
    );
  }

  useEffect(() => {
    const REPORTING_THRESHOLD_MS = 150;
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        console.log("LKJHGFDSDFGHJKL ", entry)
        if (
          entry.duration > REPORTING_THRESHOLD_MS &&
          entry.firstUIEventTimestamp > 0
        ) {
          console.log("Long task detected:", entry)
        }
      }
    });

    observer.observe({ type: "long-animation-frame", buffered: true });

    console.log("Printing something");
    onINP(console.log, { reportAllChanges: true });
  }, []);

  const handleAddItem = () => {
    const newItem = { id: items.length + 1, name: "", category: "" };
    const start = Date.now();
    while (Date.now() - start < 3000) {}
    setItems([...items, newItem]);
  };

  const handleAddItem4s = () => {
    const newItem = { id: items.length + 1, name: "", category: "" };
    const start = Date.now();
    while (Date.now() - start < 4000) {}
    setItems([...items, newItem]);
  };

  const handleInputChange = (id: number, field: string, value: string) => {
    setItems(
      items.map((item) => (item.id === id ? { ...item, [field]: value } : item))
    );
  };

  return (
    <div className="min-h-screen bg-gray-100 p-4">
      <div className="container mx-auto p-4 bg-gray-800 text-white rounded shadow-lg"> {/* Updated background color and text color */}
        <h1 className="text-2xl font-bold mb-4">INP Test Page with LoAF</h1>
        <button onClick={() => setShow(!show)}>
          {show ? "Remove" : "Show"}
        </button>
        <hr />
        {show && <Welcome />}
        <br />
        <br />
        <button
          onClick={handleAddItem}
          className="mb-4 px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-400"
        >
          Add Item (3s)
        </button>
        <button
          onClick={handleAddItem4s}
          className="mb-4 px-6 py-3 bg-green-500 text-white rounded-lg hover:bg-green-400"
        >
          Add Item (4s)
        </button>
        <div className="grid grid-cols-1 gap-4">
          {items.map((item) => (
            <div key={item.id} className="p-4 bg-gray-200 rounded-lg shadow-md">
              <div className="mb-2">
                <label className="block text-sm font-medium text-gray-700">
                  Name
                </label>
                <input
                  type="text"
                  value={item.name}
                  onChange={(e) =>
                    handleInputChange(item.id, "name", e.target.value)
                  }
                  className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                />
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700">
                  Category
                </label>
                <input
                  type="text"
                  value={item.category}
                  onChange={(e) =>
                    handleInputChange(item.id, "category", e.target.value)
                  }
                  className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Output Image:

image

Highlighted above is the sourceFunctionName and the sourceURL. In my application there is no such thing. I only clicked on the two buttons which you can see for which the function names which handle them are: handleAddItem and handleAddItem4s.

I went through the documentation for the same here: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceScriptTiming/sourceFunctionName as well, but it does not provide a great idea into the attribute (expected behaviour and the result), if the output can't be changed, is there a way to get the exact function which is taking a lot of time/blocking the main single thread ? Similarly for the sourceURL attribute as well. Since this is a relatively new addition, I could not find much resources apart from the original docs to look into it.

It would be great if this could be please looked into once and some details be provided about how to go about getting the correct source functions and in a broader sense action items to understand the metric in depth.

Thanks!

tunetheweb commented 3 months ago

You would need to raise this in the Long Animation Frames repo rather than here. At present the web-vitals library just reports the LoAF entry (in future we may do some processing to highlight the most important information).

jinja12 commented 3 months ago

You would need to raise this in the Long Animation Frames repo rather than here. At present the web-vitals library just reports the LoAF entry (in future we may do some processing to highlight the most important information).

Got it, thanks! Raised an issue there. I had one more question: Following is the screenshot of the web-vitals-attribution console log:

image

There is an attribute named: interactionTargetElement inside the object, which provides quite a few details like the children node: "Add Item (4s)" here and the onClickHandler name as well (here: handleAddItem4s). The documentation : https://codelabs.developers.google.com/measuring-inp#4 here says not much about the details within the interaction target element object. Could an idea be provided about how can we access these details like onClick() handler name, children properties within the __reactProps${key} attribute? It is not clear what is the suffix after the $ in both react props and fiber node. Moreover, wouldn't this information be helpful in debugging what and where is the root issue lying for the main thread blocking ?

Thanks!

jinja12 commented 3 months ago

@tunetheweb

tunetheweb commented 3 months ago

Link to raised issue for anyone with similar queries: https://github.com/w3c/long-animation-frames/issues/13

There is an attribute named: interactionTargetElement inside the object, which provides quite a few details like the children node: "Add Item (4s)" here and the onClickHandler name as well (here: handleAddItem4s). The documentation : https://codelabs.developers.google.com/measuring-inp#4 here says not much about the details within the interaction target element object.

It is literally the Element as exposed by the PerformanceEventTiming entry.

Could an idea be provided about how can we access these details like onClick() handler name, children properties within the __reactProps${key} attribute? It is not clear what is the suffix after the $ in both react props and fiber node. Moreover, wouldn't this information be helpful in debugging what and where is the root issue lying for the main thread blocking ?

That's exactly why we make the element available. This is a new thing in v4. Prior to that we took an opinionated stance and only gave the selector in the interactionTarget property. In #456 it was requested to make the target element itself available for those that wished to choose an alternative identifier depending on how exactly their app was built.