|
As I understand it, there's a couple of issues with naively rerendering DOM elements like this. Firstly, the DOM is stateful, even in relatively simple cases, which means destroying and recreating a DOM node can lose information. The classic example is a text input: if you have a component with a text input, and you want to rerender that component, you need to make sure that the contents of the text input, the cursor position, any validation state, the focus, etc, are all the same as they were before the render. In React and other VDOM implementations, there is some sort of `reconcile` function that compares the virtual DOM to the real one, and makes only the changes necessary. So if there's an input field (that may or may not have text in it) and the CSS class has changed but nothing else, then the `reconcile` function can update that class in-place, rather than recreate it completely. In frameworks which don't use a virtual DOM, like SolidJS or Svelte, rerendering is typically fine-grained from the start, in the sense that each change to state is mapped directly to a specific DOM mutation that changes only the relevant element. For example in SolidJS, if updating state would change the CSS class, then we can link those changes directly to the class attribute, rather than recreating the whole input field altogether. The second issue that often comes with doing this sort of rerendering naively is layout thrashing. Rerendering is expensive in the browser not because it's hard to build a tree of DOM elements, but because it's hard to figure out the correct layout of those elements (i.e. given the contents, the padding, the surrounding elements, positioning, etc, how many pixels high will this div be?) As a result, if you make a change to the DOM, the browser typically won't update the DOM immediately, and instead batches changes together asynchronously so that the layout gets calculated less often. However, if I mix reads and writes together (e.g. update an element class and then immediately read the element height), then I force the layout calculation to happen synchronously. Worse, if I'm doing reads and writes multiple times in the same tick of the Javascript engine, then the browser has to make changes, recalculate the layout, return the calculated value, then immediately throw all the information away as I update the DOM again somewhere else. This is called layout thrashing, and is usually what people are talking about when they talk about bad DOM performance. The advantage of VDOM implementations like React is that they can update everything in one fell swoop - there is no thrashing because the DOM gets updated at most once per tick. All the reads are looking at the same DOM state, so things don't need to be recalculated every time. I'm not 100% sure how Svelte handles this issue, but in SolidJS, DOM updates happen as part of the `createRenderEffect` phase, which happens asynchronously after all DOM reads for a given tick have occurred. OP's framework is deliberately designed to be super simple, and for basic problems will be completely fine, but it does run into both of the problems I mentioned. Because the whole component is rerendered every time `html` is called, any previous DOM state will immediately be destroyed, meaning that inputs (and other stateful DOM elements) will behave unexpectedly in various situations. And because the rendering happens synchronously with a `innerHTML` assignment, it is fairly easy to run into situations where multiple DOM elements are performing synchronous reads followed by synchronous writes, where it would be better to do all of the reads together, followed by all of the writes. |