Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

vtsaplin
Copy link
Collaborator

No description provided.

Copy link

aem-code-sync bot commented Dec 12, 2023

Hello, I'm the AEM Code Sync Bot and I will run some test suites that validate the page speed.
In case there are problems, just click the checkbox below to rerun the respective action.

  • Re-run PSI Checks

1. Copy the [`scripts/target.js`](scripts/target.js) to your project.
2. Include the script in the [`head.html`](index.html) as early as possible:
```html
<script src="./scripts/target.js" type="module"></script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, this would load target on every single page, which will have a negative perf overhead for most pages when only a select few actually need it. I'd probably wrap this with a basic conditional and require the use of a specific meta attribute, like Target: on or something like that.

So:

<script>
const targetMeta = document.head.querySelector('meta[name="target"]').content.toLowerCase().trim();
if (['true', 'on'].includes(targetMeta)) {
 // inject the script here and call loadTargetOffers immediately, so you don't even modify scripts.js
}
</script>

Or we return early in loadOffers if the meta tag isn't enabled (we still have the JS load overhead though).

Copy link

@buuhuu buuhuu Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that could be problematic when the customer uses conversion tracking based on page views. In that case adobe target must be enabled on the conversion page as well, lets say a thank you page of a form submission. It may not be obvious that the user has to "enable" target for both the location of the activity and the target page.

I would consider this a premature optimisation we may not even need as long as the impact of loading the offers is small.

2. Add a call to `loadTargetOffers` to the 'loadPage' function as follows:
```js
async function loadPage() {
loadTargetOffers('{Target Client Code}');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have tight coupling here between the 2 scripts, so we actually need to make sure target.js is loaded before scripts.js. And we could also instrument directly at the top of the scripts.js without touching the loadPage method

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is imported the module only executes if all imports are resolved. It should be fine like that.

<script src="/scripts/aem.js" type="module"></script>
<script src="/scripts/scripts.js" type="module"></script>
<link rel="stylesheet" href="/styles/styles.css"/>
<script src="./scripts/benchmark.js" type="module"></script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd skip the benchmarking for the prod version, and consider this like debug statements for now

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this script is not supposed to be included in production pages. It is an optional dependency.

@@ -7,6 +7,7 @@
"lint:js": "eslint .",
"lint:css": "stylelint blocks/**/*.css styles/*.css",
"lint": "npm run lint:js && npm run lint:css",
"lint:fix": "npm run lint:js --fix && npm run lint:css --fix",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically unrelated to the target integration

@@ -127,6 +128,7 @@ function loadDelayed() {
}

async function loadPage() {
loadTargetOffers('sitesinternal');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this up, just below the import theoretically a) saves a few nanoseconds of JS execution, and b) also keeps the logic together so it's easier to see at a glance

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also keeps the logic together so it's easier to see at a glance

I put it here for exactly this reason. I wouldn't expect to see any important logic among the import statements.

*/
function getSectionByElementSelector(selector) {
let section = document.querySelector(escapeSelector(selector));
while (section && !section.classList.contains('section')) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to use the .closest(…) API?

* @param useProxy Whether to use the proxy.
*/
export default function loadOffers(client, useProxy) {
if (window.location.href.includes('adobe_authoring_enabled')) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this set?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By VEC


const pendingOffers = fetchOffers(client, sessionId, useProxy ?? window.location.host.endsWith('workers.dev'));

getDecoratedContent()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would return the promise here in case someone ever wants to await it for some reason… it will also make unit testing easier down the road.

const { cssSelector } = offer;
console.debug('processing offer', offer); // eslint-disable-line no-console
const section = getSectionByElementSelector(cssSelector);
if (section) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would switch it to a guard clause instead

}
});

document.body.style.visibility = 'visible';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be toggled after the displayOffers?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

displayOffers is called per section

2. Add a call to `loadTargetOffers` to the 'loadPage' function as follows:
```js
async function loadPage() {
loadTargetOffers('{Target Client Code}');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is imported the module only executes if all imports are resolved. It should be fine like that.

// eslint-disable-next-line no-console
console.debug(`Using session ID ${sessionId}`);

document.body.style.visibility = 'hidden';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hiding the content here, not awaiting any of the async tasks and showing the content again has no effect. this can be removed as long as you don't wait anything.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is actually awaited here:

const offers = await pendingOffers;

It is revealed here after loading offers and setting up the observers:
document.body.style.visibility = 'visible';


const pendingOffers = fetchOffers(client, sessionId, useProxy ?? window.location.host.endsWith('workers.dev'));

getDecoratedContent()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it makes sense to wait for the appear class on the body. This is set after the main decoration finished, but before any block got decorated. In that state all sections are hidden. We need to apply the offers when the sections appear. So this is not necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I could hide the entire body part instead of just the main, which has to be awaited as it initially does not exist. Why do we hide the main?

The problem is that fetching offers for a webpage can take longer than expected. If the offers arrive late, after the main content of the page (LCP) has loaded, it causes issues like flickering and showing default content. To prevent this, we hide the main part of the page as a precaution, because we don't know yet which specific sections the offers will target.

@ramboz @buuhuu thoughts?

resolve(section);
}
});
observer.observe(section, config);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having one observer for each section has a performance impact. not a big one as we only have a few sections, but why not having a single observer on <main> instead? That code would be significantly easier to understand.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unsure about the performance impact, so I am attaching an observer to smaller DOM subtrees.

console.debug(`Loading offers for client ${client} and url ${url}`); // eslint-disable-line no-console

const payload = {
context: {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you left our quite some bits and pieces in the context.


document.body.style.visibility = 'hidden';

const pendingOffers = fetchOffers(client, sessionId, useProxy ?? window.location.host.endsWith('workers.dev'));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should inverse the logic and use the proxy always if not hlx.page, hlx.live or localhost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants