davedawkins / Sutil

Lightweight front-end framework for F# / Fable. No dependencies.
https://sutil.dev
MIT License
285 stars 17 forks source link

How Sutil works? #61

Closed sumeetdas closed 2 years ago

sumeetdas commented 2 years ago

Hello @davedawkins! I just came across your project and what I really like about it is how it does not have any JS dependency to install (as per HelloWorld sample), except for webpack. The docs, however, does not explain it in detail as to how Sutil really works. Could you please explain it works? From what I've understood so far, Svelte compiles HTML + JS + CSS into JS and CSS. Does Sutil too compile F# code to JS? If yes, then how is it different from Fable compiler?

Sorry if the question is not very clear. I just want to understand how Sutil works. Thank you.

davedawkins commented 2 years ago

Hi Sumeet, Sutil works by reacting to events, and rebuilding DOM directly. Sutil defines a way of representing fragments of HTML that you mount into a pre-defined element, and we call that a DSL. The DSL allows you to specify elements, attributes, event handlers, as you would in a plain .HTML file with JS, but additionally, it allows you to bind sub-DSLs to events. For example

let app() = 
    Html.div [
        Attr.style [ Css.color "red" ]
        text "Hello World"
    ]

mountElement (app()) "sutil-app"  // May have this syntax/argument ordering slightly wrong

just builds a simple DIV that says "Hello World" in red, as you'd expect. However you can also do


let textSource = Store.make "Hello world"

let app() = 
    Html.div [
        Attr.style [ Css.color "red" ]
        Bind.element( textSource, fun s -> text s )
    ]

mountElement (app()) "sutil-app"  // May have this syntax/argument ordering slightly wrong

We've now rewritten the static fragment as something that can now react to changes made via textSource. Sutil handles the event listening and DOM reconstruction. It will rebuild the DOM directly into the page though, and not via a virtual DOM.

There really isn't a lot of magic to it! Does this help?

AngelMunoz commented 2 years ago

Adding to Dave's Explanation. Webpack is a dev time build dependency which can be swapped for other tools like vite/snowpack in case you know a little bit more of the node ecosystem.

but if we skip the node part (which we can), in simple terms it uses fable to compile down the F# code to javascript, in turn that javascript does what Dave just explained

sumeetdas commented 2 years ago

Thank you Dave and Angel for the explanation. I still have one question - Svelte dropped virtual DOM and instead determines at build time how code will change and update DOM accordingly. Is that what Sutil does too using Store as in the above code? Is Store being used to implement all build-time diff logic and not just for text input-related events?

Edit: I want to ask another question - if I create a new web component like custom-form on top of Sutil, push it to NuGet as a library called 'Custom.Form' and import it into my project, then would Fable compile Custom.Form into JS? I don't also have to build it into JS first, push it to npm registry and then use it as an npm dependency later, correct?

davedawkins commented 2 years ago

Sutil is indeed inspired by Svelte (and was initially called Sveltish). So, you are correct - though I'd rather say it as "Store is providing an observable stream of events that is used to make changes to the DOM at run-time".

davedawkins commented 2 years ago

Re: custom-form Nice question. If you export your component as an F# (Sutil) component, then you just import as you would any other F# library, reference it from the DSL for your app and Fable handles it. If however you make a Web Component (https://developer.mozilla.org/en-US/docs/Web/Web_Components) from your component, in theory, you can just make this an NPM JS package. Fable is compiling the Sutil component into a Web Component and you ship the generated .js files. This is in theory! We did some work on Web Component support but I'm not sure it's been proven in a real web site yet. The proofs-of-concept seemed to work well though.

sumeetdas commented 2 years ago

Thank you Dave for your answers! F# (Sutil) component approach works for me, as I would like to minimize NPM dependencies as much as possible.