5 ms·
I wrote all of non.io with vanilla webcomponents - it's a full SPA built on them. A few learnings I'll share with going beyond just a simple nested blog compone
by jjcm 3y ago
I wrote all of non.io with vanilla webcomponents - it's a full SPA built on them. A few learnings I'll share with going beyond just a simple nested blog component:
State management is still unanswered. You'll end up having to do a lot of this yourself if you aren't using a wrapper framework. One nice thing about React is state management is mostly solved and there are existing patterns for it. There's expectations of how it's done. With web components it's the wild west, and since you can't pass complex objects as attributes like you can in jsx, you end up doing a mix of attributes, events, and calling class functions directly.
Performance isn't as big of a win as I'd have hoped. I actually reverted some smaller atomic elements (i.e. icons) away from being web components as their instantiation cost wasn't worth it when I had hundreds of these elements on the page. Static elements, despite the readability they provide in code (i.e. <nonio-icon glyph="hamburger">), just aren't worth using web components for, which was fairly disappointing.
That said, there are things I REALLY enjoy about web components. As the author shared, it's incredibly easy to add a component to new pages, regardless of the framework being used. If something is built using web components, you can truly use it anywhere. This has been really nice when building supporting sites like api docs for nonio, where I can easily pick and choose what components I want despite having a different stack. Need to embed a <nonio-user> component? Import the js, add the html, you're golden.
Style encapsulation of shadowroots is quite nice. I really love not having to worry about breaking styling of any component as I work on a page. It means less guesses and checks of clobbering styling of other elements, and less of a need for a visual diff framework to ensure this hasn't happened.
The other aspect I enjoy is how slim the package sizes are. No libraries or anything extra to load, everything is baked in. Because you can pick and choose components you need so easily as well, it's incredibly easy to keep package sizes down.
If anyone is curious, source components for nonio are here: https://github.com/jjcm/nonio-frontend/tree/master/components https://github.com/jjcm/nonio-frontend/tree/master/component...
- mdhb 3y agoRegarding the point you mentioned about not being able to pass objects via attributes, you can however pass them via properties on the element. Also as for the state management side of things there is nothing at all stopping you from hooking up whatever state management solution you want. I’ve even seen a bunch of solutions that use the browsers built in event model as well if keeping dependencies to a minimum is your goal. Finally, the last thing I would suggest is that writing an entire app in vanilla web components is kind of crazy talk in my opinion. For 5kb you can have a super nice developer experience using Lit (https://lit.dev https://lit.dev)
- jjcm 3y ago> Finally, the last thing I would suggest is that writing an entire app in vanilla web components is kind of crazy talk in my opinion. For 5kb you can have a super nice developer experience using Lit (https://lit.dev https://lit.dev) I 100% agree with this. For me it was more of a question of "can I do it", and that was something I wanted to find out. You notice that I ended up having to recreate a significant chunk of lit-like functionality on my own via a base class: https://github.com/jjcm/nonio-frontend/blob/master/components/soci-component.js https://github.com/jjcm/nonio-frontend/blob/master/component... I would very much recommend not going full vanilla. Using a library like lit will definitely help making things easier/more polished, and will integrate better with existing tooling.
- claytongulick 3y ago> writing an entire app in vanilla web components is kind of crazy talk in my opinion Why? I've done many. My guiding principles: - Avoid shadow dom unless it's a library. Stick with the light DOM and css system of your choice. - Use lit-html or similar for rendering - Use class properties for reactivity (add a render() call to setters, as needed) - Ignore attributes entirely. Just use properties. - Don't bother with composable components and slots. You generally don't need them - Use a nice router like vaadin or ionic or similar to hoist components and support deep linking - Let components maintain their own state. Structure the dom so that it's easy to grab state from a component with querySelector. If this doesn't work, I have a little state lib called ApplicationState[1] that works great. - Really think about what needs to be a component vs normal html. Don't overdo it with components. - Structure the app with high level "scene" components that map to URL deep links, and app components that you use to build the scenes. Scenes are targets of the router. I've been doing this for years for many apps for many clients. Blazing fast, simple, no framework churn, no compilation, no framework download or overhead... It's pretty tough to make myself use a framework at this point. [1] https://claytongulick.github.io/applicationstate/ https://claytongulick.github.io/applicationstate/
- mdhb 3y agoI didn’t suggest for a moment you couldn’t. I said it was a bit of a crazy decision to take. The entire reason I said that was because there is going to come a time after you have built probably your 5th component where you are going to start moving a whole bunch of shared logic into a base class that extends HTMLElement and that is the point you should probably stop and just bring in Lit at around 5kb total because they already did exactly what you’re doing and they almost certainly have done a better job with a bigger team and access to browser engineers with thousands of existing users and a bigger test suite with better documentation and well.. you get the point. A lot of your argument seems to be quite focused on the idea that frameworks are some heavy weight thing which is perfectly true in many cases. But other options exist, and with a 5kb-10kb library download budget those arguments don’t carry a lot of weight.
- nicoburns 3y ago> Performance isn't as big of a win as I'd have hoped. Is performance a win at all? I would expect performance to be worse than using a regular web framework (React, Vue, Svelte, etc). Because these tend to get their performance from batching DOM manipulations across the entire page, and you can't do that will web components. I figure web components are pretty much dead in the water for this reason alone. I guess they can be useful if you just want small "islands" of functionality rather a fully client-rendered page.
- nevir 3y agoThese days, batching DOM updates doesn't give you as big of a win as it did in the past (browser tech has improved, browser engines do more batching under the covers, etc). Also web components tend to use more modern layers of the web stack (style isolation, shadow DOM, etc) - which allow the browser to perform more optimizations, as well. I think we'll find that they become more performant over time. Also consider that some "native" html components are effectively implemented the same way as web components these days. Often you'll find that React/etc are perf hits (vs finely tuned components) because their declarative nature tends to lead to unnecessary work …which is totally fine 99% of the time, because the productivity win from that is HUGE.
- Joeri 3y agoDid you see the performance issues also on web components that didn’t use shadow dom? I noticed this performance wall as well, but I always put it down to shadow dom being heavy. Now I’m wondering if it is the instantiation itself that’s heavy. For state mgmt I have noticed the same problem and was thinking about trying to take a page out of desktop development’s playbook by having a window-level message bus that all components subscribe to and filter data from, and every state change being published on that message bus.
- mdhb 3y agoShadowDOM is actually a big part of the reason where the perf comes from because the universe of what actually needs to be considered in order to get to the eventual answer of what style properties should any given element actually have is tiny due to the fact that it is encapsulated. That and the the other big win comes from using native compiled C++ browser code to implement large chunks of what libraries typically provide in userland JS. React is probably the worst offender here as it for all intents and purposes brings its own implementation of both DOM and events. It’s just an outdated architecture choice that made a huge amount of sense at the time but nobody would do that today if they were building a front end library from scratch. Finally regarding your point about the message bus idea you might want to take a look at the BroadcastChannel API if you aren’t already familiar with it. Sounds like it might be what you’re looking for.
- wg0 3y agoBroadcast Channel API - unbelievable that it has such a wide support. Really amazing. Thank you for this. Didn't know that. OP above or anyone else can say anything about routing in context of web components? Deep linking? Any pointers? Noob backend guy here.
- mdhb 3y agoOne approach I don’t mind is to just make a HTML file per screen in the application and route like the rest of the web. When you bring in modern things like service workers and the prefetch API you can still very much keep the speed that is traditionally associated with SPAs. Otherwise I’ve seen good things said about both of these options: 1. https://github.com/vaadin/router https://github.com/vaadin/router 2. https://github.com/lit/lit/tree/main/packages/labs/router https://github.com/lit/lit/tree/main/packages/labs/router Both follow the mental model of mapping a URL pattern to a component fairly intuitively.
- breadwinner 3y ago> since you can't pass complex objects as attributes like you can in jsx But you can! Here's an example of passing complex objects as attributes (look for zx-listeditor): https://github.com/wisercoder/uibuilder/blob/master/WebComponentsDemo/Views/DemoPage.tsx https://github.com/wisercoder/uibuilder/blob/master/WebCompo... And here's the web component itself: https://github.com/wisercoder/uibuilder/blob/master/WebComponentsDemo/elements/ZxListEditor.tsx https://github.com/wisercoder/uibuilder/blob/master/WebCompo... And here's the tiny (500-line) lib that enables it: https://github.com/wisercoder/uibuilder/#jsx-for-web-components https://github.com/wisercoder/uibuilder/#jsx-for-web-compone...
- crooked-v 3y ago"You can pass complex objects using web components, if you also use something other than web components" isn't really all that much of a defense of web components.
- breadwinner 3y agoIt is 223 lines of code: https://github.com/wisercoder/uibuilder/blob/master/dist/uibuilder-1.7.0.js https://github.com/wisercoder/uibuilder/blob/master/dist/uib... You object to this tiny amount of code?
- skrebbel 3y agoHow do you make this work with frameworks like Svelte which, when server side rendering, cast all props to string (!) attributes?
- breadwinner 3y agoThat should be an easy bug fix in Svelte.
- akira2501 3y ago> With web components it's the wild west, and since you can't pass complex objects as attributes like you can in jsx, you end up doing a mix of attributes, events, and calling class functions directly. Or you just write a 'value' getter and setter and be done with it. I write most of my web components to operate like a form component. Forms have state. Forms have mechanisms for gathering state from all components and saving them into a single object. Forms have a means of restoring all that state. You _barely_ need a layer of abstraction to handle all this. I've honestly never understood the "state management problem" or why anyone would think a framework is the way to solve it.
- superchris 3y agoSince you mention that state management is an unsolved problem with Web Components, I thought I would share a project that aims to bring a solution: https://github.com/launchscout/live_state https://github.com/launchscout/live_state. The basic pattern and idea of LiveState is "dispatch events, subscribe to state". The events and state updates are sent over a websocket connection, and the front end and back end libraries are a thin layer over Phoenix Channels. Currently, event handler functions are written in Elixir, but work is underway to allow them to be written in any language that compiles to WebAssembly.