4 ms·
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 comp
by spankalee 10y ago
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
- kensign 10y agothank you, thank you, thank you. Aurelia is an excellent system for this exact use of a web component.
- spankalee 10y agoMy 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?
- kensign 10y agoyes
- spankalee 10y agoSo 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?
- kensign 10y agothat work is handled by Aurelia itself. You don't explicitly need to define the element. http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/creating-components/1 http://aurelia.io/hub.html#/doc/article/aurelia/framework/la...
- spankalee 10y agoSorry, 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 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.
- underwater 10y agoOf 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.
- spankalee 10y agoThis 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.
- underwater 10y agoSo implement the accessors logic and invalidation logic, centralize the rerender scheduling and you've rebuilt React.
- scotty79 10y agoWhat 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.