Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Can someone teach me how Svelte does what it does? The front page is not telling me much?

How does it "surgically" update the DOM? Why is it better than virtual DOM?



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:

    const MyComponent = {
      mount(parent) {
        const element = document.createElement('div')
        div.innerHTML = "Here's <b>bold text</b>"
        parent.appendChild(div)
      },
      update() { /* no-op */ }
    }
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.

https://svelte.dev/repl/7161f10f8eb645d8984fb231a0bc3982?ver...

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.


This is a cool approach, thank you for sharing that link.




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

Search: