HN2new | past | comments | ask | show | jobs | submitlogin

I'm with you. All this complexity is baffling, when there's a very optimized rendering engine sitting there in the form of the browser, and soon a suitable component model.

If you wire up your components so that you just re-render the components that have data changes, you get the benefits of dom diffing, without having to diff.

You can skip all this framework JS, and just directly write an async-rendering Web Component. It's pretty compact and easy to understand. I just wrote up a sketch that would work with one of the incremental-dom-based template libraries:

    class AsyncWebComponent extends HTMLElement {
      constructor() {
        super();
        this.isLayoutValid = true;
        this.attachShadow({mode: 'open'});
        this._foo = 'bar';
      }
      
      // Accessors could be generated with decorators or a helper library
      set foo(value) {
        this._foo = value;
        // You could check for deep equality before invalidating...
        this.invalidate();
      }
      
      get foo() {
        return this._foo;
      }
    
      connectedCallback() {
        this.invalidate(); // trigger the initial render
      }
    
      render() {
        // Call into template library to re-render component.
        // If the template is incrementally updated (say with incremental-dom),
        // then only child components with data that changes will be updated.
        templateLib.render(this.template, this.shadowRoot);
      }
    
      invalidate() {
        if (this.isLayoutValid) {
          this.isLayoutValid = false;
          // scheduleRenderTask enqueues tasks, so they'll be run in order down the component tree
          // A simple and correct scheduler is to just enqueue a microtask with Promise.resolve().then(task)
          scheduleRenderTask(() => {
            this.render();
            this.isLayoutValid = true;
          });
        }
      }
    }
edit: example uses. In a template:

    <async-web-component foo="[[foo]]"></async-web-component>
When the parent component renders and sets `foo`, the child will schedule a task to re-render.

In JS:

    let e = document.querySelector('async-web-component');
    e.foo = 'baz'; // e schedules a task to re-render
    e.foo = 'qux'; // e doesn't schedule a task, because one is pending


Of course you can do manual dirty tracking. But why would you ever want to do that on every single component when you could get it for free?

React's DOM digging has zero cognitive overhead so the benefits are essentially free to me as a developer.


This is a sketch of a raw custom element with no library for sugar. A library that assisted in the pattern would presumably implement the accessors for you.

ES/TypeScript decorators would be an easy way to do this, or just a function that fixes up the class after declaration.


So implement the accessors logic and invalidation logic, centralize the rerender scheduling and you've rebuilt React.


What if your component has a lot of nodes? Do you re-render them all? I guess that's the reason they made react. Even if the state of component changes most of the DOM nodes of render component stays the same and re-rendering them would take too long.


thank you, thank you, thank you. Aurelia is an excellent system for this exact use of a web component.


My example was "plain" custom elements, nothing to do with Aurelia.

Last I checked Aurelia used a lot of the Web Components terminology, but didn't actually create or use custom elements. Did that change?


yes


So Aurelia components are web components? I can create one with document.createElement('my-aurelia-component'), and container.appendChild(), and the element will fully work and have it's lifecycle callbacks called?

I tried to look in the documentation, but couldn't see this clearly stated. It looked like Aurelia would have to be in charge of lifecycle to get dependency injection to work. Specifically, custom element constructors are called by the browser with no arguments: how does this work with Aurelia components that expect to receive constructor arguments via dependency injection?


that work is handled by Aurelia itself. You don't explicitly need to define the element.

http://aurelia.io/hub.html#/doc/article/aurelia/framework/la...


Sorry, I still don't get it. Take the CustomerDetail example in the "Component Instantiation Through Dependency Injection" section:

    @inject(HttpClient)
    export class CustomerDetail {
      constructor(http) {
        this.http = http;
      }
    }
If this really creates a Web Component, how does `this.http` ever get set? The browser will call the constructor with no arguments.

What exactly gets passed to `document.registerElement()` (for the v0 Custom Elements API) or `customElements.define()` (for the v1 Custom Elements API)? Are you saying that Aurelia generates and registers a separate custom element class? That's not in the docs. If so, how does that element find the instances to inject into the user-defined constructors?

The main issue that talks about custom element support is still open, and the answer really does seem to be that Aurelia can't create custom elements: https://github.com/aurelia/framework/issues/7

This unfortunately seems to confirm that Aurelia uses the terminology of Web Components, but doesn't actually use or create real Web Components.


yeah... You might want to try it out and read the docs before you make such claims

https://github.com/aurelia/web-components

remember too, the ES6 is transpiled. Also, I am done with the nerd war. If you have questions, use the gitter channel.


"nerd war"? Wow, good to see you go then.

That link you just posted is not contained in any of the docs or links that either you or I posted above. I can't actually find it in the entire documentation hub, so even if I did "read the docs", how was I supposed to find it?

Bye.




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

Search: