readthedocs / ext-theme

Read the Docs drop in replacement site templates
2 stars 2 forks source link

JS: consider using web components over Knockout #233

Open agjohnson opened 11 months ago

agjohnson commented 11 months ago

With the work that we've been doing on addons with web components, I'm slightly motivated to use web components on the new dashboard eventually. The goal would be to eventually remove Knockout from our stack.

A couple points on technical implementation:

I'm not entirely sure of how the actual work here might proceed though. I suspect we'd have a mix of Knockout/web components for a short while. There are some views that would translate easily. There are also some views that would require some more thought -- mostly views with nested views like model listing pages.

Either way, it wouldn't be something to jump deep into, but instead take on the additional dependencies (Lit) and try this pattern out on new elements. We'd have a period where our bundle would be a bit inflated with Lit and Knockout (though luckily both are relatively small libraries). This would likely happen after moving out of a beta period.

If we are interested in consolidating on web components, this could be a path forward.

Pinging @humitos as you've had experience with both. I'd be curious to know what you think and what you feel your experience is on both of these libraries.

humitos commented 11 months ago

We probably have more shared understanding of web components now than Knockout

This my case. I never used Knockout deeply enough to feel comfortable and I'm still not sure how it works. I maybe able to modify/adapt something, but I don't know how to start from scratch.

There are some views that would translate easily. There are also some views that would require some more thought -- mostly views with nested views like model listing pages.

I'm not sure how Knockout is currently being used and how clean that code is. Creating a small summary of where and how it's used would be useful to have this conversation with more clear understanding.

I'm thinking about the new notifications since it's what I'm currently working on the backend and it will require new code on the front end. If we implement this with Web Components, I imagine it will be pretty similar to what we are doing with any of the addons:

What are other use cases for Knockout/Web Components you've used or thinking about?

That said, and without too much knowledge on Knockout, I'm 👍🏼 on using Web Components since it's "the new thing", but also it's what we are already using for the addons --which is also a long term project we will need to maintain.

agjohnson commented 11 months ago

Creating a small summary of where and how it's used would be useful to have this conversation with more clear understanding.

This is documented in a fair amount of detail at:

If you haven't had a look through those docs, they have a fair amount of information about the whole project.

If we implement this with Web Components, I imagine it will be pretty similar to what we are doing with any of the addons

Yeah, it should be really close. Knockout isn't just used to consume API data either, it's used any place that JS influences the DOM, or when we need to pass in information to the KO view from the backend.

What are other use cases for Knockout/Web Components you've used or thinking about?

So, big views like the build detail page are heavy on KO, but you'll also find KO views for things like popup cards, buttons on the project listing, and other small chunks of UI.

That said, and without too much knowledge on Knockout, I'm 👍🏼 on using Web Components

Me too. I also appreciate that web components are so basic, compared to Knockout/Vue and definitely to bloated frameworks like React.

My only real concern is in JS bundle size. I might try a quick test to see what the size increase looks like first. We could absorb the larger bundle, split Lit out to a vendor bundle, or decide that this is a larger project and skip Lit for now.

I'd still say that a larger project to revamp the old Knockout code is a long term project, but we could start by writing new code as Lit components, starting with notifications perhaps? We do already have some KO code for build detail and notifications, so perhaps this is not needed at this time either way.

agjohnson commented 11 months ago

I found 28kB is added to the vendor library (522kB inflated to 550kB) and 1kB to the application JS.

So, about 30kB more to support basic Lit and Knockout simultaneously. I would expect the end result from switching Knockout to Lit would be pretty comparable, as Knockout isn't huge either. jQuery is used from FUI/SUI so that will continue to be the largest chunk of the vendor system.

humitos commented 11 months ago

Knockout isn't well maintained and TKO is probably never going to gain serious adoption with other modern options available

Just for reference here: https://github.com/knockout/knockout/issues/2608#issuecomment-1853648951

agjohnson commented 8 months ago

Thinking just a bit more here as I am looking at notifications, I think there are some UI components that make a lot of sense as web components. Notifications, popupcards, etc are very digestible pieces of UI that don't need to be KO.

But web components don't feel like a replacement for larger views yet. For example, replacing the project listing view (which is a Knockout view) with a web component would push a lot of logic, that is fairly well defined in templates, into Lit specific JS instead.

One pattern I'd like to experiment with here before considering this a bad fit is to define views like the ProjectListView as a Lit element and use <template> in a slot. This would keep some of the authoring in HTML templates and move us towards Lit template syntax instead. I'm not certain this will work nicely though.

We could use web components for small, digestible chunks of UI, but then we are writing with both Lit and Knockout. I still have questions here, as much as I would like to use something lighter weight.

agjohnson commented 7 months ago

Another thought I've just had here is that interaction between elements seems like it could be as simple as:

// Call a method on a remote element
document.querySelector("readthedocs-notification").render();

I still feel there is more work required to understand how large views/nested web components work out though.

humitos commented 7 months ago

But web components don't feel like a replacement for larger views yet. For example, replacing the project listing view (which is a Knockout view) with a web component would push a lot of logic, that is fairly well defined in templates, into Lit specific JS instead.

I don't think this is necessarily bad, but I think I would need to see a real component of these ones to understand its complexity 😅

One pattern I'd like to experiment with here before considering this a bad fit is to define views like the ProjectListView as a Lit element and use <template> in a slot

This doesn't sound good to me 🙈 . I'm seeing it more complex than useful --mainly because we will end up with logic on the HTML template that will change things inside the web component itself. In that case, I'd prefer to have all the logic inside the web component and use all the Lit helpers to build the resulting HTML to display to users.

agjohnson commented 7 months ago

mainly because we will end up with logic on the HTML template that will change things inside the web component itself.

Yeah, I would avoid this sort of pattern specifically too. The pattern I'm describing is maybe hard to illustrate, but it is more about adding in FUI HTML structure than web component logic. Very loosely:

<readthedocs-build-detail id={{ build.pk }}>
  <template>
    <div class="ui segment">
      <div class="ui grid or something">
        <readthedocs-build-detail-header></readthedocs-build-detail-header>
      </div>
      <div class="ui grid or something">
        <readthedocs-build-detail-commands></readthedocs-build-detail-commands>
      </div>
    </div>
  </template>
</readthedocs-build-detail>

That is, wrapping much of the template in <readthedocs-build-detail> which would do the API polling, and the child elements would use this data response. I'd much prefer to keep as much as possible in the HTML template, though we certainly could do all of this in a web component too.

Anyways, it's something to explore, I am also skeptical of this pattern so far. This would be the one use case and pattern keeping us tied to KO for the immediate future.