Saturday, September 30, 2023

Unopiniated variable binding

I'm a bit crazy. I like vanilla front-end work. I just like it when you do not need a compile step and what you've written is what is executed. I've always been like that, even when it made a lot less sense than it does right now, like in the times of Internet Explorer and browsers having lots of incompatibilities and some not supporting standards well at all.

In the light of using vanilla javascript, seeing the technology making progress is really nice. Sometimes, that means that you get shiny new tools built into the browser. Other times it means that you no longer need a library to do what you were already doing. I loved learning about document.querySelector(), as it covered one of the two things I used jquery for. The other thing I used it for was later covered by the fetch api. Learning about css variables was far more exciting to me than learning about the css preprocessors of the time.

When I do use a framework (for personal projects), I like using something that isn't very opinionated. The big front-end frameworks generally tell you how to set up your whole project. I prefer something that I can introduce in that one place if I want. And if the project grows big enough, I will refactor it to have a better structure. I probably end up with something similar to what the frameworks prescribe. Or I might end up with something slightly better or a lot worse. In any case, it'll probably be more tailored to my project.

In the past few years, my goto front-end framework has been Knockout.js. When I was first introduced to it at a previous job, I thought it was just another AngularJS with a slightly different syntax, but in the years after that I started appreciating it for how unopinionated it was. There was one major part of its usage that I didn't like, but adding knockout-es5 solved that. The es5 library doesn't work well with knockout's component functionality, though, which is essential once you start refactoring. So I wrote a script that worked around that. The script was a quintessential hack: it does some pretty ugly manipulation and leaves things in a state where it usually works. Together, all of these basically gave me what I wanted: unopinionated variable binding.

The time to let go of knockout has been steadily approaching for a while, though. There really hasn't been much development of the project for years. The project's website has had trouble several times recently. There is tko.js which aims to be the next version of knockout, but development seems stalled in an alpha state and it doesn't actually address most of what I want to see changed. I just don't really see a path towards not needing the knockout-es5 or my own script on top of it, and both of those feel more like temporary fixes than true solutions. For projects that I expect to live for a long time, it just doesn't seem to be sustainable enough to keep using it.

Recently, I have been working on a very old project. This project is built around the google maps API. Because of that, it doesn't actually do a lot of html manipulation. So, even when redoing most of the project six years ago or so, I just did that little bit of manipulation in vanilla javascript. But now I'm adding features, and some of those do require a lot more manipulation.

That sent me into a bit of a panic. Not using a framework was starting to weigh down the project. Using knockout doesn't feel like a sustainable option, but other frameworks are much more opinionated than I like. Even the ones that are minimal and/or proclaim not to be opinionated require you to work in components and thus don't just drop right into your project to do that one task you need from them. I felt like I was at a crossroad with none of the paths leading where I wanted to go.

I think I have a solution, though. I started looking at WebComponents recently. It's a collection of three javascript APIs that are mostly used by frameworks. That's because using them without one is complicated and doesn't really manage to compete with using a framework. I have been using two of those APIs to prototype what is basically my own framework. It provides the unopinionated variable binding that I was looking for and not much more than that.

It's only some three hundred lines of code, but it's able to bind variables to text, attributes and events, and it's able to repeat parts of your html or remove them. About a third of the code is dedicated to a solution for tables, as the default looping solution clashed with the way those are special in html. And because it is built on top of WebComponents, the system to upgrade to when refactoring is built right in.

There's definitely more to be done. When you use the prototype incorrectly, it doesn't tell you what you need to change. There will probably also end up being more things that the library needs to do to. But I do believe this pretty small prototype already covers most of what I want.

I compared total byte size of my prototype to Alpine.js (just because it came up in a search for small frameworks) and the byte sizes are about the same. That's more impressive than it sounds, because we're comparing a minified and gzipped version of Alpine with the plain sources of my prototype. While size comparisons aren't actually that useful, size does matter. It matters in how sustainable maintaining the code base is. It's also a part of how close it is to vanilla javascript, which is a part of how it stays unopinionated and how it can interoperate with existing codebases and other frameworks. For neither of those it's the whole story, but it is an important part of it.

I think I'll be using this for the time being. And I'm starting to think that a version of this may end up being my tool of choice for a long time to come...