coffeekup
coffeekup copied to clipboard
Optimize compiled templates
I have been experimenting with optimizing coffeekup templates using UglifyJS to analyze the template source and prerender the html. The idea is to turn this:
function() {
//
// Lots of boilerplate code...
//
html(function() {
head(function() {
return title(this.title);
});
return body(function() {
h1(this.title);
return p('Super fast templates');
});
});}).call(data);return __ck.buffer.join('');
}
into this:
function() {
//
// Much less boilerplate
//
((function() {
text("<!DOCTYPE html>");
text("<html>");
text(function() {
text("<head>");
text(function() {
return function() {
text("<title>");
text(this.title);
text("</title>");
}.call(data);
}.call(data));
text("</head>");
return function() {
text("<body>");
text(function() {
text("<h1>");
text(this.title);
text("</h1>");
return function() {
text("<p>");
text("Super fast templates");
text("</p>");
}.call(data);
}.call(data));
text("</body>");
}.call(data);
}.call(data));
text("</html>");
})).call(data);return __ck.buffer;
}
The advantages of this approach are shorter template functions (unless the template is very long) thanks to reduced boilerplate, and faster execution owing to much of the work being done at compile time. A further performance improvement is achieved by using string concatenation rather than array join to build the output. Here are my benchmark results, running a compiled template 5000 times on node:
CoffeeKup (precompiled): 263 ms
CoffeeKup (precompiled, optimized): 24 ms
Jade (precompiled): 530 ms
haml-js (precompiled): 89 ms
Eco: 92 ms
See here for browser rendering performance: http://jsperf.com/coffeekup-optimized/2
As far as I can tell, these optimizations give a 10x performance boost on node, a 30x boost in Chrome and a more modest 3x improvement in Firefox.
Usage
template = coffeekup.compile 'h1 @title', optimize: yes
template(title: 'Super fast template')
# '<h1>Super fast template</h1>'
Caveats
As this compiler uses static analysis to optimize your templates, too much complex logic may cause it to fall over. But templates should be logic-free so that shouldn't be a problem, right?
I have aimed for API compatibility with regular coffeekup, but there are a few differences to be aware of:
- The
coffeescript
helper function does not add"text/coffeescript"
to the<script>
tag. I could implement this, but it doesn't strike me as very useful. - Output formatting is not implemented (yet). You get a single line of html.
- Arrays are not rendered directly in the template output. Join them into a string before passing them to the compiled template function.
- You can't set up a hash of options and then pass it to the tag function, e.g.
attrs =
name: 'email'
type: 'text'
input attrs
Do this instead:
input
name: 'email'
type: 'text'
I have made some minor modifications to the test suite to account for the above.
Great work!
I wish github had a +1 button, this is awesome! I know it's in https://github.com/gradus/coffeecup now but I just wanted to express my awe :-)
@wmertens
It does. Just type.
+1