A polyfill (or polyfiller) is a piece of code (or plugin) that provides the technology that we, the developers, expect the browser to provide natively.
Transpiler bzw. Compiler wie babel machen es heutzutage einfach modernes JavaScript zu schreiben. Diese compilieren z.B. moderne ES 2015+ Syntax (z.b. let
, const
oder arrow-functions) in eine JS-Syntax, die auch ältere Browser interpretieren können. Doch neue Browser bringen nicht nur das Verständnis für moderne Syntax mit, sondern auch neue Methoden, die nicht von alten Browsern unterstützt werden.
Mit Babel lässt sich ES6 zu kompatiblen JavaScript transpilieren
Um hier keine Fehler zu machen haben wir nur zwei Optionen:
-
Wir schreiben Code der in allen Browsern, die wir unterstützen wollen, nativ funktioniert
-
Wir transpilieren unseren Code und binden Polyfills ein
Option 1) kann unter Umständen sehr schnell komplex werden und man muss sich an Syntax und Methoden halten die in Zukunft u.U. an Relevanz in der Entwicklung verlieren werden.
Polyfills sind also ein sinnvoller Schritt für eine vorwärtsgewandte Entwicklung.
Ein gängiges Beispiel für einen solchen Polyfill ist babel-polyfill. Dieses Node-Package bringt diverse Methoden wie zum Beispiel Object.assign
(zum verbinden mehrerer Objekte) in den Browser.
Diese Polyfills lassen sich in der Regel leicht einbinden. Doch es gibt ein Problem: In vielen Fällen werden die Polyfills einfach geladen, ohne darauf zu achten ob der Browser diese Methoden schon unterstützt oder nicht. Das bestraft die User in modernen Browsern, welche die neuen Methoden verwenden, da so zusätzlicher Code geladen wird, welchen der Browser gar nicht zum darstellen der Seite benötigt.
Im besten Fall sollten Polyfills auch nur dann geladen werden, wenn sie auch benötigt werden.
Leider ist das nicht ganz einfach.
Möchte man jeden Fall einzeln betrachten, steigt die Komplexität erheblich: Unterstützt ein alter Browser zwar Funktionalität x aber nicht y, ein anderer wiederum y aber nicht x und noch ein weiterer beide nicht, erzeugt das ein schnell komplex werdendes Konstrukt an Abfragen.
Die Entscheidung welche Polyfills eingebunden werden, sollte deshalb wenn möglich mit der Nutzungsstatistiken der Seite einher gehen. Haben wir eine Seite die hauptsächlich im IE11 aufgerufen wird, sollte man wohl möglich lieber auf Polyfills verzichten. Bei der Entscheidung ob Polyfill oder nicht sollte man sich deshalb an der Mehrheit der Nutzer orientieren.
Lösungsansätze
Ein beliebter Lösungsansatz Polyfills einzubinden ist polyfill.io.
Polyfill.io ist eine einfache aber mächtige Möglichkeit Polyfills einzuinden
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
Mit Hilfe eines CDN-Scripts passiert genau diese Magic: es werden nur die Polyfills geladen die für den Browser auch benötigt werden.
An dieser Stelle könnte man schon fast aufhören, denn polyfill.io ist echt gut. Jedoch sollte man sich über die Vor- und Nachteile von CDN-Diensten bewusst sein. CDN-Dienste können mit wenigen Round-Trips die Performance heben, haben aber auch theoretisch die Möglichkeit Informationen über deine Seite zu sammeln. Insbesondere in Zeiten wo Datenschutz durch GDPR an Relevanz gewonnen hat, sollte man sich deshalb Gedanken machen, ob man so ein CDN-Script einbinden möchte.
Allerdings kann man den Polyfill-Service von polyfill.io auch selbst hosten.
Dennoch hat das Einbinden des Scripts von polyfill.io einen Nachteil, denn der Browser hat einen zusätzlichen HTTP-Request, egal ob er die Methoden der Polyfills unterstützt oder nicht.
Aus diesem Grund hier noch ein weiterer Lösungsansatz, wie man sich selbst um die Einbindung der Polyfills kümmern kann.
Ohne einen Dienst wie polyfill.io ist es kaum möglich das genaue unterstützte Featureset der unterschiedlichen Browser selbst abzubilden, um dann genau die richtigen Polyfills für den jeweiligen Browser zu laden. Deshalb macht es Sinn sich vorher darauf festzulegen allen Browsern die unser Featureset an modernen Methoden nicht unterstützen, auch alle unsere Polyfills zur Verfügung zu stellen.
So wird zwar unter Umständen in einem alten Browser ein Polyfill mehr geladen, als benötigt wird, dafür müssen die modernen Browser keinen zusätzlichen Code laden.
Um die Unterscheidung zwischen neuem und alten Browser des Clients zu machen benötigt man eine Funktion die das JavaScript der Seite initialisiert.
In unserem Beispiel ist das die mainInit()
Funktion.
In dieser werden wiederum alle notwendigen Funktionen aufgerufen die auf der Seite benötigt werden. Das gibt uns die Kontrolle unser JS zu einem von uns bestimmten Zeitpunkt auszuführen, anstatt, dass es direkt nach dem Ausführen des Script-Tags ausgeführt wird.
main.js
import initSlider from './functions/slider';
function mainInit() {
initSlider();
}
// Write the function on window to make it available globally
window.mainInit = mainInit;
polyfill.js
import 'babel-polyfill';
Am Ende des <body>
-Tags binden wir die main.js
ein (da dort der DOM schon gebaut sein sollte).
In einem Script prüfen wir ob unser Browser die entsprechenden Methoden unterstützt oder ob wir dafür einen Polyfill benötigen.
Wichtig ist, dass der Polyfill geladen wird bevor das JS in der main.js ausgeführt wird, da wir den Polyfill für das darauf folgende JS benötigen. Deshalb wird die mainInit()
Funktion erst nach erfolgreichem Laden des Polyfills ausgeführt.
index.html
<!-- Import optimized main JS file with init-function -->
<script src="main.js"></script>
<script>
/* Initialize all JavaScript
** use polyfills only if needed
**/
// Detect available methods
var modernBrowser = (
'assign' in Object
);
if ( !modernBrowser ) {
var scriptElement = document.createElement('script');
scriptElement.async = false;
scriptElement.src = '/polyfill.js';
// Execute main init function after polyfills are loaded
scriptElement.onload = function() {
mainInit();
};
document.head.appendChild(scriptElement);
} else {
mainInit();
}
</script>
Fazit
Sinnvolles Polyfilling ist wichtig und notwendig.
Die einfachste Variante für passende Polyfills zu sorgen ist wohl polyfill.io. Bei Bedarf sogar selbst gehostet.
Möchte man auf diesen zusätzlichen HTTP-Request verzichten, bietet sich das selbstständige Einbinden der Polyfills (wie oben beschrieben) an.
Happy polyfilling.
Inspiriert wurde dieser Beitrag insbesondere von einem Artikel von Philip Walton:
https://philipwalton.com/articles/loading-polyfills-only-when-needed/