-
-
Notifications
You must be signed in to change notification settings - Fork 26.9k
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
Add preload to script and link tags in production builds #3319
Comments
@muhammadtarek Here it is: const fs = require('fs');
const pathToEntry = './build/index.html';
const bundlesRegExp = /\/static\/\w+\/\w+.[a-z0-9]+.\w{2,3}/g;
const splitBy = '</title>';
const builtHTMLContent = fs.readFileSync(pathToEntry).toString();
const links = builtHTMLContent.match(bundlesRegExp);
const parts = builtHTMLContent.split(splitBy);
let fileWithPreload = [
parts[0],
splitBy,
];
links.forEach(link => {
let fileType = 'script';
if (/\.css$/.test(link)) {
fileType = 'style';
}
fileWithPreload = [
...fileWithPreload,
`<link rel="preload" href="${link}" as="${fileType}">`,
];
});
fileWithPreload = [
...fileWithPreload,
parts[1],
];
fs.writeFileSync(pathToEntry, fileWithPreload.join('')); to run it automatically after each prod build add {
"scripts": {
"build": "react-scripts build && node ./addPreloadLinks.js"
}
} Hope it will help you 🙂 |
It works. Thank you @necinc |
Can you explain why this is useful? The JS/CSS bundle generated by CRA is required for the application to work, and there is nothing to display until it has loaded, so why would |
I need to request the JS/CSS bundle as soon as possible which will reduce the loading time that's as far I understand |
@muhammadtarek |
@necinc Thanks, I didn't know that. |
If I understand it right, "preload" is useful when you don't use a script/link right away but will likely need it in the future. Browser then optimistically starts loading it with a low priority. This is different from CRA case where you can't possibly render anything without these tags. It might, however, be useful for codesplit chunks. |
You're right. I misunderstood it. Thanks for clarification |
Hey all, just chiming in here. link rel preload is useful since it enables critical resources to start downloading before the page has finished parsing... As soon as the streaming HTML parser encounters this tag the browser knows to go ahead and start fetching the asset. Putting link rel preload for critical CSS and JS can increase boot time on the order of 10+%. The other script tags that are higher in the DOM can actually cause the critical One of the talks I have given on this topic: -- Let me know if I can be of any help here! Thanks as always. |
In case of CRA the page is usually tiny (a hundred bytes at most) but I'd be open to supporting this if it's contributed upstream to |
Here's what I believe to be another use case: I have an above-the-fold image that needs to be loaded as quickly as possible. Currently, the browser only knows to fetch the image after the bundle containing the reference to that image has finished loading and parsing. Adding support for rel='preload' would allow me to begin loading that image more or less immediately. |
@jacklenehan I don't think this is related to this issue? The issue is about generated script tags. I don't see how putting |
@gaearon sorry, my comment wasn't clear - I wanted to add
where |
What is hacky about this? (Assuming you mean I think I don't understand what other solution you have in mind. |
You would have to keep your resources in two places, manually adding the preload links when you reference assets in your code using A perf boost of 10% (as @samccone said) with a webpack plugin like https://github.com/GoogleChromeLabs/preload-webpack-plugin should be a no brainer, imo. It should be part of best practices when building a React app. |
import(/* webpackPrefetch: true */ "DashboardPage") |
Another use case: preload font files in
I get dinged for not having this in the Chrome Lighthouse audit. This might be more complicated than preloading .js assets though. |
Script above creates malformed HTML appending Hacked a fix together based on what's above.. // postbuild.js
const fs = require('fs');
const pathToEntry = './build/index.html';
const bundlesRegExp = /\/static\/\w+\/\w+.[a-z0-9]+.\w{2,3}/g;
const builtHTMLContent = fs.readFileSync(pathToEntry).toString();
const links = builtHTMLContent.match(bundlesRegExp);
const linkAs = {
css: 'style',
js: 'script'
}
const linkPreloads = links.map(link =>
`<link rel="preload" as="${linkAs[link.split('.').pop()]}" href="${link}">`
).join('');
const htmlWithPreload = builtHTMLContent.replace(
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">',
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' + linkPreloads
)
fs.writeFileSync(pathToEntry, htmlWithPreload); {
"scripts": {
"build": "react-scripts build && node ./postbuild.js"
}
} Be careful with how many things you preload. Chrome only makes 6 requests at a time. Great thread on preload from the Chrome Lighthouse audit crowd (+bonus Paul Irish). For anyone interested in a deeper dive on this topic: GoogleChrome/lighthouse#3106 |
@necinc script has worked well for me 🙏 but since CRA 2.0 I had to rewrite the REGEX to match the new chunks name pattern. I have tried the recommendation which @stereobooster has mentioned but this works only for Webpack >= 4.6 const fs = require("fs");
const pathToEntry = "./build/index.html";
const bundlesRegExp = /\/static\/\w+\/\w+.[a-z0-9]+.[a-z0-9]+.\w{2,3}/g;
const splitBy = "</title>";
const builtHTMLContent = fs.readFileSync(pathToEntry).toString();
const links = builtHTMLContent.match(bundlesRegExp);
const parts = builtHTMLContent.split(splitBy);
let fileWithPreload = [parts[0], splitBy];
links.forEach(link => {
let fileType = "script";
if (/\.css$/.test(link)) {
fileType = "style";
}
fileWithPreload = [
...fileWithPreload,
`<link rel="preload" href=".${link}" as="${fileType}">`
];
});
fileWithPreload = [...fileWithPreload, parts[1]];
fs.writeFileSync(pathToEntry, fileWithPreload.join("")); |
Can I use |
It looks like a feature of the HTML webpack plugin that is not yet shipped :-/ |
aha! many thanks for the answer. where can I track the progress or contribute to this feature? |
I'm not sure I read it in the comments of the Medium article that describes the feature :-( |
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 5 days if no further activity occurs. |
It looks like we're waiting on jantimon/html-webpack-plugin#1014 |
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 5 days if no further activity occurs. |
Bad stale bot, bad bot, don't act this way |
This issue has been automatically closed because it has not had any recent activity. If you have a question or comment, please open a new issue. |
No recent activity? What the fk |
I want to preload styles and main script. But the current config generates the .html without preload support. So is there any way to make that happens?
The text was updated successfully, but these errors were encountered: