Migrating from Node to Clojure
The first version of the Prismatic web application used a pretty standard web stack: ExpressJS as a server running on Node.js, Jade as a server-side templating language and Jadevu for client-side templating (and Stylus for CSS, which will be the subject of a future post).
It more or less looks like HTML but opts to use indentation rather than angle-bracket tags for structure. As a nice addition, it allows the use of css-style selectors (e.g.
cdnAsset function to convert those paths into urls that use our CDN rather than being requested directly from the server.
The above is valid Clojure code, not another language whose syntax you have to learn or separate compiler to use. It's the same language you're using to write the core logic of your web server. You trade the whitespace oriented syntax for a data structure oriented syntax, but you gain a lot since you have a full and powerful language at your disposal:
- You can write convenience functions to remove boilerplate more effectively, for instance the
- You can avoid the procedural for-loop to add scripts. Instead, just use map to generate the data for scripts, which is both less code and more functional.
- Since your template is data, you can write 'middleware'-like functions to transform template data.
As an example of this last point, you might notice the absence of
cdnAsset in the Clojure template above. Wrapping every relative asset URL in this function is a bad idea; it is tedious and error prone. Since our Clojure templates are just Clojure data, we can instead transform the data before converting it to HTML to map each resource link to it's CDN variant:
This makes it far easier to ensure that all the assets we want in the CDN are there without having to remember in each of our templates.
Bringing it to the client: ClojureScript
Our migration to Clojure for our web server afforded us the chance to re-examine ClojureScript. We're happy to say that ClojureScript has matured substantially since we first looked at it and we are now running it in our production web app. The first thing we moved to ClojureScript was our client-side templating to do things like render articles and search results on the client from back-end JSON data.
Introducing dommy: Fast Clojurescript Templating
We made a similar transition in client-side templating as on the server: we replaced jadevu with a ClojureScript-native approach very similar to Hiccup. We tried some popular ClojureScript templating libraries (e.g., crate), but couldn't find one with acceptable performance, so we wrote one -- and we're happy to announce that this library, dommy, is open-source. The entire templating library is one small file.
But what about performance?
One potential concern with using ClojureScript is that you're taking a large performance hit relative to something more standard like jQuery. To address this concern, we compared performance of three client-side templating approaches: jQuery, crate, and our library dommy; see our pert test for the code. The task was to generate 10,000 nested structure elements and add them to a DOM parent. Here's the ClojureScript representation of the DOM element in dommy (the crate representation is almost identical):
Of course, we could use something like jQuery templating or another client-side JS template library, but our only goal in comparing against jQuery is to see how much performance we are losing relative to Clojurescript templating.
Here are the number of seconds it took to make 10,000 elements averaged over 3 trials:
jQuery: 1.57 secs
dommy: 2.17 secs
crate: 6.97 secs
So our dommy ClojureScript templating is only roughly 38% slower than jQuery, but 300% faster than crate. With some tuning, we were able to get the expressiveness of Clojurescript in templating and pay relatively little cost above jQuery. Overall, we feel the expressiveness of ClojureScript templating with dommy is well worth the minor loss of efficiency.
Go Use ClojureScript!