Skip to content
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

MWPW-158071: Optimize LCP loading times #2914

Open
wants to merge 4 commits into
base: stage
Choose a base branch
from
Open
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 48 additions & 36 deletions libs/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,27 +446,27 @@ export async function loadTemplate() {
await Promise.all([styleLoaded, scriptLoaded]);
}

export async function loadBlock(block) {
if (block.classList.contains('hide-block')) {
block.remove();
return null;
}

function getBlockData(block) {
const name = block.classList[0];
const hasStyles = AUTO_BLOCKS.find((ab) => Object.keys(ab).includes(name))?.styles ?? true;
const { miloLibs, codeRoot, mep } = getConfig();

const base = miloLibs && MILO_BLOCKS.includes(name) ? miloLibs : codeRoot;
let path = `${base}/blocks/${name}`;

if (mep?.blocks?.[name]) path = mep.blocks[name];

const blockPath = `${path}/${name}`;
const hasStyles = AUTO_BLOCKS.find((ab) => Object.keys(ab).includes(name))?.styles ?? true;

return { blockPath, name, hasStyles };
}

export async function loadBlock(block) {
if (block.classList.contains('hide-block')) {
block.remove();
return null;
}
const { name, blockPath, hasStyles } = getBlockData(block);
const styleLoaded = hasStyles && new Promise((resolve) => {
loadStyle(`${blockPath}.css`, resolve);
});

const scriptLoaded = new Promise((resolve) => {
(async () => {
try {
Expand Down Expand Up @@ -731,6 +731,8 @@ function decorateDefaults(el) {
}

function decorateHeader() {
const breadcrumbs = document.querySelector('.breadcrumbs');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I noticed that if a breadcrumbs is authored as first block, we immediately load it. The global-navigation should take care of this and on L752 we move the breadcrumbs into the header if (breadcrumbs) header.append(breadcrumbs);

Copy link
Contributor Author

@mokimo mokimo Sep 19, 2024

Choose a reason for hiding this comment

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

breadcrumbs?.remove();
const header = document.querySelector('header');
if (!header) return;
const headerMeta = getMetadata('header');
Expand All @@ -744,7 +746,7 @@ function decorateHeader() {
|| getConfig().breadcrumbs;
if (metadataConfig === 'off') return;
const baseBreadcrumbs = getMetadata('breadcrumbs-base')?.length;
const breadcrumbs = document.querySelector('.breadcrumbs');

const autoBreadcrumbs = getMetadata('breadcrumbs-from-url') === 'on';
const dynamicNavActive = getMetadata('dynamic-nav') === 'on'
&& window.sessionStorage.getItem('gnavSource') !== null;
Expand Down Expand Up @@ -1221,41 +1223,51 @@ export function partition(arr, fn) {
);
}

async function processSection(section, config, isDoc) {
const inlineFrags = [...section.el.querySelectorAll('a[href*="#_inline"]')];
if (inlineFrags.length) {
const { default: loadInlineFrags } = await import('../blocks/fragment/fragment.js');
const fragPromises = inlineFrags.map((link) => loadInlineFrags(link));
await Promise.all(fragPromises);
const newlyDecoratedSection = decorateSection(section.el, section.idx);
section.blocks = newlyDecoratedSection.blocks;
section.preloadLinks = newlyDecoratedSection.preloadLinks;
const preloadBlocks = (blocks = []) => blocks.map((block) => {
if (block.classList.contains('hide-block')) return null;
const { blockPath, hasStyles, name } = getBlockData(block);
if (name === 'marquee' || name === 'hero-marquee') {
loadLink(`${getConfig().base}/utils/decorate.js`, { rel: 'preload', as: 'script', crossorigin: 'anonymous' });
}
await decoratePlaceholders(section.el, config);
loadLink(`${blockPath}.js`, { rel: 'preload', as: 'script', crossorigin: 'anonymous' });
return hasStyles ? new Promise((resolve) => { loadStyle(`${blockPath}.css`, resolve); }) : null;
}).filter(Boolean);

async function resolveInlineFrags(section) {
const inlineFrags = [...section.el.querySelectorAll('a[href*="#_inline"]')];
if (!inlineFrags.length) return;
const { default: loadInlineFrags } = await import('../blocks/fragment/fragment.js');
const fragPromises = inlineFrags.map((link) => loadInlineFrags(link));
await Promise.all(fragPromises);
const newlyDecoratedSection = decorateSection(section.el, section.idx);
section.blocks = newlyDecoratedSection.blocks;
section.preloadLinks = newlyDecoratedSection.preloadLinks;
}

async function processSection(section, config, isDoc) {
await resolveInlineFrags(section);
const firstSection = section.el.dataset.idx === '0';
const stylePromises = firstSection ? preloadBlocks(section.blocks) : [];
preloadBlocks(section.preloadLinks);
await Promise.all([
decoratePlaceholders(section.el, config),
decorateIcons(section.el, config),
]);
const loadBlocks = [...stylePromises];
if (section.preloadLinks.length) {
const [modals, nonModals] = partition(section.preloadLinks, (block) => block.classList.contains('modal'));
const preloads = nonModals.map((block) => loadBlock(block));
await Promise.all(preloads);
const [modals, blocks] = partition(section.preloadLinks, (block) => block.classList.contains('modal'));
blocks.forEach((block) => loadBlocks.push(loadBlock(block)));
modals.forEach((block) => loadBlock(block));
}

const loaded = section.blocks.map((block) => loadBlock(block));

await decorateIcons(section.el, config);
section.blocks.forEach((block) => loadBlocks.push(loadBlock(block)));

// Only move on to the next section when all blocks are loaded.
await Promise.all(loaded);
await Promise.all(loadBlocks);

// Show the section when all blocks inside are done.
delete section.el.dataset.status;

if (isDoc && section.el.dataset.idx === '0') {
await loadPostLCP(config);
}

if (isDoc && firstSection) await loadPostLCP(config);
delete section.el.dataset.idx;

return section.blocks;
}

Expand Down
Loading