Hacker News new | past | comments | ask | show | jobs | submit login
The broken promise of Web Components (dmitriid.com)
149 points by dmitriid on Mar 15, 2017 | hide | past | favorite | 147 comments



The assertions about the DOM are long on hyperbole but rely on e.g. benchmarks from a decade ago and broad hyperbole like:

“This was cumbersome. This was limiting. You could not prototype easily and quickly. You could not easily escape the limitations of UI libraries. etc. etc. etc.”

It would have been far more interesting to look at whether the broadest claims were even true, or why people ran into problems using various libraries. Every time I've done a test, React is significantly slower than using standard JavaScript + DOM but that's the wrong question to ask when you really care about whether it's a) fast enough and b) helping you write code faster, reducing maintenance or sharing costs, etc.

All I got from this is that using React makes life easier than calling the DOM APIs directly every time, which is technically true but doesn't say anything about e.g. how WebComponents compare when using abstraction following the best practices of software developers since something like the 1950s.

The “easy-peasy” example is:

    ['Hello', 'world'].map((text) => {
        return <p><span>{text}</span></p>
    })
The alternative given is:

    ['Hello', 'world'].forEach(text => {
    	const p = document.createElement('p')
    	const span = document.createElement('span')
    	span.textContent(text)
    	p.appendChild(span)
	
    	MyComponent.appendChild(p)
    })
Since the first example requires 140KB of React code plus a ton of setup code to get to that it's either careless or dishonest to compare the two without acknowledging the possibility of using a helper function or two to simplify the second one, as JavaScript developers have been doing routinely for multiple decades.

Again, not a slam on React, just this kind of unhelpful advocacy. It'd be a lot more interesting to compare real examples with multiple libraries and talk about how the different choices impact debugging, performance, isolation, etc.


Yes, it's like with JavaScript itself, it had some issues in the past, all the big issues were resolved but the stigma is still there.

I can't see any fundamental reason why generating the DOM with React would be faster than generating the DOM with Web Components - They're doing the same thing; it's a matter of time before they even out (if they haven't already).

I think that Web Components would end up faster ultimately due to native browser support (more room to optimise).


> JavaScript itself, it had some issues in the past, all the big issues were resolved

What about automatic semicolon insertion (ASI)? This makes it impossible to internalize how the JS implementation will interpret your code without memorizing a bunch of special cases, and makes "newline" semantically significant even if you only intend it for formatting your code.

It's never going to be fixed because of backwards compatibility. I can put up with a lot of language peculiarities, but I don't like wondering if some random "return" statement in my code is going to misbehave because of extra whitespace.

This is enough reason for me to keep looking for an ideal language.


Possibly embrace the fact that semicolons are noise, and treat JS like you would Ruby or other line oriented languages??? (ignoring the fact that JS will "look ahead" past an otherwise complete line - e.g. - return [nothing] followed by open curly brace - but Ruby for example won't)

Remember the bad old days of BASIC? You only needed that extra delimiter (colon, rather than semi) when you were going to put multiple statements on one line :-)

Yeah, minifiers are often too dumb to accept valid input text :-(


> Possibly embrace the fact that semicolons are noise, and treat JS like you would Ruby or other line oriented languages???

I'd be happy to treat JS as "statements end with a newline, not semicolon", if that were consistently true.

The paradox of Javascript: Do statements end in a semicolon, or newline? If you answer either way, you're sometimes wrong. As far as I can tell this is the only misfeature of JS that is really unfixable.


The React documentation says it so it has to be true. DOM slow, VDOM fast. Facebook is out there buying developer mindshare.


We don't claim that in React documentation. React can't be faster than the same DOM mutations written by hand because by definition it has to do more work.

We do think it helps to create maintainable apps though, and it is fast enough for practical use cases despite its immutable design. That's why we use it at Facebook.


I just haven't been able to figure out what, exactly, it is about manual DOM mutations that makes an app unmaintainable. As long as your code is organized and modular, it's always been easy enough for me to navigate through and maintain a non-React codebase.


I found these things hard:

* Inconsistencies or duplicated code between initial render and updates.

* Having to write code for N^2 transitions between N valid states instead of N valid states themselves.

* Harder to ensure DOM operation order across different parts of the codebase doesn’t produce jank.

Sure, this is all possible to solve, but in my experience I just ended up with a crappy version of React.


Seconded on all counts. We also ended up independently implementing a really simplistic and in many ways broken version of React for these reasons and many others.


Probably finding the right place of the dom to change and keeping track of what to change. React basically says "make it like this" and takes care of a lot of things.

jQuery code is well known to be hard to maintain when it changes things based on element-types, classes and id's nested in each other.


It's quite simple to handle that situation with jQuery and modules. If many parts of you code need to make a change to the DOM in the same place, you wrap the DOM changing in a module.

  var widget = require('./widget');
  widget.foo('bar');
And the widget handles the jQuery'ing for you.

  module.exports = {
    foo: function(s) { $('.widget').text(s) }
  }


Only:

- it breaks immediately if there is more than one '.widget' on the page

- your app will get out-of-sync DOM updates because different parts will flush changes to the DOM at different times (React is DOM = f(state, time), so if multiple changes happen in f, they all be flushed to DOM in one go)


In my experience it's the needed complexity added by bookkeeping and the coupling that requires to do rendering/updates and data binding using manual DOM manipulations in a performant way, especially in larger scale apps. You can easily write maintainable and simple applications that manipulate the DOM directly if you don't care about performance, or in some cases correctness, and you'll quickly do worse than React would, from what I've seen.


> Yes, it's like with JavaScript itself, it had some issues in the past, all the big issues were resolved but the stigma is still there.

Honest question, because I use Javascript just infrequently enough to have to look this up every time (and it's possible the reported solutions are old), is there a good concise universal way (or two, at most) to determine the type of the variable I may have? It seems it's always a tossup between typeof, instanceof, == null and == undefined, and to my eyes that's way too many.


As far as I know, `typeof` works on everything, but for Objects will only yield "object". You then should use `instanceof` to determine the type of object, if necessary. Typically, once I know it's an object I just use it, so that people can provide "non-traditional" but otherwise valid implementations. Only exception is if I'm specifically looking for a Date or DOM Node or other built-in type.

EDIT: Of course, I forget that arrays also have `typeof` "object"... Again, probably best to just start using length and indexing and hope for the best.


A somewhat more succinct option could be to coerce to Object and then use instanceof: 'Object(x) instanceof String'. Or you could use Object.getPrototypeOf, which coerces to Object automatically these days. But null and undefined are special: calling Object on them returns {}, and calling Object.getPrototypeOf on them throws.

If the type you're testing for is not one of the builtin literal types, you can just use instanceof.


"Object(foo) instanceof Class" does seem to be the most definitive, but unfortunately seems to require specific testing against a literal that must exist or it throws, and doesn't work for null/undefined. That at least brings the things I need to know to check the type of something down to three, two of which are singular special cases (if somewhat common ones), so that's better and possibly adequate, if not what I would consider good. Then again, I'm not sure I'm capable of thinking it could achieve good while there exists both an undefined and null in the language. It still boggles my mind that it has both.


To clarify, by "literal that must exist or it throws" you mean that you don't know whether the class you're testing for exists? Testing against an invalid class would be an error in most languages…

I guess an alternative is foo.constructor and foo.constructor.name, which will give you a string if you want that.


Since many different js libraries may be loaded from many different sources, I could envision a situation where some utility functions may want to support different inputs depending on what's passed in, and what's passed in might depend on what's loaded, and what's loaded can change at runtime.

That's why many dynamic languages allow access to the type through a string representation.


Not really. Also, typeof doesn't work at all on object literals.

Best just to see if it quacks, or barks :-)

(i.e. - does the object define a given function/"method")


except it still doesn't have support for modules in 2017.


Also, for whatever it's worth, the js could be written more concisely as

  ['Hello', 'world'].forEach( text => MyComponent
      .appendChild(document.createElement('p'))
      .appendChild(document.createElement('span'))
      .textContent = text
  )


This doesn't really help you with updates at all, only with initial mounting. In React, elements describe the current tree, and React figures out how to apply changes between the trees.


Argh. I should learn to read/comment at once, and not split replies into multiple chunks :)

> it's either careless or dishonest to compare the two without acknowledging the possibility of using a helper function or two to simplify the second one, as JavaScript developers have been doing routinely for multiple decades.

To quote from the article:

--- start quote ---

DOM APIs are horrible, cumbersome, awkward and clunky. Polymer and others are bravely trying to use DOM APIs only, but even they resort to innerHTML anywhere they don’t have to put on a show (tests, for example). When Web Components take root, the web will be flooded with less performant innerHTMLs and possibly re-implementations of snabbdoms and virtual-doms (obviously incompatible)

--- end quote ---


Again, why is the scary second half of that sentence an absolute certainty or bad? There's no law requiring use of innerHTML, and it's been easy to beat React on raw performance in many cases (to their credit, that's been getting harder but that's always a challenge for a general purpose library) — and in most cases none of that matters because you're not triggering updates fast enough to care. Ease of use and all of the other concerns are far more important for most people.

Similarly, if people are building reusable components it's not clear why using different virtualdom libraries or not using one at all is a problem. Doesn't sound software engineering practice recommend a well-defined public interface rather than deeply interconnecting them like that? Besides, this is the web: what are the odds that you wouldn't quickly hit a problem where component X depends on v1.2.3 but component Y requires v2.3.4?


> Every time I've done a test, React is significantly slower than using standard JavaScript + DOM

I'd be interested in seeing those tests.


I also curious to see how web components will be faster than simple javascript objects :)

React apps are usually built with many layers of components that returns another components. And with standard Web API, there will be significantly more DOM nodes because each component is a DOM node.


Try beating a loop where e.g. every time you get a change you do something like getElementById() + .textContent = newValue. There's no way for React to be faster than that because it has to eventually do the same work but there are many ways for it to be less code, more correct (e.g. by not forgetting to call the update in some cases), or otherwise easier to get to the point where your app can ship.


Someone has tried beating the loop. And more than a loop, but rather a more real-life-like test rendering multiple components and component trees, sets attributes etc. etc. and you can run it on your own computer in the browser

You can check it out yourself: https://localvoid.github.io/uibench/

I highly recommend the author's article about it: https://medium.com/@localvoid/how-to-win-in-web-framework-be...


Also, it seems that many people are defending WCs with an argument about interoperability. But in reality, if we start adding components that are using different frameworks, there will be problems with layout thrashing, etc, because there is no standard scheduling APIs, and each framework implements its own scheduler to batch reads and writes https://github.com/ampproject/amphtml/blob/8a76ee942f329179d...


Yes. If you're already checking for changes yourself then you've basically rewritten React.


Look at the tests for inferno, it is close to js + dom for some things and inside 2x factor for bascially everything if I remember correctly. The main thing is development speed anyway, fast enough while still easy to write and maintain code.


This example shows how cumbersome the DOM APIs and their limitations are. Also, React is not _just_ DOM diff-ing. It also provides a host of other benefits... some of which are both shown here and covered immediately in the next set of examples with Polymer's kinda-templating-language.

You cannot buy into Web Components without buying into at least Polymer to supply you with data binding.

If you don't like React's size, there are slimmer options: Inferno, Preact etc.


> You cannot buy into Web Components without buying into at least Polymer to supply you with data binding.

Why? You can use Polymer and not its data binding. In fact you can use its polyfills and deal with mostly the standard based APIs. X-Tags and many others also use Polymer's polyfills.

DOM APIs are awkward as hell. But you can create Web Components and handle the data binding yourself. It's actually pretty easy to roll your own data binding. Now whether you should is another matter.


They're not even "its polyfills", the polyfills are a completely standalone project that Polymer depends on.


If you look at the commits / maintainers to the Polymer repos and the WebComponents polyfills, you'll see that it's the same people.


Depending on your use-case you can use a variety of options for data-binding including 1) No data-binding at all (small components), or 2) An HTML-inspired data-binding solution like what Polymer provides or 3) A virtual-dom library like Preact or gasp even React


You don't have to use the DOM API for everything as in your example. JSX is very much virtualized template tags which are HTML standard.

> This gets progressively worse as your components grow in complexity. Imagine adding a span around the text in p?

In this case you simply type in the span inside the template tag similar to what you have in the React JSX and then just importNode the contents into your webcomponent.


> JSX is very much virtualized template tags which are HTML standard

Nope, it's not. It's a thin XML-like DSL which translated into Javascript calls: http://bit.ly/2nC6fdu


> You cannot buy into Web Components without buying into at least Polymer to supply you with data binding.

This is completely and utterly false. What are you basing your assertion on?


As an example, you can't even build a simple ToDo List without Polymer's weird data-binding syntax: https://github.com/PolymerLabs/todo-list/blob/master/app/ind...


So you link to the Polymer TodoMVC example to show that it uses Polymer's data-binding, and this is supposed to show... what exactly?

SkateJS's TodoMVC uses JSX[1]. Does that also imply that all Web Components need to use JSX? I don't see any logic in your claims.

[1]: https://github.com/skatejs/todomvc/blob/skatejs/examples/ska...


I don't follow this logic. Because a Polymer example shows it using Polymer's data binding therefore you "cant even build a simple ToDo list without" it? That just isn't true at all. In fact you never have to be declarative.


> you can substitute your favorite library: Inferno, Preact, Vue, snabbdom, virtual-dom (or non-js libraries and frameworks like Elm, Om, etc.). Similarly, replace Polymer with Vaadin, or X-Tag, or…

It's gotten to the point where if you list JS frameworks, you can slip in 2-3 that don't exist and I don't think anybody would notice.


It's gotten to the point where if you list JS frameworks, you can slip in 2-3 that don't exist

No, it's gotten to the point that any attempt to do so is doomed to failure. ;)


I only recognized... I guess 3 of these. A bunch of them could be total nonsense and I have no idea.


Some of them I knew or I thought I knew. They came up when I was "researching" for the article :)


But those are all real frameworks.


And even if they weren't when the grandparent post was written, they are by now.


This article is trash. In retrospect it was a huge waste to coin the term "web component", by doing so the expectation is that web components eliminate all need for 3rd party JS libraries, which as the article points out, they don't. Instead "web components" is a set of low-level libraries that provide bits and pieces of stuff needed to make components.

The article makes the usual criticism that web components doesn't have a databinding spec. It's true, it's a short coming. But the lack of a databinding spec doesn't make Shadow DOM any less useful. The two are unrelated.

The author really likes JSX, but didn't bother to google "jsx web components"; if he had he might have come across this: https://skatejs.gitbooks.io/skatejs/content/

There are some real things to criticize about web components. Shadow DOM makes it hard to theme your app. Custom elements has some warts in regards to the way that some events are composed. But the article doesnt make these criticisms.


Yep, the criticisms are just bad. "Attributes are strings", seriously? Uh, DOM elements in the browser have these things called JavaScript attributes…

> Shadow DOM makes it hard to theme your app.

Polymer uses CSS custom properties for this and it's not even hard, it's pretty cool, it's like you're making a CSS API for theming <your-element>.


> Uh, DOM elements in the browser have these things called JavaScript attributes…

Could you please link to a reference? I've never heard of JavaScript attributes and I can't find any reference on MDN.


I think that person is talking about the HTMLElement interface is essentially a JS object and how you can set properties on it. e.g.

  element.foo = 123;
vs

  element.setAttribute('foo', '123');


Exactly. I accidentally wrote "attributes" instead of "properties" :D


My takeaway is that web components are purposefully low-level (as specs out of the W3 usually are). It doesn't have a databinding spec because it's not supposed to. That's why libraries like Polymer emerged, to make the features more streamlined to use. The apples-oranges comparison from the article is similar to comparing jQuery to vanilla DOM.


Yeah, the author seems a little crazy about web components being approved. React and JSX are still good and they're not instantly disappearing or anything.

> Shadow DOM makes it hard to theme your app

The styling is a little different from normal but not too bad after getting the hang of it. There's usually a few components for colors, fonts, and common app styles (see paper-styles [1]). A few other components can provide CSS helper classes (e.g. Polymer's spin on flexbox [2]). CSS properties make tweaking component styles pretty easy, which makes the components more reusable. There are probably other styling tricks that'll pop up over time too.

(you don't have to use Polymer, I picked it because it's what I'm most familiar with)

[1] https://www.webcomponents.org/element/PolymerElements/paper-...

[2] https://www.webcomponents.org/element/PolymerElements/iron-f...


My team is using web components, polymer 1, 2, React, Vue and Riot in 3 strategic projects.

I have a bigger rant about web components, and polymer and interesting enough, I believe - none except one - of the article owner's comments matter much.

Here's another rant and what currently sucks at web components (for us)

1. Still no adequate browser support coverage (OP's point) - Google polymer team keeps the mood up, is excited and does great things but we're still waiting

-- for Firefox to support it, (which declined to implement html imports). I kind of think Mozilla is sabotaging the efforts for some political reason after reading their comments.

-- for Safari 10.2 to support it natively

-- for Edge to care.

Unless all the browsers support the v1 spec, we end up being just another JS framework and the Polyfill webcomponent-lite.js itself becomes a framework in competition with vue, react, riot, and others.

2. Web components (especially) polymer ones are not a good citizen inside an HTML page next to others.

-- Due to component scoping (which is ok), style scoping in the main document css (custom style) (which is weird) and DOM and shadow dom, you end up feeling hopeless at some point.

-- I just want to use the paper-toggle and I end up downloading 100s of files.

3. The opposite of "it just works" ( as someone wrote somewhere, kind of, like you invented the opposite of it just works.)

- Polymer, and the paper, iron and other libraries are cool, but they somehow fail silently and often. It's hard to figure out what went wrong.

4. Polymer team is trying to replicate the cool other JS tech and tools, and it just lags behind. (ES6 in polymer 2.0, the still beta app router, bower, gulp and OMG.js ....)

5. The iron, and then paper, and other elements are too heavily interdependent and no other leaner library or collection appears in sight.

- A lot of nested imports

- and the hopeless the wait for HTTP/2 and the future web of 2020s.

I will write later what can be done.


For web components to still win I see:

1. A very lighweight set of touch-first self-contained UI components collection

Carousel, navigation bars, card, switch (toggle), a range slider, calendar/time picker, badge, rating and some. They must be self-contained and light-weight. All the set of iron-, paper-, vue, (material, mintui, and even Bootstrap set available.

It must be so easy, and so cooperative that WordPress themes must be able to use those components with 2 lines of imports. If the old web starts using it, not the mobile app web, it ll be easier to force other browsers to support it natively.

2. Full browser coverage and elimination of polyfill.

Next to Chrome/Opera and Chromium++, Safari 10.2. Then HTML import support issue (ES6 modules?) with both Safari and Firefox remain as still big obstacles as well as Edge's uncertain future support/

3. Mobile App-like components and features only for the advanced apps,

- Router, caching, service workers.. - Better debugging and error messages. - Better failover. - Here competition with angular, vue, react, riot and others seems hard to win, and this must be a separate battle.


One of the main problems I have is that, well, if buy into Polymer, that's what you buy into: yet another vendor lock in. Otherwise you wouldn't even be able to write a simple Todo List: https://github.com/PolymerLabs/todo-list/blob/master/app/ind... (all the {{}}'s etc.)

And then... They are really not different from React/Angular/<add your own> :)


It's completely different. With Web Components you can start migrating away by using components just not written in Polymer.

How is that anything like React or Angular that really don't let you interop with other libraries out of the box?


Could you clarify what you mean by "don't let you interop"? React gives you a DOM node so you can render anything into it, including an Angular component, a jQuery plugin, or a web component. How is that not interop?


It looks like you are saying only Chrome supports this (firefox, safari and edge all currently don't). Maybe instead of blaming everyone else, it's worth looking at why no-one other than Google seems to want to implement this.


Everyone wants to implement it (and is implementing it!), with the exception of HTML imports, which are likely going to get dropped from Chrome eventually.


Are HTML imports not saved by widespread http2 adoption, where the imported pages are pushed to the client upon initial page request? No reason to throw it out completely.


They are killed by ES6 import spec.

So, "declarative custom components" have fully devolved into "JS-only framework that pretends to be DOM and adds weird limitations"


It's not quite so clear cut, AFAIK, rather wanting to wait and see how ES6 imports end up getting used rather than speccing and implementing something similar for HTML imports.


Thank you!


Web Components are damned awkward because they extend the damned awkwardness of the DOM rather than address it.

DOMNodes are these hot data structures when they're linked into the DOM tree. Writing (and even sometimes reading) from DOMNodes can cause whole page reflows or paints to occur, sometimes synchronously. The advice to "touch the DOM as little as possible" stems from the fact that the DOM has so many performance pitfalls.

The DOM is so damned awkward that it's faster to maintain a virtual DOM tree, diff changes to it on the next tick with those of the previous, and then only modify the real DOM tree with those changes like React does. Couple that to the fact that React's DOM update strategy is tuned for speed and they can hide/handle most weird DOM object creation/insertion rules, and overall you wind up with a less awkward system.

If I compose my page entirely out of Web Components, the chief benefit I see is good looking markup representative of what's on the page.

If I were to have designed a component system from DOM, I would have added a lightweight iframe-like tag that could be instantiated from a script/css/markup template package. Performance-wise, I thought that having a nested element's box be laid out and painted independently from the parent page could mitigate some of the global ripple effects of touching the DOM. Of course, this lite iframe's box itself would need to live in the parent page and its dimensions would have to factor into the page flow, and that could have global effects. I suspect there could be clever JavaScript or event system additions that would help limit the reflow and paint damage there.


> The DOM is so damned awkward that it's faster to maintain a virtual DOM tree, diff changes to it on the next tick with those of the previous, and then only modify the real DOM tree with those changes like React does.

Sorry, but it's not. I don't know what it is that you think React does, but it makes those same awkward DOM API calls. React is an abstraction layer and abstraction has a price. Having React call .appendChild is not faster than you calling .appendChild yourself.


The strength of using a virtual DOM as an abstraction isn't that you get around making DOM API calls, but that you can easily batch and sequence them to eke out the best performance. To use your .appendChild example: if you iteratively call .appendChild the browser has to reflow every time that happens, whereas if you know you have a bunch of elements to add you can use another strategy (like document fragments) to do that manipulation all at once.

Yeah, you can do this all on your own, but you get it with React for free. Abstraction does not necessarily have a price.


I wasn't judging virtual DOM has wholly good or wholly bad, just pointing out the often incorrect belief that some how React does a magical thing that you are unable to do yourself. Batching, etc. is no different. The advantage in the abstraction is that it makes it a bit easier to maintain, not that its faster.


> if you iteratively call .appendChild the browser has to reflow every time that happens

No, it doesn't. Not unless you do one of two things: 1) yield to the event loop and wait for a refresh tick or 2) query layout information.

The usual perfomance cliff is when you do #2 interleaved with appendChild. Because then the browser really does need to reflow.

With React, what you get is the ability to queue up all your appends but keep asking for the _old_ layout information, without taking your appends into account. Sometimes that's what you want, sometimes it's not. It really depends on why you're asking for the layout information.


It is not for small changes. But as soon has you update a whole layout of many listings, I doubt you are manually diffing your nested data structure to produce the less possible updates.

React also uses a couple of other tricks, like attaching one event handler to the top of the DOM doc and fire fake events (they call it synthetic) to avoid having many handlers in the page. Or queueing udpates and merging them so that if many updates arrive at a very close time, you update only the DOM once.

Vue.js and Angular 2 do the same. Although Vue.js is my favorite right now because it's so much more flexible and easier to use.


I get that. It's why I mentioned "next tick" to indicate the changes are minimized to the diff'd elements and batched.

As you say, one can rig up DOM updates through VanillaJS calls that are more efficient than React if you're a clever masochist.

If sub-optimal accesses to the DOM is where most of the time is spent in an application, then using an abstraction like React to manage the DOM really can make the system faster AND more manageable, as you say.


    > The advice to "touch the DOM as little as possible" stems from the fact that the DOM has so many performance pitfalls.
What are those performance pitfalls?

The only ones I know are that if you read something related to the layout size (offsetHeight), it might cause layout recalculation. And obviously if you modify how element looks like (innerHTML, changing class, etc).

This doesn't seem like much (assuming I dit not forget something). The first one is not obvious through.


One more pitfall: if you add or remove elements, it also causes a reflow. A common pitfall with applications that render lists is that they do append operations iteratively (with a reflow for each iteration) rather than in one go (with only one reflow).

http://jankfree.org/ is a good resource for causes of slowdown.


I believe this is the comprehensive list:

https://gist.github.com/paulirish/5d52fb081b3570c81e3a

My favorite of the doozies is _reading_ from some properties on mouse events will cause a reflow.


I mean who cares, really. 90% of developers will spend time building reusable components that will be used exactly once, and and some people will open source some stuff that wedges in every suggestion and pull so it is 5 times the complexity any one person ever needs and then we will talk about the failed promise of web components.


I don't think the thesis of the article is that it hinders reusability, though — it's that anything built on the DOM presents significant limitations compared to what we can do with JavaScript.

The example of passing dynamic styles to a web component is a good one: since attributes have to be strings, you have to handle serialization somewhere rather than have it Just Work, like it does with most modern JavaScript frameworks.


It's also the main problem React faces when trying to incorporate/integrate with Web Components. React Components can be arbitrarily complex and accept arbitrarily complex properties.

Web Components work ok as "leaf" nodes: then it's just a component with string props/attributes.

The inverse is nigh impossible: Web Components can only deal with strings, so you would end up with yet another incompatible wrapper à la Polymer if you want to do anything remotely complex.


When you say "Web Components", what is it that you are talking about? I assume you mean custom elements. Custom elements are DOM Nodes like any other. They are represented in JavaScript like any other object.

Of course you can pass non-strings to DOM elements. Open your development console and type document.createElement('div').user = {name: 'Dmitri'}. Did you get an error?


> React Components can be arbitrarily complex and accept arbitrarily complex properties.

Web Components can be arbitrarily complex and accept arbitrarily complex properties or children.

Did you even try Web Components before spewing falsehoods?


The W3C editor's draft for custom elements clearly states that attributes are sequences of DOMString: https://w3c.github.io/webcomponents/spec/custom/#concept-cus...


So? Properties are objects and children are nodes.


So, basically, "we push everything to JS and we have the curse of the empty body tag where everything is rendered with JS" turned into "we push everything to JS and we have the curse of the empty body tag where everything is rendered with JS ... but with WebComponents! So it's cool now!"


Precisely the reason I missed angular when going to react and why I now love vue (which is kinda the best world of both): most of the time, you don't start with reusable components. You just hack on your template, and maybe, maybe, if your project grow, you start extracting common code into reusable widgets.


When I use React I do exactly the same, too. Templates are not that different from components in terms of code ortanization. Components don't have to be reusable at all—it's just nice that once you need them to, they are.


This works only if you are a full js stack. Using any other language means you get 2 sets of templates.


Not sure what you mean. My point is that you can “keep everything in one component” in React exactly the same way you’d do it in Angular or Vue.


If you use django/rails, suddenly you have django/rail template files and templates in your components.


Couple years ago I gave some thought about how to handle reusable JS in a sane fashion. Actually, a lot of thought. The end result was something that superficially sounds like Web Components, but it quite different underneath. I guess a good name for it would be "Web Behaviors".

It's not a framework or a library, but rather a set of rules and practices of how to do things. It will sounds somewhat weird, but I'll list them anyway. Maybe someone will find it useful:

- Overall mental model: libraries don't implement components, but rather change the behavior of certain elements on the page.

- No library keeps state in JavaScript variables. DOM is your model.

- All configuration is also done through DOM with liberal use of CSS selectors to reference things.

- No library has any direct dependencies on any other.

- Things that need to react to changes should (if at all possible) use timers. If that's not possible, they should register global event handlers, rather than attach them to individual components.

While this might horrify some people, it actually forced me to come up with rather nice, abstract behaviors that capture many common features I had to commonly implement. Also, this plays well with the notion of progressive enhancement. I recommend everyone to try it on some new web-based project of low to moderate complexity. Ir really does simplify a lot things.


Do you have some code samples you could link? On the face of it, this is the exact opposite of what React, Vue, Angular, Ember, Backbone, etc. do.


A demo of sorts:

http://quickenloans.github.io/Behaviors.js/

The code will look extremely non-idiomatic to most frontend engineers. Treat it as a thought experiment (which it is). The important thing isn't the particular implementation, but the ideas I outlined above. There are other, often simpler, ways to implement the same concepts.

One property of these libraries that might not be immediately obvious is that they "automatically" adapt to changes on the page. So you can add an element with a behavior via AJAX or dev tools and it will actually work. This is why using timers and global events is a part of that list.

Another thing is that none of these libraries have an API or modify the global JS namespace. This is part of the thought experiment not directly related to the main concepts. It drives some of the complexity.

Edit: Fixed the link.


Link does not work, care to double check it?


Fixed it.


Appears as close enough to principles of Twitter Flight (https://flightjs.github.io/) - principles of creating loosely coupled components that communicate with each other by DOM events.


For anyone interested, I wrote up a response: https://robdodson.me/regarding-the-broken-promise-of-web-com...

Kinda used it as an attempt to address some bigger questions around Web Components I've seen come up a bunch. Curious what you all think.


I've updated the article to include a link to you response


Thank you!


Just in case, architecture of components in Sciter. Could be interesting for some.

Assignment and composition - by CSS, and only by CSS, Sciter's specific prototype property:

   .myComponent {
     prototype: MyComponent url(components/my.tis);
     color: red; 
     ...
   }
where MyComponent is the name of script class in components/my.tis script file. That MyComponent looks like as

   class MyComponent : Element 
   {
     function attached() {
       // called when DOM element gets this class
       // a.k.a. DOM constructor

       // content initialization, if needed: 
       this.$content( <button.prev/> 
                      <button.next/> );
     } 
     function detached() {
       // called when DOM element looses this class
       // a.k.a. DOM destructor
     } 

     function method() {
        // some public method  
     } 
     property prop(v) {
        // some public property
        get { ... }  
        set { ... }
     } 

     event mousedown (evt) {
        // default mousedown event handler
     } 
     event mouseup (evt) {
        // default mouseup event handler
     } 

     event click $(button.next) {
        // click on its button.next
     } 
     event click $(button.prev) {
        // click on its button.prev
     } 

     // etc
   }
To use such component:

1. include CSS where the binding is defined (prototype above) 2. In markup to define matching element:

   <div.myComponent />  
Nothing too complicated and pretty effective. Could be done even 18 years ago: http://www.w3.org/TR/1999/WD-becss-19990804#behavior


Yeah. Sciter's ideas would be awesome :(


This entire article is invalidated by the fact that React's DSL (or the TSX variant, to be precise) can be used to implement Web Components!

Take a look: https://github.com/wisercoder/uibuilder


I think your native web components example is wrong, or more probably outdated.

Here's an example with the latest wc spec: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sl...

But as far as I understand from the spec, sadly, you would still need a separate DSL that would leverage iterations, conditionals and other templating operations on top of web components.


Yeah, I got it from Polymer's docs. If I don't forget, I'll update the article


The author of this article really gets web components wrong. You don't use JS for everything. Web components have a built in DSL.

The below from the article is ridiculous and HORRIBLY INNACURATE:

  ['Hello', 'world'].forEach(('text') => {
	const p = document.createElement('p')
	const span = document.createElement('span')
	span.textContent(text)
	p.appendChild(span)

  MyComponent.appendChild(p)
  })`
In RiotJs you would build a web component this way:

  <text-breaker-upper>
   <p each={this.input_str as out_text}>
     <span>{out_text}</span>
   </p>

    <script>
       this.input_str = input.split(" ");
    </script>
  </text-breaker-upper>
Then you declare it ON THE PAGE.

  <text-breaker-upper
      input_str="Hello World">
  <text-breaker-upper>
Web components much more intuitive, reusable, better separation of concerns, less bloat, and more elegant than React..no bastardization mixing of JSX, CSS, and JS, and how the web (and any sane technology) was meant to be.

The sooner this JS front end framework B.S. wasteland nightmare is behind us, the gournd salted on where it stood, and we laugh at JS like we laugh at COBOL.... the better in my opinion.


Looks like Riot.js is just another frontend library that has a templating pseudo-language like so many others. Maybe they use the words "web component" on their site or something? Looks like they parse their templates out of the DOM and then do what any frontend framework does. Strange that you get so emotional about whether your templating language is shoehorned into some DOM nodes that get thrown away and replaced once the framework reads them. This is not the web components that the article is talking about and it is not interoperable.


This and Polymer are the precursor to built in browser web components that the author claims React is superior to.


When done correctly web components aren't just about extensibility. They are also about encapsulation, debugging, testing, *ing, and then reusability.


I think the point in the article about how you structure your application is the big strike against Web components.

To me they are fine for when you want to compose things by nesting them but bad at any other kind of composition.


All of these are much better solved by literally everything else :)


Literally everything else is not a useful example.


I wonder if there's a JavaScript framework called "literally else"... :)


React has a bigger community, more hype, more tooling around it, better native mobile support and better support for automated testing.

But if I had to pick my favourite, I'd have to go with Polymer because:

- It's simpler (fewer dependencies).

- Declaring styling and layout is cleaner (it's easier to visualise a component's appearance and to modify it during debugging because layout and styling are kept separate from a component's behavior logic). When it comes to debugging, styling/layout issues almost always happen independently from behaviour issues so it makes sense to keep them separate in the source.

- It works better with browsers' native development tools.

- No need for a compile step (and associated complexity that comes with it as your app grows).

It's not fair to pretend that React bypasses the DOM entirely. Ultimately, when the user clicks on a button, they are interacting with a DOM element, so the event still has to pass through the DOM layer. The DOM is still the API whether you like it or not.


I think we're approaching a point at which browsers will need to make some sort of break with the current DOM in order to provide the types of rich experiences engineers and users want. There's simply too much baggage with the current implementation, and this article highlights a lot of it: attribute values limited to strings, a global namespace for CSS, etc.

This is happening at other levels of the web with the HTTP/1 —> HTTPS/2 transition. We're patching over shortcomings in the DOM right now with JavaScript, but it's easy to see the appeal of a native solution to some of these issues that doesn't require a facade over the actual thing that people interact with.


It saddens me that the software industry has been flailing around for at least 15 years trying to resolve the tension between having zero-install + standardized framework and having a rich UI toolkit that can do "real" apps without making developers crazy. The answer seems to be this weird cycle of layering reinvention atop reinvention because nobody can just blow up the whole stack and rethink it with any market success.

Using the DOM + scripting pattern with a DOM more suited to "rich app" development has been tried multiple times as plugins (FLEX and Silverlight come to mind). But the gravitational pull of zero-install + standards crushed them. We also have in progress what is essentially a reinvention of the JVM idea (WebAssembly) but without the UI layer to go with it. That may be successful in its narrow niche, or it may try to overreach into the UI layer and fail like all the others…too soon to tell.


What would be the language to use? JavaScript won early, so it's been the de-facto choice every since to the point where browsers probably don't even run other scripting languages with the script tag. The first major hurdle in changing all of this would be getting everyone to agree on what to use. Different languages are suited for different jobs. HTML is perfectly fine for static UI, which is what most sites need it to do. What would a better language or set of tools for the web even look like?


This whole tangled mess of JavaScript, HTML, CSS and custom expression languages screams Lisp.


Yup, I'm reading this and thinking "oh my, Clojurescript is SOOOOO much nicer" than jsx and dom js api and whatever becomes "trendy" tomorrow.


Cannot agree more


Web Components have turned into a dead end for me. Instead, I like what Facebook has done in that they have proven the declarative approach can translate to native and even VR in a very intuitive and approachable way. As far as I'm concerned this is the future for awhile as I've been having a breezy easy time doing native with React and have never experienced a simpler way to develop anything. I have also programmed in Vue but for some reason still prefer React. Possibly because it seems there is more community support, usage and toolage and of course it supports more types of devices, aside from being baised.


Important context (Twitter thread) for this article, by the same author a few days before publishing it: https://twitter.com/dmitriid/status/841172534586429441


I don't think Web Components are a "framework replacement", they are an addition.

Stuff like Bootstrap needs them to enrich their "top down" approach with CSS and HTML so you don't have to mess around with either their source OR CSS classes and "their" HTML structure everywhere to get the components right. You add their CSS & (Web Component) JS at the top and use their custom components as the "leafs" of your sites. I wrote about it here: https://dev.to/kayis/web-components-for-your-custom-elements

The other frameworks like React, Angular, Cycle or Ember will then become the Data-flow layer of the app.


>The other frameworks like React, Angular, Cycle or Ember will then become the Data-flow layer of the app.

None of these frameworks like dom being changed from underneath them, you will run into all sort of weirdness.


I think this depends on the complexity of the components you implement as web-components.

I mean, in React, the whole app is basically a component. It wouldn't make any sense to replicate this with Web Components.

But yes, I read a few times that the React devs don't consider React a "data flow framework", so they decide when to re-render and stuff. Cycle is more in line with this thinking.


Shadow DOM encapsulates the web components sufficiently so that VDOM or other frameworks don't see the inner HTML changes.


> In 2017 React fulfills all the promises of Web Components

Not really - React needs a large run time, and it's really not a good fit for an existing app where you need a true component you can pop in and use within a Java or Rails app.


You can use slimmer runtimes: Inferno, React, snabbdom, virtual-dom...

Web Components also need a runtime as soon as you need anything sufficiently complex. Such a simple example as a ToDo List cannot run without Polymer's weird data-binding implementation: https://github.com/PolymerLabs/todo-list


This article is just awful.

There seem to be two main criticisms here:

1. Web Components don't provide a DSL for creating DOM trees. This is true, because Web Components don't provide a DSL for creating DOM trees! That's and entirely separate concern that can be addressed by a library[1] or by a future, more-ergonomic tree construction API in the DOM.

2. The author doesn't like Polymer's templates. Which is, nearly as unrelated to Web Components as the first complaint, because it's also the same as the first complaint. Polymer's template system allows you to create DOM trees, and automatically wire together properties and events. That's it. You can do this without using custom elements at all.

You can also use any number of ways to construct trees: JSX, DSLs, HTML templates and cloning, string templates, etc. It's no different from any other framework including React.

And then he repeats Sebastian Markbåge's, creator of React, tired complaint that Web Components can only consume string data. This is FUD, pure and simple. Web Components are Elements, and Elements are Objects with properties. Every Web Components framework relies on this for data-binding or setting "props".

And... that's it. That's basically the extent of the critique.

The author could have actually tried to compare React and Web Components and he would have seen that:

1. The component models are very similar, there are analogs for most lifecycle events.

2. Web Components don't prescribe state management or dataflow, and there are indeed functional, unidirectional, immutable (by convention), Web Components libraries and patterns out there.

3. Shadow DOM fixes nearly everything that css-modules address in a native and performant way.

Web Components aren't a be-all-end-all standard. They are a few fixes to the platform that should have been there from the beginning, namely that you should be able to extend the HTML vocabulary (Custom Elements), you should be able to encapsulate some DOM and styling (Shadow DOM, which most browsers already had for their own UIs), and that you should be able to ship and parse _potential_ DOM (<template>). That's it. Everything else is either a library responsibility and/or TDB. Web Components are a foundation to make it easier to create frameworks, libraries, and an base-level interop story that answers some, but not all, critical, simple questions:

How do I write a widget? : Extend HTMLElement How do I keep other scripts and styles from interfering with my widget's internals? : Put the internals in a ShadowRoot. How do I scope my own styles? : Put them in a ShadowRoot How do I instantiate my widget: Use a tag in markup, call createElement(), or call it's constructor.

Anyone coming to the web platform as a new developer now doesn't have to sift though 100 frameworks and contradictory tutorials for such simple questions, the platform provides answers, as it should have from the beginning.

[1]: See Hyperscript as a React-like example https://github.com/hyperhype/hyperscript


>> Web Components don't provide a DSL for creating DOM trees! That's an entirely separate concern that can be addressed by a library

Here's that library. JSX for Web Components: https://github.com/wisercoder/uibuilder


So, basically, "we push everything to JS and we have the curse of the empty body tag where everything is rendered with JS" turned into "we push everything to JS and we have the curse of the empty body tag where everything is rendered with JS ... but with WebComponents! So it's cool now!"

;)


What does Hyperscript have to do with web components?


Exactly.

The complaint in the article is that Web Components don't _also_ add a nice API fro creating DOM trees.

This is true, but not relevant. The ideas of a component model and tree creation DSL are orthogonal.

Hyperscript (the JS library) is a nice API for creating DOM trees, that's similar to React's createElement(). You can use it with or without Web Components.


[Typo flagged and fixed]


Done! Thank you!


It starts to sound like browsers should support JSX natively...


Browsers should have kept doing just plain HTML/CSS, or have adopted XHTML with the accompanying standards that would have allowed for a XUL / XAML like experience.

Instead, it's hack on top of hack.


Template strings look like a better way to do JSX, imo: why can't html`<p>Hello world</p>` be made to return the DOM element for the p tag?


JSX appeared before template strings where even an idea (I think :) ).

The thing though, JSX doesn't return DOM elements. It's a very thin DSL on top of React.createElement calls. Since it's Javascript all the way down, you get a number of benefits:

- you are not constrained by limitations such as "attributes can only be strings" or "children can only be DOM Elements". It's all Javascript expressions

- React knows the return value. It can do efficient diff'ing against its internal DOM representations and only update the actual DOM as needed (same goes for other virtual dom implementations such as snabbdom, virtual-dom etc.)


Better DOM APIs, surely. It could be anything from JSX to https://github.com/Matt-Esch/virtual-dom/tree/master/virtual....

When all you can pass to an attribute is a string, it's very hard to seriously talk about useful extensible custom components on the web :)


Properties and children have you heard of them?

Here's the docs for an element that has some arbitrarily complex properties and function-values properties, that you can set from plain JS or Polymer's template syntax or Angular's, etc... https://www.polymer-project.org/2.0/docs/api/elements/Polyme...

It also takes a template and stamps it out several times based on the complex data, like "higher-order components".

It's very hard to seriously talk about web components with someone so willfully ignorant about them.


I've heard about them. I also included an example in my article specifically about children.

DOM APIs are a much bigger problem than just WCs, but are a part of the WC's problem as well


JSX in 201x is like new XML of 200's. I've seen webpack.configs written in JSX. WTF is wrong with people?


Well, there's this thing called HTML...


I think it's pretty clear that HTML alone is not sufficient for creating the types of web applications we are today — otherwise, no one would have bothered to create or adopt React/Angular/web components in the first place.


Firefox supported embedded xml for years (e4x) but took it out. Jsx feels a little bit similar.


HTML Components (1998) was proposed and implemented by Microsoft starting in IE5.5 (obsoleted in IE10).

XBL (2001) and its successor XBL2 (2007) was proposed by Mozilla as a companion to their XUL user-interface language

https://blogs.windows.com/msedgedev/2015/07/14/bringing-comp...




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: