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
    2022
    Polyfilling the Web
    Forward
    [email protected]

    View Slide

  2. Proprietary + Confidential
    The modern web is
    composed of
    layers.

    View Slide

  3. Proprietary + Confidential
    The Platform
    Foundation
    Applications

    View Slide

  4. Proprietary + Confidential
    Hardware (CPU, GPU, RAM) and Network
    Operating System
    The Platform
    Foundation
    Applications

    View Slide

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

    View Slide

  6. Proprietary + Confidential
    Hardware (CPU, GPU, RAM) and Network
    Operating System
    JavaScript Frameworks & Tools
    Components
    The Platform
    Foundation
    Applications
    Infrastructure
    Web Browsers
    Polyfills / Transforms

    View Slide

  7. 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

    View Slide

  8. 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

    View Slide

  9. Polyfills & Transforms come in many flavors
    Web APIs

    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

    View Slide

  10. 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);

    View Slide

  11. CSS: Container Queries Polyfill
    \
    const supportsContainerQueries = "container" in
    document.documentElement.style;
    if (!supportsContainerQueries) {
    import("container-query-polyfill");
    }
    // …
    @container (min-width: 200px) {
    /* ... */
    }

    View Slide

  12. ECMAScript Transform: Nullish Coalescing
    \
    // before running the transpiler
    height = height ?? 100;
    // after running the transpiler
    height = (height !== undefined && height !== null) ?
    height : 100;

    View Slide

  13. Tl;dr: Polyfills are an important layer.
    Polyfills / Transforms

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. Proprietary + Confidential
    Polyfills are often more
    complex than we might
    guess.

    View Slide

  19. 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).

    View Slide

  20. 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'

    View Slide

  21. 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.

    View Slide

  22. 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.

    View Slide

  23. Promises - in many browsers since 2014

    View Slide

  24. Promises - still being polyfilled in 2022

    View Slide

  25. One of many features still being polyfilled

    View Slide

  26. 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 , and scripts for older<br/>browsers with <script nomodule>:<br/>○ This pattern relies on the fact that old browsers – ones that<br/>don’t support ES2015 – will not load type="module" scripts –<br/>and will load nomodule ones. Which means you can use<br/>nomodule to serve ES2015 polyfills exactly to browsers that<br/>need them!<br/>● @babel/preset-env has an option called useBuiltIns. With this<br/>option, you can make Babel cherry-pick polyfills for specific<br/>browsers<br/>

    View Slide

  27. module/nomodule
    \






    View Slide

  28. 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.

    View Slide

  29. 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.

    View Slide

  30. ShadowDOM polyfill vs ShadyDOM

    View Slide

  31. 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

    View Slide

  32. Proprietary + Confidential
    There is a lot that
    library authors can
    keep in mind.

    View Slide

  33. 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

    View Slide

  34. Proprietary + Confidential
    Discuss

    View Slide

  35. 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

    View Slide