A comprehensive comparison of front-end templating solutions

|

0

Templating is a technology that help us to represent data in different forms.

In the old days, choosing a appropriate template engine for client templating is not easy, beacuse you were left with little other choice. Nowadays, choosing templating engine is still a big probelm, beacuse there are so many template engines and most of them seems do nothing different(Template-Engine-Chooser! comes).

In this article, from a principle perspective ,we will have A comprehensive comparison of front-end templating solutions. there are some distinct types of solution that will be mentioned.

  1. String-based templating (String-based parser & compiler)
  2. Dom-based templating (Dom-based link & compiler)
  3. Living templating (combine String-based parser with Dom-based compiler)
  4. Other: Coffeekup: Inner DSL based on coffeescript syntax

The article won’t take the fourth type into detail, except for React. But you will find that react is very similiar with Living templating. beacuse all them is compeletely independent with innerHTML.

Before diving into detail, let’s talking about innerHTML first.

innerHTML

innerHTML is the key through this post,so we need to have a brief review on it. But i don’t think there is any necessity to bring innerHTML into detail, since we are all familiar with it. so let’s talk about the pros & cons directly.

innerHTML is good, without doubt

Before innerHTML becomes web standard, it has been a conventional “practical standard” for serveral years beacuse its irreplaceable advantages. for example.

1 . easy to code and intuitive to view

imagine that you need to append a html like that.

<h2 title="header">title</h2>
<p>content</p>

use innerHTML

node.innerHTML = "<h2 title="header">title</h2><p>content</p>"

compare with the way using Dom API

var header = document.createElement('h2');
var content = document.createElement('p');
h2.setAttribute('title', 'header');
h2.textContent = 'title';
p.textContent = 'content';
node.appendChild(header);
node.appendChild(content);

innerHTML obviously win the game.

Although some frameworks like mootools:Element provide some more efficient way to constructing HTML with Dom API, innerHTML is still the most intuitive way.

2 . it is faster,especially in old IE

> the test maybe out of date in modern browser, the difference between innerHTML and Dom Level 1 is become smaller and smaller.

But we also learned: The recommended way to modify the DOM is to use the DOM Level 1 API.

A great reference to this is Chapter 15 of “Javascript: The Definitive Guide”. Why?

innerHTML is disobedient, sometimes

1. security issues
innerHTML has more security issues , especially if you don’t sanitize what you’re putting into it. for example

document.body.innerHTML = "<img src=x onerror='alert(xss)'/>"

I know you won’t code like this, but if the HTML is not compeltely controlled by yourself(for example : from remote server), it will be a big issue.

2. it is slow
Indeed, I mentioned innerHTML is fast. if you just need change a attribute but replacing all DOM nodes with innerHTML completely, it would be obviously inefficient.

Context is everything

3. not smart enough
it disconnect all existing DOM nodes and rerendering again, all events and state in previous DOM nodes is gone.

4. potential for generating invalid markup with invalid markup.
html parser is so “friendly”, it even accpet invalid html, but developer wont get any ‘parse error ‘during the parsing.

Maybe it’s not innerHTML that is the problem, but constructing HTML with string operations is.

we have thought through innerHTML already, it is time to talk about “templating solutions” now.

String-based templating

It is essentially a way to address the need to populate an HTML view with data in a better way than having to write a big, ugly string concatenation expression.
— cited from http://www.dehats.com/drupal/?q=node/107

String-based templating is the most common solution we ever used. beacuse frontend templating is derivatived from backend, in server side the output must be a string , so the browser can render it.

Example

  1. mustache: less-logic support
  2. Dust.js: rich-logic support
  3. doT.js: super fast

The basic process

String-based Template

As shown above, string-based templating is tightly coupled with innerHTML (for Rendering).

pros

  1. Faster initialize time
  2. Isomorphic: support rendering on server-side and client-side.
    beacuse this solution is compeletely dom-independent.
  3. More powerful template-logic support (all depend on the design of your DSL )

cons

  1. security issue: see innerHTML section
  2. not smart enough: see innerHTML section
  3. performance issue when updating.
    Although the string-based template is become more and more faster beacuse of the intense competition,we need also to take into account the time taken to load the template output into the DOM which is actually the real bottleneck.

Dom-based Template Engine

In recent years, dom-based begun to pop up, the prime example is Angular that earned almost 28000 stars in github.

Example

  1. Angularjs: most popular one
  2. Knockout: the early one
  3. Vuejs: upstart, more concise and aiming to build interactive UI, enough is as good as a feast.

The basic process

Dom-based Template

dom-based template doesn’t have their own parser, so if you need creating view from a template string, you have to use innerHTML to convert the string to dom(parsing), then walk the dom tree using the Dom API(attributes, getAttribute, firstChild… etc). All information like directives is hold by the dom node and its attributes.

In fact, the whole process is more like reshaping than rendering.

pros

  1. output dom is Living.
  2. is runtime efficient.
  3. using directive(or other similar concepts), the coding style is pure declarative, just like you writing html.

cons

  1. have no parser themselves, syntax is restricted by dom and is hard to embed logic in it.
  2. also have security issues beacuse using of innerHTML.
  3. will have some useless placeholder on generated dom , beacuse dom-based template need this information to act operation. for example, if you inspect the angular’s todomvc, you can some placeholder (ng-show, etc) in every nodes.

Living Template Engine

string-based and dom-based template are all tightly coupled with innerHTML, the differrence is: String-based template use innerHTML for Rendering and Dom-based use it for Parsing.

Why not combining String-based parser and Dom-based compiler to abate the dependence on innerHTML ?

In fact, there have been servaral templates that realized in this way .

Example

  1. htmlbar: built on top of Handlebars template compiler.
  2. ractivejs: standalone
  3. Regularjs standalone

The basic process

Living Template

As shown in the picture above, parsing and compiling are similar with String-based template and dom-based template respectively

1 . Parsing

First. it use a builtin parser to parse the template string then output a AST.

for example, template string(syntax base on regularjs)

<button {{#if !isLogin}} on-click={{this.login()}} {{/if}}>
  {{isLogin? 'Login': 'Wellcome'}}
</button>'

will be parsed to:

[
  {
    "type": "element",
    "tag": "button",
    "attrs": [
      {
        "type": "if",
        "test": {
          "type": "expression",
          "body": "(!_d_['isLogin'])",
          "constant": false,
          "setbody": false
        },
        "consequent": [
          [
            {
              "type": "attribute",
              "name": "on-click",
              "value": {
                "type": "expression",
                "body": "_c_['login']()",
                "constant": false,
                "setbody": false
              }
            }
          ]
        ],
        "alternate": []
      }
    ],
    "children": [
      {
        "type": "expression",
        "body": "_d_['isLogin']?'Login':'Wellcome'",
        "constant": false,
        "setbody": false
      }
    ]
  }
]

  1. it is very similar with the string-based template, so we can use more powerful syntax (it is all depend on the DSL you defined).
  2. string-based templates only parse the “dsl element” and consider the “xml element” as the “text”. but in living template, we need parse the “xml” together with the “dsl element” to make it be dom-aware. beacuse we need the infomation for creating living dom.
  3. unlike Dom-based templating, instead of real dom, the AST holds the all information we needed(statement, directive, attributes and tagname…etc).
    1. it is more lightweight, setter and getter on dom is expensive.
    2. it is reusable.
    3. it can be serialized , so you can preparse it on server.
  4. only output the necessary part.
    compare with dom-based template, living template’s output is more clean. inspect regularjs’s todomvc on codepen.io

    <ul id="todo-list">
    <!--Regular list-->
    <li class="completed">
      <div class="view">
    
        <label>sleep</label>
        <button class="destroy"></button>
      </div>
    
    </li>
    </ul>
    
    

2 Compiler

with spcified model (in regularjs, it is a plain object), template engine walks the AST and generating the dom recursively, meanwhile, according to the directive and other binder(event, inteplation… etc), it also create the binding between model and dom to make the dom living.

for example, just like the inteplation {{isLogin? 'Login': 'Wellcome'}} showed above. once the compiler seen it, the expression walker will be called.

// some source from regularjs

walkers.expression = function(ast){
  var node = document.createTextNode("");
  this.$watch(ast, function(newval){
    dom.text(node, "" + (newval == null? "": String(newval)));
  })
  return node;
}

as shown above, once the expression changed, node.textContent(or innerText) will changes synchronous.

Compare to string-based template, instead of innerHTML, it use DOM api(createElement, setAttribute, createTextNode…etc ) to generate the dom. so it is safe.

In fact, in compiling phase, the most difference between Living template and dom-based template is: __dom-based template act a reshaping on dom nodes, But living template is building that according to the resuable AST.

living template’s clever brother —— React

React can be considered as a templating solution,it avoid be coupled with innerHTML by using virtual dom which is created by nested function call(you can also use jsx syntax)

Example

var MyComponent = React.createClass({
 render: function() {
   if (this.props.first) {
     return <div className="first"><span>A Span</span></div>;
   } else {
     return <div className="second"><p>A Paragraph</p></div>;
   }
 }
});

which in regularjs

{{#if first}}
  <div className="first"><span>A Span</span></div>
{{#else}}
  <div className="second"><p>A Paragraph</p></div>;
{{/if}}

Every one thinks in his way, And I prefer using template to describe my structure, do you?

A comparison table

__Warning: __

Contrast /Solutions String-based templating Dom-based templating Living templating
Example Mustache,Dustjs Angularjs, Vuejs Regularjs 、Ractivejs、htmlbars
Syntax ♦♦♦ ♦♦♦
Living Dom X ♦♦♦ ♦♦♦
Security ♦♦ ♦♦♦
SVG support(*1) X ♦♦ ♦♦♦
Dom independent ♦♦♦ X ♦♦
Server Rendering ♦♦♦
  1. no one can compeletely replace another one.
  2. They are not necessarily incompatible, for example, you can use string-based template engine to generate template string for dom-based template.

Reference

  1. Template Engines by @Sendhil
  2. string-templating-considered-harmful
Tags
1, 'include' => $prevPost->ID ); $prevPost = get_posts($args); foreach ($prevPost as $post) { setup_postdata($post); ?>
Previous

1, 'include' => $nextPost->ID ); $nextPost = get_posts($args); foreach ($nextPost as $post) { setup_postdata($post); ?>

Next

Leave a Comment