When your React component renders, it returns an object that describes the
desired state of the component tree. Take this component:
function MyComponent() {
return <div>Here's <b>bold text</b></div>
}
At runtime, React calls your component function and then applies the returned
data to the DOM imperatively. If your component has rendered before, React needs
to _diff_ the virtual DOM your latest render returned against the VDOM React last
applied to the real DOM. Very simplified idea of what happens on every render:
function reactInternalRenderLoop(root) {
const newVdom = MyComponent({})
const diff = diffVdoms(root.prevVdom, newVdom)
for (const change of diff) {
change.applyToDOM(root.domNode)
}
root.prevVdom = newVdom
}
React's runtime time complexity is fundamentally limited by this `diffVdoms`
function, and React's memory usage is fundamentally limited by the size of the
VDOM returned by components.
What Svelte does is entirely eliminate this whole process involving diffing and
what-not. At compile time, Svelte analyzes the data dependencies between the
rendered template and reactive values in the app, and generates self-contained
components that know how to update their own DOMs. You can think of it as
essentially moving the entire `reactInternalRenderLoop` function from _runtime_
to _compile time_. There is no more diffVdoms function call on your user's
devices. There is no more big allocation of VDOM data structures.
Svelte might compile `MyComponent` to something that looks like this:
And since the Svelte compiler knows the contents of MyComponent never change,
the mount function will only be called once for the entire runtime of your app.
So it sounds like the tradeoff here is that the generated bundle is more complicated/bigger. I'm not familiar with Svelt, but wondering about bundle size + debuggability.
At a certain size, yeah the bundle will be larger, but it would take a lot to get to that point. For pretty much anything you would do, the bundle will be much smaller. Especially if you use SvelteKit which is smarter about what is bundled and when it loads.
As for complexity, the generated Svelte code is extremely readable. Take a look at the generated JS tab in the repl: https://svelte.dev/repl
When I tried Svelte a while back (year or two ago) I had difficulty working with code in the debugger, couldn't seem to get source maps to work correctly, I wonder if it's fixed.
Surgical = "It checks a diff of what the component rendered before an update vs. after and then only patches or updates the parts of the DOM that need to be changed (as opposed to replacing the entire DOM, which is slow)." Similar to React. Svelte may not be using the exact same mechanisms but this is in the ballpark.
Virtual DOM is just an in-memory representation of the DOM that describes which child elements (e.g., a <p /> nested within a <div />) belong to which parents, and which attributes/values those elements need at render time. When a change happens, you can generate an updated virtual DOM and diff it against the previous (or current) version and pick out what specifically needs to be changed in the DOM (read: surgically).
There is no diff going on with Svelte. And there doesn't need to be an in-memory representation of anything. Because it is compiling the code, it knows exactly what will change and in response to what.
For example in that repl, you can see that it makes a call to `$$invalidate` when the input value changes (see `input_input_handler()`) which triggers a `set_data` call (see `p()`) to update just the textNode that contains the name. This is what is meant by surgical. No other work is done other than what is required to update the specific places where a variable is used.
How does it "surgically" update the DOM? Why is it better than virtual DOM?