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
Force some polyfill into de bundle #2969
Comments
What browsers are ? I thought that babel solved the problem of compatibility. |
As I understand, Babel transpile es6 code to es5. That doesn't means that all browser support all functions. That's why they also provide babel-polyfill. The think is I don't want to load all babel-polyfill and I don't want the polyfills to overwrite native functions on browser that already support them |
This is an interesting one, most people include it in the head using _document.js |
Yes. I was using polyfill.io services, which is great and work fine. But the request to the service it's pretty slow even for browser that doesn't need any polyfill. So I think that if I'm going to penalize every user in favor of those with old browser, having the polyfill code inside of the bundle is a better option than adding an extra/external request before the bundle get loaded. What do you think? NOTE: I've try to dynamically add the polyfill.io script when needed (using a small JS code that appends the script tag -with |
Same issue for me. I need to add some polyfills but not language polyfills as Promises or Array functions but Window/Render polyfills. Specifically IntersectionObserver and Smoothscoll. Both require access to window, but it is not available in server side rendering. So i need to put it into separate script file. Any way how to force Webpack create new js file? |
It is pretty hacky, but the option it worked the best for me is to dynamically load the polyfill with a synchronous (function(undefined) {
if (!('Promise' in this)) {
var src = 'https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise&flags=always&unknown=polyfill';
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var f = new Function(xhr.responseText);
f();
}
}
xhr.open('GET', src, false);
xhr.send();
}
})
.call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {}); If you don't want to use Polyfill service, you can always get the code from them, save it in a local file and then loaded from some static url/CDN (polyfill is not a code that usually change) |
@coluccini yes it is pretty hacky, but no other options because NextJS has not easy hooks to change generated files (entry and CommonPlugin). Where did you put this code ? Just for example in _document.js at begin of file? |
@radeno, exactly: in |
@coluccini does it work for you? I get and error |
Yes. But I'm not trying to executing directly in next. I'm print that code on SSR: const polyfill = `THE_CODE`;
export default class baseDocument extends Document {
render() {
return (
<html lang={locale}>
//...
<body>
//...
<script dangerouslySetInnerHTML={{ __html: polyfill }} />
<NextScript />
</body>
</html>
);
}
} |
I see, so you inlining requested script content, is it neccessary? Isn't this easier to use script tag with src attribute? export default class baseDocument extends Document {
render() {
return (
<html lang={locale}>
//...
<body>
//...
<script
type="text/javascript"
src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise&flags=always&unknown=polyfill"
/>
<NextScript />
</body>
</html>
);
}
} or just put script tag into component. By testing performance is same and NextJS script is executed right after. |
Yes, you can do it that way for sure (you should remove There are two bad thing about this approach:
If you are ok with those issues, this is the way to go. |
If Next.js had a way of allowing the control of //
// You can achieve non-blockin polyfills with polyfill.io using the following
// event-pattern, assuming you can control when to start rendering.
//
// ·······························································
// 1) Consumer entry point, waits for polyfills before being executed.
// (Single App or multiple separate components like Header, Footer, Modal)
// ---------------------------------------------------------------
// Each consumer listens for the custom event.
window.addEventListener('polyfills-loaded', function () {
// Start up the application where imports and everything else can use polyfills
require('./startApp');
}, false);
// ·······························································
// 2) Script at the bottom of the page, right before the body closing tag.
// Loads the polyfills without blocking html render
// ---------------------------------------------------------------
// Initialize a custom event so that consumers can listen to it
var event = document.createEvent('Event');
event.initEvent('polyfills-loaded', true, true);
// Define what should happen once the polyfills have finished loading
window.__whenPolyfillsAreLoaded = function () {
console.log('Polyfills are ready. Starting up...');
// Dispatch the event to run the application components
window.dispatchEvent(event);
};
// Add a script tag to the page to start loading the polyfills
const polyfillScript = document.createElement('script');
polyfillScript.src = 'https://cdn.polyfill.io/v2/polyfill.js?callback=__whenPolyfillsAreLoaded';
polyfillScript.type = 'text/javascript';
polyfillScript.async = true;
document.head.appendChild(polyfillScript); |
I ran into an issue when creating an Outlook add-in, where
because that generates something like
I'm not finding a good way to put the file at the beginning of the |
That’s weird, cause Next usually puts their scripts at the end of the body tag. Have you try to use a |
Ah, no you're right - I misunderstood how |
To prevent blocking request in modern browsers you can add Ex: <script
src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise"
nomodule="nomodule"
/> |
You can now use https://github.com/zeit/next.js/blob/canary/server/build/webpack.js#L48 in
^ in this case you need to have |
That's great @timneutkens! I'll try it and, if I succeed, I'll add an example :) |
Thanks @coluccini! |
@coluccini , did you get anywhere with a documented example? I am trying to load an intersection observer polyfill using the clientBootstrap, but the trouble is I don't know how to hold now execution until my polyfil is completed loading. Anybody done something similar? |
Sorry, I still didn't try this new configuration option. But as I understand if you achieve to bootstrap your polyfill file like proposed, it should be executed before next.js runs. |
@coluccini, Thanks for your reply, I was kind of hoping I could dynamically import the polyfil only when it is actually needed. |
To do that I run this code inside the (function(undefined) {
if ('IntersectionObserver' in global) {
var src = ROUTE_TO_POLYFILL_FILE;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var f = new Function(xhr.responseText);
f();
}
}
xhr.open('GET', src, false);
xhr.send();
}
})
.call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {}); I'm using it on a site with a lot of user and seams to work pretty well. |
@coluccini , thanks I think i'll have to give that a go. |
Hello, We are going to remove |
I need to support some older browsers, so I would like to always include some polyfills (Intl, Object.assign and Promise) in the app bundle and just execute them if need (I've tried to do it conditionally, but it fails in a lot of situations), but I can't figure out the way to do it. Has anybody did this o has an idea on how can I do it?
The text was updated successfully, but these errors were encountered: