From 61e80af6bf33275258d27b8b990931b5b1ff59dd Mon Sep 17 00:00:00 2001 From: Joshua Kiwiet-Pantaleoni Date: Fri, 31 May 2024 10:18:31 -0700 Subject: [PATCH] feat: render preload links for js and css used during render --- server/vue-render.js | 5 ++--- src/server-entry.js | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/server/vue-render.js b/server/vue-render.js index 5be273b55c..2cbe976312 100644 --- a/server/vue-render.js +++ b/server/vue-render.js @@ -12,8 +12,7 @@ export default async function render({ context, serverConfig, serverEntry, - // TODO: use ssrManifest to determine which modules to preload - // ssrManifest, + ssrManifest, template }) { const s = Date.now(); @@ -50,10 +49,10 @@ export default async function render({ setCookies = [...cookieInfo.setCookies]; // render the app context.template = template; + context.ssrManifest = ssrManifest; const { html, setCookies: appSetCookies } = await serverEntry(context); // collect any cookies created during the app render setCookies = [...setCookies, ...appSetCookies]; - info('modules', context.modules); // send the final rendered html return { html, diff --git a/src/server-entry.js b/src/server-entry.js index 516299ad94..fbdd053937 100755 --- a/src/server-entry.js +++ b/src/server-entry.js @@ -70,6 +70,36 @@ function renderExtraHtml(config) { } } +// This function renders a tag for a given file +function renderPreloadLink(file) { + if (file.endsWith('.js')) { + return ``; + } + if (file.endsWith('.css')) { + return ``; + } + // TODO: handle other file types if needed + return ''; +} + +// This function renders tags for all files in the manifest for the given modules +function renderPreloadLinks(modules, manifest = {}) { + let links = ''; + const seen = new Set(); + modules.forEach(id => { + const files = manifest[id]; + if (files) { + files.forEach(file => { + if (!seen.has(file)) { + seen.add(file); + links += renderPreloadLink(file); + } + }); + } + }); + return links; +} + // This exported function will be called by `bundleRenderer`. // This is where we perform data-prefetching to determine the // state of our application before actually rendering it. @@ -84,6 +114,7 @@ export default async context => { user, locale, device, + ssrManifest, template, } = context; const { accessToken, ...profile } = user; @@ -201,12 +232,17 @@ export default async context => { // inline the state in the HTML response. This allows the client-side // store to pick-up the server-side state without having to duplicate // the initial data fetching on the client. - const payload = await renderSSRHead(head); const appState = renderGlobals({ __APOLLO_STATE__: apolloClient.cache.extract(), pageData: buildUserDataGlobal(router, cookieStore, apolloClient) }); + // render head tags + const payload = await renderSSRHead(head); + + // render preload links + const preloadLinks = renderPreloadLinks(context.modules, ssrManifest); + // check for 3rd party script opt-out const hasOptOut = cookies?.kvgdpr?.indexOf('opted_out=true') > -1; @@ -217,6 +253,7 @@ export default async context => { appConfig: renderedConfig, externals: hasOptOut ? renderedExternals : renderedExternalsOptIn, googleTagmanagerId: config.googleTagmanagerId, + preloadLinks, }; return {