Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Polyfilling The Web Forward

Polyfilling The Web Forward

In this talk, we’re going to discuss polyfills and their role in moving the web forward. We’ll also talk about how Chrome thinks about the maintenance and scalability of polyfills. This deck builds on many previous articles and share-outs by others.

Addy Osmani

July 30, 2022
Tweet

More Decks by Addy Osmani

Other Decks in Technology

Transcript

  1. Proprietary + Confidential Hardware (CPU, GPU, RAM) and Network Operating

    System Web Browsers Polyfills / Transforms The Platform Foundation Applications
  2. Proprietary + Confidential Hardware (CPU, GPU, RAM) and Network Operating

    System JavaScript Frameworks & Tools Components The Platform Foundation Applications Infrastructure Web Browsers Polyfills / Transforms
  3. Google | Confidential & Proprietary Definition A polyfill is a

    piece of JavaScript code that is intended to exactly simulate a native, interoperable feature of the web platform, for the purpose of allowing website developers to use modern web features in older browsers. Polyfilling the web forward
  4. Google | Confidential & Proprietary Definition A transpiler takes the

    syntax that older browsers won’t understand (e.g. classes, ‘const’, arrow functions), and transforms them into syntax they will understand (functions, ‘var’, functions). Polyfilling the web forward
  5. Polyfills & Transforms come in many flavors Web APIs <dialog>

    Web Animations API Clipboard API ResizeObserver Custom Elements ShadowDOM CSS Container Queries CSS Scoping Paint Worklet Custom Properties Focus Visible JavaScript / ECMAScript Decorators Top level await Optional Chaining Nullish Coalescing Pipeline operator Private methods
  6. Web: ResizeObserver Polyfill \ import ResizeObserver from 'resize-observer-polyfill'; const ro

    = new ResizeObserver((entries, observer) => { for (const entry of entries) { const {left, top, width, height} = entry.contentRect; console.log('Element:', entry.target); console.log(`Element's size: ${ width }px x ${ height }px`); console.log(`Element's paddings: ${ top }px ; ${ left }px`); } }); ro.observe(document.body);
  7. CSS: Container Queries Polyfill \ const supportsContainerQueries = "container" in

    document.documentElement.style; if (!supportsContainerQueries) { import("container-query-polyfill"); } // … @container (min-width: 200px) { /* ... */ }
  8. ECMAScript Transform: Nullish Coalescing \ // before running the transpiler

    height = height ?? 100; // after running the transpiler height = (height !== undefined && height !== null) ? height : 100;
  9. standards write design doc create launch bug begin implementing roll

    out to canary/dev request beta approval request stable approval enable by default identify + understand design write it down ship standard intent to implement enable by default intent to ship if web-facing if failed intent to deprecate intent to remove remove adoption check-in landed web platform launch chrome launch web platform removal first TAG check-in external blink contributors old/existing features (2nd impl ships) final TAG sign-off grow + iterate intent to experiment if need to experiment chromium intent to implement Life of a browser feature
  10. Google | Confidential & Proprietary Polyfilling the Web forward Guiding

    principles for Web features Discuss & Design Idea discussion and Incubation. The first step is identifying a specific need, or use case. An Explainer may be written at this phase. Implement First native implementations (as trial/behind flag). Specification should be iterated on. Web Platform Tests. Interop Multiple interoperable implementations across browsers. Universal support Stable support for the feature across all major browsers and platforms. Polyfills bridge this gap
  11. Privileged & Confidential Who are polyfill stakeholders? • Polyfill authors?

    ◦ Browser engineers ◦ Web developers ◦ Framework authors ◦ Web infrastructure ◦ Community • Website developers • Library and framework authors • Polyfill distributors (eg. polyfill.io) • Spec editors
  12. Privileged & Confidential Guidance for browser engineers • If you

    are developing a new feature for the web, polyfills can be hugely beneficial in helping to roll out that feature. • Don't be constrained by what is 'polyfillable' [discuss?] • Make your feature easily detectable • Work with polyfill authors Polyfills and the evolution of the Web Platform
  13. BigInt // arbitrary-precision integers \ 1234567890123456789 * 123; // →

    151851850485185200000 ❌ 1234567890123456789n * 123n; // → 151851850485185185047n ✅ // Originally, BigInt was only supported in Chrome. // Hard to transpile to ES5 with Babel et al. Changes behavior of operators (like +, >= etc).
  14. JSBI for BigInt \ import JSBI from './jsbi.mjs'; const max

    = JSBI.BigInt(Number.MAX_SAFE_INTEGER); const two = JSBI.BigInt('2'); const result = JSBI.add(max, two); console.log(result.toString()); // → '9007199254740993'
  15. Privileged & Confidential Can everything be polyfilled? No. • Transpilers

    and polyfills can’t do everything. • A good general rule is this: ◦ If it’s new syntax, you can probably transpile it ◦ If it’s a new object or method, you can probably polyfill it ◦ If it’s something clever that the browser does outside of your code, you may be out of luck.
  16. Privileged & Confidential Don’t serve unnecessary polyfills • It is

    generally better to optimize for modern browsers, so performing efficient client-side feature detection and waiting for an extra script to load on older browsers is usually a good trade off. However, if the full set of polyfills you might need in the worst case constitutes a negligible overhead, then you could choose to serve the full set to all browsers. • Polyfill authors may choose to throw a warning if a polyfill is loaded when not needed, though care should be taken not to create unnecessary noise. If unnecessarily loading a particular polyfill creates a significant performance or security concern, a warning is appropriate.
  17. Privileged & Confidential Loading polyfills as needed • polyfill.io is

    a service that inspects the browser’s User-Agent and serves a script with polyfills targeted specifically at that browser. ◦ The polyfill.io script will add 50-300 ms to your Time to Interactive. The script is (obviously) hosted on a server different from yours, and loading stuff from a different server is costly. The browser will have to spend extra 50-300 ms to setup a connection to the server, and this means adding 50-300 ms to your Time to Interactive. • module/nomodule is a pattern when you serve scripts for modern browsers with <script type="module">, and scripts for older browsers with <script nomodule>: ◦ This pattern relies on the fact that old browsers – ones that don’t support ES2015 – will not load type="module" scripts – and will load nomodule ones. Which means you can use nomodule to serve ES2015 polyfills exactly to browsers that need them! • @babel/preset-env has an option called useBuiltIns. With this option, you can make Babel cherry-pick polyfills for specific browsers
  18. module/nomodule \ <!-- Full polyfill bundle for old browsers -->

    <script nomodule src="/polyfills/full.min.js"></script> <!-- Smaller polyfill bundle for browsers with ES2015+ support --> <script type="module" src="/polyfills/modern.min.js"></script> <!-- Bundle script. `defer` is required to execute this script after the `type="module"` one --> <script src="/bundle.min.js" defer></script>
  19. Risks of premature polyfilling • Early, speculative polyfills help shape

    the standards process. However, any JavaScript library that defines a property of the global object or extends a prototype of a global constructor using a proposed or generically useful name, risks creating problems for the development of the Web if that library becomes widely used, prior to the standardization and implementation of the feature it seeks to create or emulate. • EXAMPLE: Mootools Array.prototype.contains => .includes ◦ The standardization of Array.prototype.contains in JavaScript ran into problems. • Polyfill authors can avoid these problems if their version of the feature can be used under a custom name, regardless of the existence of any native implementation of the same or similar feature under a different name.
  20. Things that are hard to get right: performance • The

    runtime nature of more complex polyfills can sometimes mean their performance in browsers is significantly different to browsers that natively support a feature, so this is always worth keeping an eye on. • Historically, developers used solutions like Modernizr and Yepnope to conditionally load polyfills in supported browsers, while in modern times developers often take a lighter weight approach to checking (or defer to configuration steps like present-env and conditional polyfill loading to handle such checks for them). • As the number of web platform features increases, the amount of polyfill code required to make website developers' application code run in older browsers may become extremely large - in some cases exceeding the size of the application code that depends on it. This has implications for performance, both in terms of parsing the polyfill code, and the time required to download it. • Consideration should be given to whether excessively large polyfill bundles place a punitive cost burden on users with the least ability to pay that cost: those with older devices which may be on metered connections paying for data in very small increments.
  21. Look for signs of quality in polyfills, such as: •

    Comprehensive test suite, especially if it includes relevant Web Platform Tests • Effects on the performance profile of your site, measured using in-browser profiling tools or other auditing or monitoring tools or services Additional recommendations for polyfill authors Understand stability Some features are not worth polyfilling. Instead, consider: • Progressive enhancement • Transpilation (eg. Babel) • Lo-fi mode (aka. "Basic" mode, "m." etc) Consider the least capable devices
  22. Guidance for library authors on shipping polyfills yourself • In

    deciding whether to ship polyfill-like code inside your library, you should consider questions such as: ◦ How much of the feature are you using? If you want a large feature but your use of it could be emulated in naive 10-line implementation, then it's better to do that than to bundle a spec-compliant polyfill. ◦ How large is the polyfill code relative to your library code? If it's a tiny polyfill (such as for a Number.isNaN implementation) then the overheads are negligible and not worth any concern. However, in some cases a polyfill might exceed the size of the library. ◦ Are there multiple polyfills available? If developers have a choice of polyfills for this feature, you may want to choose the one that works best with your library. If there's only one canonical option, it's easier to assume that the developer will use it. ◦ Does your library provide a good experience only when the feature is available natively? If a polyfill allows your library to work but in an unacceptably slow or buggy way, it probably should not ship as part of the library. Polyfills and the evolution of the Web Platform
  23. Privileged & Confidential Further reading • “What is a Polyfill?”

    by Remy Sharp • Polyfills and the evolution of the Web Platform was influential. • Polyfills are a part of the web • Inspiration for the term replica: The Eiffel Tower in Las Vegas • Useful clarification of “polyfill” and related terms: “Polyfills and the evolution of the Web”. Edited by Andrew Betts. • Polyfills, shims and Ponyfills