-
Notifications
You must be signed in to change notification settings - Fork 0
feat: vec for helix #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: review
Are you sure you want to change the base?
Conversation
- decouple perf measurement
- decouple perf measurement
Hello, I'm the AEM Code Sync Bot and I will run some test suites that validate the page speed.
|
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> |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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}'); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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')) { |
There was a problem hiding this comment.
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')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this set?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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}'); |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
helix-target-integration/scripts/target.js
Line 257 in eafdb21
const offers = await pendingOffers; |
It is revealed here after loading offers and setting up the observers:
helix-target-integration/scripts/target.js
Line 274 in eafdb21
document.body.style.visibility = 'visible'; |
|
||
const pendingOffers = fetchOffers(client, sessionId, useProxy ?? window.location.host.endsWith('workers.dev')); | ||
|
||
getDecoratedContent() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
resolve(section); | ||
} | ||
}); | ||
observer.observe(section, config); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
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')); |
There was a problem hiding this comment.
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.
No description provided.