Giter Site home page Giter Site logo

Comments (5)

bicknellr avatar bicknellr commented on July 30, 2024 14

Hi @tconroy, the native shim can't be used compiled to ES5 because native custom elements implementations require that the HTMLElement constructor is called as a constructor from your code and only features available in ES2015 are able to do this.

Particularly, when using the native custom element implementation, your definition either...

  • is written as a class extending HTMLElement and needs to call super in the constructor:

    class MyElement extends HTMLElement {
      constructor() {
        super();
        // `this` is now the element.
        this.prop = 1234;
      }
    }
    customElements.define('my-element', MyElement);
  • or, is written as a normal ES5 constructor and needs to call Reflect.construct, use its result instead of this, and return that result:

    function MyElement() {
      // As usual, `this` does in fact exist here and it is an object
      // with `MyElement.prototype` as its prototype. However, there is
      // no way to call `HTMLElement` as a constructor while also
      // supplying the local `this` as the `this` value for the
      // construction.
    
      // To work around the above and because `super` has no meaning
      // within this function (or, more concretely, will cause a
      // SyntaxError), you must *construct* a new `HTMLElement`:
      var inst = Reflect.construct(HTMLElement, [], new.target);
    
      // Then, you can use the result as if it was `this`:
      inst.prop = 1234;
    
      // When plain functions are called as constructors and the function
      // returns undefined, the object that was automatically created to
      // become this (described in the first comment) is the result of
      // the construction. However, if the constructor returns an object
      // (primitives won't trigger this), that returned value is the
      // result of the construction:
      return inst;
    }
    MyElement.prototype = Object.create(HTMLElement.prototype);
    MyElement.prototype.constructor = MyElement;
    customElements.define('my-element', MyElement);

This ultimately means that if you give the native shim to a compiler to convert to ES5, the compiler will see the class and rewrite it in a way that it thinks is roughly equivalent - usually as a plain function with HTMLElement.call(this) acting as super(). However, because HTMLElement can only be called as a constructor in one of the ways above, anything in pure ES5 that the compiler converts it to isn't sufficient.

The native shim is only meant to wrap a native implementation of custom elements so that custom element definitions that have specifically been compiled from ES2015 to ES5 will work there. Particularly, it will break if you try to run it in a client that doesn't have a native custom elements implementation because HTMLElement won't be constructible. Also, because of how the native shim works right now, it doesn't work with ES2015 classes as custom element definitions - all definitions have to be compiled when using it. We'll look into it in the future but supporting mixed compilation situations doesn't seem to be that critical at the moment.

So, what do you actually need to do?

  • If you decide to build (you're shipping ES5 definitions):
    • If the client you're running on has a native implementation of custom elements, you need to load the native shim.
    • Otherwise, you need to load the polyfill.
  • If you decide not to build (you're shipping ES2015 definitions):
    • If the client you're running on has a native implementation of custom elements, you don't need to do include anything else.
    • Otherwise, you need to load the polyfill.

Also, you should load the polyfill separately, rather than bundling it with your app, so that clients that don't need it don't download it. If you're planning on using the full set of web components polyfills, take a look at the loader in webcomponentsjs; it checks what things are supported by the browser its running in and loads only the polyfills needed by that browser.

Here's a small example project that uses webpack and babel to compile custom elements written in ES2015 (with modules) to ES5 and load the native shim or polyfill as needed:

https://glitch.com/edit/#!/webpack-babel-custom-elements

(result: https://webpack-babel-custom-elements.glitch.me)

The document.write('<!--') thing in index.html looks pretty gnarly but it works reliably and we use it in polymer build to optionally load the native shim. I've tried out that example project in IE11, mobile Safari 9, and most auto-updating browsers and everything checks out. However, the polyfill doesn't support Safari 8 (here are the browsers it does), so I can't say whether or not it will work there.

from custom-elements.

bicknellr avatar bicknellr commented on July 30, 2024 1

Sorry, I left out an important distinction: you can definitely call a function as a constructor in ES5 - new :) - but the key part is that you can't pass new.target like you can with Reflect.construct and is implied with super(). (The HTML spec's description of HTML element constructors has the details of why you need new.target.) Also, that example might seem a bit strange since the polyfill / native shim are pretty separate from the webpack / babel stuff going on. (.. and serving directly from node_modules, I was lazy.) The point is just that the native shim and polyfill should be loaded separately from your bundle if your server isn't deciding what clients get what bundles. One thing you might want to consider doing is minifying (but not compiling) the native shim, this is also something that webcomponentsjs does (description, minified native shim).

from custom-elements.

bicknellr avatar bicknellr commented on July 30, 2024

Hmm, looks like my example was a bit too janky and started creating this nested series of folders called 'built' - just updated it with a slightly more robust build process.

from custom-elements.

treshugart avatar treshugart commented on July 30, 2024

@Hotell would what you're doing in skatejs/web-components#48 help make this simpler for consumers?

from custom-elements.

Hotell avatar Hotell commented on July 30, 2024

ofc, with new skatejs/web-components everything is handled for you and you save 35kB in worst case scenario

best case - browser with full support of WC ( if you're transpiling to es5, just es5-adapter will be loaded - 2kb )
worst case - IE11 - lazy loaded polyfills/es6 needed features -> savings 35kb against webcomponnets shims in this repo

you can try it right now

from custom-elements.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.