-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
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
[template-renderer] Make it possible to not automatically render used async scripts #9847
Comments
I'm working on an app where this would be a huge improvement to lighthouse scores. Would a PR be accepted to include this option? |
Provide a new option (shouldRenderAsyncScripts) to render or not async scripts in TemplateRenderer fix vuejs#9847
this is the related PR #10794 |
That would be neat, Vue & Nuxt applications would see a huge performance boost, especially on LightHouse scores. |
Provide a new option (shouldRenderAsyncScripts) to render or not async scripts in TemplateRenderer fix vuejs#9847
this would be super awesome to have, I'm currently struggling with lighthouse performance and this would make things a lot easier... ( want that nearly perfect score :) ) |
If you use nuxt, you can actually work around either by using modern mode (due to a bug) or by using a regex to remove the async deferred chunks from html ouput (render:route, see https://nuxtjs.org/api/internals-renderer#hooks) example plugin: import consola from 'consola';
const logger = consola.withScope('js-optimization:module');
const bodyRegex = /<body[^>]*>(.*)<\/body>/s;
// list of all JS includes
const scriptRegex = /<script[\w"= ]*src="(.*?)".*?><\/script>/g;
// essenitials are all with "pages" or ending with "app.js"
const validScriptRegex = /\/(legacy-)?.*?-(pages.*?|.*app).js/;
module.exports = async function JSOptimizer(moduleOptions) {
if (!moduleOptions.setOutputFilenames) {
logger.error(
'JS optimization works only when you explicitly opt in for overwriting output filenames in nuxt!'
);
return;
}
if (!this.options.build) this.options.build = {};
this.options.build.filenames = {
...this.options.build.filenames,
app: ({ isModern, isDev }) =>
`${!isModern ? 'legacy-' : ''}${!isDev ? '[contenthash]' : '[name]'}-app.js`,
chunk: ({ isModern, isDev }) =>
`${!isModern ? 'legacy-' : ''}${!isDev ? '[contenthash]-' : ''}[name].js`
};
this.nuxt.hook('render:route', async (url, page, { req, res }) => {
if (!page.html || (res.statusCode && res.statusCode !== 200) || page.redirected || page.error) {
if (moduleOptions.debug) {
logger.info(
'skipping optimize JS render:route',
JSON.stringify({
url,
isAmp: req.isAMP,
matchedRoute: req.matchedRoute,
page: page.html.length,
statusCode: res.statusCode,
error: page.error,
redirected: page.redirected
})
);
}
return;
}
if (moduleOptions.debug) {
logger.info(
'optimize JS render:route',
JSON.stringify({ url, isAmp: req.isAMP, matchedRoute: req.matchedRoute })
);
}
if (!req.isAMP) {
// remove all non-essential JS files
let { html } = page;
const bodyString = bodyRegex.exec(html);
if (!bodyString || !bodyString[0]) {
logger.warn('no body tag found', html);
return;
}
const body = bodyString[0];
const links = body.matchAll(scriptRegex);
for (const match of links) {
if (!validScriptRegex.test(match[1])) {
// remove non essential JS
html = html
.replace(match[0], '') // script tag
.replace(`<link rel="modulepreload" href="${match[1]}" as="script">`, '') // module preload
.replace(`<link rel="preload" href="${match[1]}" as="script">`, ''); // preload
if (moduleOptions.debug) {
logger.info('removed js tags for', match[1]);
}
}
}
page.html = html; // set new response
}
});
logger.success('JS optimization module initialised');
}; save it in modules/js-optimization.js and add |
@simllll |
You are welcome, I just updated the example plugin to also get rid of preload tags. How did you remove them? |
I used another replace with the matched scripts:
Will update to use fully your module 👍 |
@simllll I've got a question here. |
Yes, that's exactly how we use it. It only makes sense to use with lazy hydration though, otherwise the JS will be needed for the inital page load and removing the preload tags would just slow down the page rendering. |
Gotcha! It makes perfect sense. |
Unfortunately, I couldn't see any change in the scripts loaded nor in the performance.
Then
Then build: |
Your example looks right, you can easily add some output in "mounted" hook to see if everything works as expected. Lazy hydration with ssr-only mode, shouldn't call the mounted hook at all. So e.g if you place a console log in there, the browser should not log it.
It's very likely that you will only notice this on mobile phones though. Try reducing cpu and network performance in chrom dev tools to debug and analyze this on your desktop pc. Check out the performance tab, and watch the JS profiler. |
Yes, did that and the JS files for that specific page were indeed deleted. However, the performance didn't improve at all. I guess there is a core issue with vuetify performance. |
@simllll Hey Simon, I did exactly what you told, but I am getting: │ ✖ Nuxt Fatal Error │ Update: Had to call it |
@simllll I am getting this error:
|
@simllll Had to install the matchall polyfill (https://www.npmjs.com/package/string.prototype.matchall) (aparently only es2020). My file ended up looking like so:
However, it didn't work. No scripts would be loaded to page at all. When visiting any page, having
|
seems this workaround breaks with nuxt v2.13.2 nuxt v2.13.3 does somebody has an updated solution? |
@maoberlehner are you aware of a workaround to this solution posted by @simllll which unfortunately doesn't seem to work anymore? |
Actually, I just tried again to use @simllll's solution on latest Nuxt version, and this time I didn't need the polyfill, and it worked out of the box. Will run a pagespeed test soon to see how/if things have changed. |
@simplenotezy Can you share any results, please? |
@simllll Thanks for |
Managed to adapt @simllll workaround to work with nuxt full static mode.
|
This will stop all other components |
i'm getting |
check node version |
What problem does this feature solve?
In oder to make https://github.com/maoberlehner/vue-lazy-hydration more useful it would be great if we could prevent the template renderer from automatically injecting async scripts. The way vue-lazy-hydration works is, that it behaves differently on the server than it does on the client. On the server the script (of an async component) is loaded immediately so the template renderer correctly detects that it is used. But on the client the script might not be needed at all but because the template renderer has already injected it it is immediately loaded on page load.
There is currently kind of a backlash against loading huge amounts of JavaScript. vue-lazy-hydration can help with removing a lot of unnecessary JavaScript on server side rendered, mostly static sites like blogs and documentation. But currently it can't completely prevent loading all of the unnecessary JavaScript because of the way how template renderer works.
Here is the relevant line in the code: https://github.com/vuejs/vue/blob/dev/src/server/template-renderer/index.js#L226
What does the proposed API look like?
I propose to make this configurable:
The text was updated successfully, but these errors were encountered: