Awesome FOSS Logo
Discover awesome open source software
Launched 🚀🧑‍🚀

JS component-centric libraries and my rationale for considering MithrilJS

Categories

JS component-centric libraries, and why I’m considering MithrilJS for some major work

Recently I’ve picked up a contract that requires taking a relatively old server-side rendered codebase with some JS bolted on to a more “modern” front end javascript stack. As those who do front end will immediately notice, just the notion of a “modern” front end JS “stack” can mean so many things (when it probably shouldn’t). There is a plethora of approaches, libraries, frameworks, ideologies to be found on the front end (possibly representative of programming in any niche as a whole).

In this veritable forest of options, My basic plan is to make the codebase just a little fancier, but still recognizable to those that don’t particularly love doing front end development. Since the application is company-internal, a few goals are intenteded:

  • Make it easier to see interdependencies of various pages/files
  • Reduce LOC
  • Increase re-usability of UI components across the backend
  • Move away from depending on server-side rendered data being manipulated server-side rather than just being passed straight to the JS.

When I need something a little bigger and with more than 5 developers who might be in on the project, I reach for Ember. For all my smaller projects, however, I generally reach for what I like to call “component-centric” libraries, which are pretty plentiful these days.

What characterizes a “component-centric” library?

If you consider Angular 1 to be the first fully-fledged framework to really reach widespread acceptance and use, MVC (Model View Controller) is generally the largest/heaviest form of methodology that is widely employed on the frontend. A step below MVC are the various MV* frameworks (ex. KnockoutJS) that replace or do away with controllers for something else.

Component-centric libraries for me one step below MV* frameworks like KnockoutJS in that they provide solely the View portion of MVC. As is obvious, they’re completely (and often only) concerned with managing the aspects related to rendering components of a page. Surprisingly, component-centric libraries are often all I find that I need, especially if the data you’re wrangling is relatively straight forward and doesn’t need much massaging.

A few of these libraries have caught my eyes over the years, and I generally use new projects as a chance to get familiar with them:

  • Rivets - Never used
  • Riot - Never used
  • Mithril - Never used
  • Vue - Used for non-trivial applications
  • React - Used for non-trivial applications
  • Polymer - Never used

What value do component-centric libraries provide?

In terms of value, component-centric libraries provide primarily the following:

Componentization

These days I feel the holy grail of front end development (or web applications) is the ability to write a component, and use it everywhere, as well as reduce/eliminate duplicate work done by implementing similar functionality. With the isolation of components and the hieararchy of components that ensues, it’s also possible to test the components in isolation, with mocked data, to make sure that they work, creating a functional “unit” of UI that is smaller than a rendered page (which would likely be the unit otherwise).

A good component-centric framework also has a convincing and well-thought-out approach to passing information from component to component, whether from parent to child, child to parent, and between unrelated components.

Another feature I generally look for/appreciate is the inclusion of support for custom components. This lets you write code like:

<todo-list :data="todos">
    <slot name="todo-list-item">
        <todo-list-item>...</todo-list-item>
    </slot>
</todo-list>

The above example is Vue-inspired though the syntax is a tiny bit different in practice.

Simplicity

If something is billed as a “library”, I strongly believe and expect it to be simple to use with a consistent API and clear/straightforward integration points. “Simple” means a lot of things to a lot of people, but I generally characterize it as the following:

  • Transpilation should be optional (don’t force new tools on me)
  • No CLI program
  • No excessive amounts of library-specific jargon (please no cute names for leaky abstractions)
  • Zero surprise form a simple hello world/todo list app
  • No downloading of a “boilerplate” zip file or needing to run some boilerplate generation tool.

Again, violating these things I look for doesn’t mean a library is “bad” it just means it doesn’t satisfy my requirements for being simple.

START RANT

React breaks a bunch of these rules, and for that reason, I am very against teaching it to beginners (which unfortunately is what everyone seems to be doing these days). React is simple in concept (as any component-centric library is) but there is certainly complexity inside it, close to the surface, that isn’t well hidden enough for beginners to easily grasp. For illustration of my point, compare getting started with KnockoutJS to getting started (from scratch) with React. For added effect, maybe have someone who has no experience with frontend libraries/frameworks/build tools.

There are also some things in React that should be simple, but actually aren’t, and some ugly warts on the API in places. I’m going to avoid going into it here, but feel free to pick up and start using react for yourself to prove me wrong :)

END RANT

If you haven’t watched Rich Hickey’s talk on simplicity vs ease, absolutely stop reading this article and go watch it. It will make you a better developer, guaranteed.

Even if a framework passes all the requirements I set forward for being “simple”, it is important to be careful to note whether the framework simply makes the things “easy” or if they’re truly simple to understand and to perform.

Simplicity is also important to consider from teh angle of someone who is NOT a javascript expert (or even likes working on web applications). It is up to experienced front end developers to be considerate and think twice before moving a dead-simple HTML/CSS/JS codebase to Web3.0 babel transpilation async loader ES6 goodness. Yes, the new tech is a step forward in many ways, but you’re going to introduce a ton of build and runtime complexity that others may not as easily swim through.

On the upside, I guess it does increase job security, I guess.

Data management

Component-centric libraries often also at least should have a plan (or show that they considered) how to pass data in/out of their components and how components will interact with the sources of their data in the long run. While it’s not imporant to have a fully fleshed out plan and integration points for data management, it’s important that a framework at least thinks about this, as almost 99% of usecases will involve data of some kind coming from somewhere that is NOT also a component.

This sometimes implies some sort of data-binding, but not necessarily in the case of libraries like React and Mithril. KnockoutJS, for example, provides all the tools you need to make a free-standing object that reliably fetches data from the backend and does necessary transformations on it, by expositin the simple and easy-to-use Observable primitive. Libraries like React take a slightly different approach in that when any data changes at all, the component completely re-renders, which is a little less granular, but can also make things much simpler.

It’s also important for the data-management/data-binding system to not be very surprising. KnockoutJS is excellent at this, in that once you have some object that’s an Observable, though the interface for modifying it changes a little (it’s a function), you can generally change it from anywhere inside or outside the normal KnockoutJS flow, as the object handles triggering of all necessary re-renders.

What about the rest of the things…?

Generally, a “full-fledged” framework would also offer some principled way to organize/perform data fetching, caching, etc. As component-centric libraries don’t really offer this, the question of how to do all these things often arise.

Services/Resources

Basically, in most of the applications I write, these are basically things that do some request to retrieve some data from the backend. Sometimes they correspond to on-page data (for example a PageScrollService might keep track of the scroll position of the current page whenever it’s used for relevant components to use/watch).

While using component-centric frameworks, I often just create relatively simple JS objects that have the necessary methods to do requests and obtain data.

Controllers

Controllers are generally used for doing page-wide logic that doesn’t quite fit into a single specific component, but might have to deal with coordinating multiple components on the same page.

As most have come to think, I really don’t find controllers necessary, and find that they can just be replaced with a PageComponent or something similar. This gives you the isolation you love for components and the fuzzy feeling that comes from finding a proper place to put component-coordinating logic. While I don’t know exactly where the effort stands today, Ember has all but removed controllers from the framework (though last I checked, routable components weren’t a thing yet).

So, Mithril fits this bill?

All this said, let’s return to the original thrust of the article: is MithilJS a good candidate for the front end framework to use in transforming this aging codebase?

I think Mithril fits the bill largely for all these points. While the original iterations of mithril came with a more complete plan for data management, the basics of Mithril are simple (and have stayed simple), and I’m encouraged to try it out. Though it doesn’t seem to have a KnockoutJS-level simple reactivity/data-binding system, it seems to be of very-little surprise.

The one huge downside in using Mithril is it’s templating story. While it is great that it doesn’t require something like JSX (which React will often SAY it doesn’t require, but I’ve never seen anyone skip in a codebase of any size). Basically, Mithril requires you to write render functions that can be hard to read if you’re not used to the concept of expressing DOM elements with javascript. You also lose a bit of the searchability of HTML and some expressiveness. Straight from the Mithril docs:

m("main", [
    m("h1", {class: "title"}, "My first app"),
    m("button", "A button"),
])

Turns into the folowing HTML:

<main>
    <h1 class="title">My first app</h1>
    <button>A button</button>
</main>

Unfortunately, to get a more ergonomic HTML writing experience, some sort of bundler or pre-compilation has to happen. It also doesn’t really seem reasonable to push the template rendering to the client side, so I feel stuck with the JS only syntax. This isn’t a huge deal, but can be jarring for people who are used to a codebase in which they can just search for HTML without deep understanding of JS.

So what?

This was a quick introduction to how I think about this subject, as well as how I generaly vet new (and old) component-centric frameworks. I find that this rubric works for me and helps me to pick the winners from the losers in the large rapidly-changing field that is front end development.

Mithril fits the bill in most ways, and seems to give me all the value that I generally look for in a component-centric system, so I’m weighing it heavily. I’m also weighing KnockoutJS’s take on components very heavily, since components can be expressed as HTML and used with a require plugin to just feed text in.

One of the big sticking points is the ergonomic differences between Mithril’s redraw system and KnockoutJS’s Observables+redraw systems. Observables are almost certainly the system of least surprise for me, but it’s yet to be seen if mithril lives up to it’s promises for simple data flow (and thus simple components). There’s just something nice about knowing when I change some Observable, the changes are going to propogate like I expect, no matter where I do it from, and the redraws I expect to happen will happen.

Sometimes this makes me think, however, that the comfort with which I look upon observable-based reactivity/data-binding is actually more of a crutch, allowing me to write code with bad data-flow, but still get the results I want.

Time will tell which approach is better, maybe I’ll write another blog post in the far away future where the conversion is done, and I have the holy grail firmly in my possession.