There are many well-known open source javascript template engines in the front-end world, such as Handlebars, Nunjucks, EJS, etc. Many of you are familiar with them.

The state of the JS template engine

In general, these JS template engine projects have one thing in common: they only focus on rendering strings (HTML).

As early as a few years ago, Backbone and other MV * frame popular, JS template engine met their spring, because Backbone can support matching user preferences of the template, and provide access scheme. However, with the popularity of a new generation of front-end MV * frameworks today, more attention is paid to how powerful the React JSX logic is, how convenient Vue v-show and other commands are to use, and what about the JS template engine? They seem to find their home only on the Node.js server side, and React and Vue SSR(server rendering) continue to eat into what little market there is.

Why do the various JS template engines focus only on rendering HTML strings? This may have something to do with history, after all, there was no virtual DOM 5 or 6 years ago, and rendering the DOM using the $(‘div’).html(STR) method of frameworks like jQuery was a matter of course.

New JS template engine

If only the JS template engine could render virtual DOM objects in the current era, it might find opportunities to reuse them again:

+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + ¦ < the Template string / > ¦ + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + | | + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + | render to | | | + -- -- -- -- -- -- -- -- -- -- -- -- -- + + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + ¦ HTML string ¦ ¦ React virtual dom ¦ + -- -- -- -- -- -- -- -- -- -- -- -- -- + + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +Copy the code

However, there is a new JS template engine that can do this and support rendering HTML and React components at the same time. It is called NornJ.

Github:github.com/joe-sky/nor…

Github Pages: Joe-sky.github. IO/Nornj-Guide

Official documentation (Gitbook) : Joe-sky.gitbooks. IO/NornJ-guide

The installation

npm install nornj
npm install nornj-react   Install this package together
npm install nornj-loader  # WebPack environment Install this package together
Copy the code

Online demo address

Render HTML Strings

  • Online Playground (jsfiddle)
  • Online Playground (codepen)

Render the React component

  • Online Playground (jsfiddle)
  • Online Playground (codepen)

Basic usage in React development

React often introduces itself as “optional”, but in reality React is almost impossible to develop without JSX. JSX will truly become the technology of choice if there is another DSL(JS template engine) available for React development.

NornJ’s template syntax is based on many famous projects such as Handlebars, Nunjucks, Vue and so on. It also has its own unique syntax such as tagged Template String, custom statements and operators, which is very similar to HTML + JS and can be used quickly. React-templates is another React template project that needs to be mentioned. It is the only template project in the React ecosystem that is well established, but unfortunately it is now barely maintained and has very limited functionality.

Each React component needs to render a drop-down box in the HelloWorld component, using JSX and NornJ syntax:

  • JSX
export default class HelloWorld extends Component {
  render() {
    return (
      <div className="hello" style={{ width: 300.height: 200}} >
        <input type="text" />
        <select>
          {[1, 2, 3].map((item, i) => i > 1
            ? <option>{item + 1}</option>
            : <option>{item}</option>
          )}
        </select>
      </div>); }}Copy the code
  • NornJ
import { template as t } from 'nornj';
import 'nornj-react';
import { Input } from 'antd';

export default class HelloWorld extends Component {
  render() {
    return t` <div class="hello" style="width:300px; height:200px;" > <input type="text"> <select> <#each {1 .. 3}> <#if {@index > 1}> <option>{@item + 1}</option> <#else><option>{@item}</option></#else> </#if> </#each> </select> </div> <${Input} placeholder="Basic usage" />
    `; }}Copy the code

For example, this is the most basic way to use NornJ, right out of the box. It can use ES6+ tagged Template literals syntax to describe templates in JS files. Template syntax is more readable than JSX syntax in processing logic, and the syntax is more similar to HTML:

  • You can write class instead of className.
  • Style can be written in the same way as the STYLE attribute in HTML, and writing objects is also supported.
  • Template syntax is provided#if,#eachSuch extension tags are used for processing logic and can be replacedTernary operatorwithArray map method.
  • Tags such as input and img support only open tags, such as<input type="text">, JSX must be written as<input type="text" />.
  • You can directly return multiple tags at the same level in the render component without having to wrap an array around them.
  • Double curly braces{{}}And single flowers around{}The React syntax is supported throughout the React development, depending on personal preference except for special scenarios.
  • Templates, like JSX, support embedding arbitrary JS variables, which also include third-party React components and JSX variables.NornJTemplates and JSX can coexist!

Tagged Template Literals syntax for NornJ can be found in the documentation for more details.

The above example could also be rewritten like this:

import nj from 'nornj';
import 'nornj-react';
import { Input } from 'antd';

const tmplFn = nj` <div class="hello" style="width:300px; height:200px;" >... </div> <${Input} placeholder="Basic usage" />
`;

export default class HelloWorld extends Component {
  render() {
    returntmplFn(); }}Copy the code

Tagged Template literals syntax is essentially creating a template function and executing it in render. Using the first method to execute a template function in Render would be a performance penalty if the template was compiled every time render was executed (with all the regular disextracts involved)? No, because NornJ templates are cached at compile time, only the first render will be cached after that.

In addition, NornJ and JSX can be written in nesting, and there is no problem using NornJ templates only at a very small granularity, as described in the official documentation.

Single file template

NornJ templates can be written in a separate template file, in addition to js files, to separate the component (or page) presentation layer from the structure layer (see the official documentation for details). For example, write a helloworld.nj.html file:

<template name="partial">
  <ant-Input placeholder="Basic usage" value={value} />
</template>

<template name="helloWorld">
  <div class={styles.hello}>
    <select>
      <#each {1 . 3} >
        <#if {@index >1} ><option>{@item + 1}</option>
          <#else><option>{@item}</option></#else>
        </#if>
      </#each>
    </select>
  </div>
  <#include name="partial" />
</template>
Copy the code

It can then be used after being introduced in a js file:

import tmpls from './helloWorld.nj.html';

export default class HelloWorld extends Component {
  state = {
    value: 'test'
  };

  render() {
    return tmpls.helloWorld(this.state, this.props); }}Copy the code

As mentioned above, one or more template tags can be defined within each *.nj.html file. These template tags are parsed by nornj-Loader in the js file that references them, generating a set of template functions with the name attribute of the template tag as the key. Calling them in the render of each component generates the corresponding React VDOM object.

For NornJ’s single-file templates, we also provide some IDE syntax highlighting and tooltips.

Extension template

Similar to Handlebars, NornJ is very extensible. #if, #each, etc., are actually extended syntax. You can extend #customIf, #customEach, etc. In terms of extensibility, it is not hard to imagine that JSX could also be extended with Babel and new syntax could be created, such as JsX-Control-statements. However, Babel extension is not easy to learn, and it is not easy to learn the various uses of Babel AST and develop a perfect plug-in.

Since NornJ inherits Handlebars extensions, each extension inside NornJ can be easily developed with a function, such as extending the ** operator for NornJ to perform the power operation:

import nj from 'nornj';

nj.registerFilter('* *', (val1, val2) => Math.pow(val1, val2));
Copy the code

Then you can use it directly:

<input value="{2 ** 10/100}">
Copy the code

Of course, the above is only the simplest example, please refer to the official documentation for more descriptions of template extensions.

Create bidirectional data binding with Mobx

With the extensibility of NornJ, the template syntax can theoretically achieve unlimited possibilities. # mox-model is an inline extension tag implemented by NornJ (similar to vue and ng directives). It is used as follows:

import { Component } from 'react';
import { observable } from 'mobx';
import nj from 'nornj';
import 'nornj-react';
import 'nornj-react/mobx';

class TestComponent extends Component {
  @observable inputValue = ' ';

  render() {
    return nj`<input :#mobx-model="inputValue">`(this); }}Copy the code
  • Mobx-model Online Examples (Jsfiddle)
  • Mobx-model Online Example (Codepen)

The underlying implementation of # mox-Model is similar to Vue’s V-Model. React has other bidirectional binding implementations such as Mota, but this project is implemented through higher-order components. Using NornJ’s extension syntax, we can implement more extensions like # mobx-Model.

Combine React’s ecology

NornJ can seamlessly integrate various React ecosystems, including React-Native, Redux, React-Router, Mobx, Ant Design, etc. It can coexist with any existing React ecosystem.

Please refer to the official documentation for more details.

Works with various React-like libraries

NornJ can theoretically be adapted to any React-like library, including Preact, Inferno, ANu, Nerv, etc.

For details about adaptation methods, see the official documents.

Render HTML Strings

NornJ also supports rendering HTML strings, which is exactly the same as a traditional JS template engine. React works almost exactly the same as React.

  • Online Playground (jsfiddle)
  • Online Playground (codepen)

NornJ also supports Node.js server Express and Koa. NornJ also supports the compile, render and other methods of traditional JS templates.

See the official documentation for more details.

NornJ’s future plans

NornJ has many areas that we can enhance in the future, such as:

  • Develop the ESLint plugin

NornJ currently has an internal syntax error warning mechanism, but only prints errors on the console. For static syntax error detection, NornJ will need to develop an ESLint plug-in in the future.

  • Continuous performance optimization

Although NornJ’s current template rendering efficiency is not low (see template rendering efficiency Test), there is still a lot of room for optimization, and the optimization work will continue.

  • internationalization

  • Provides adaptation to Vue

NornJ for Vue is a tentative idea, theoretically possible. But there are a few snags:

  1. The use of VueVirtual domThe object structure is not as simple as React; it uses a similar structuresnabbdomIn which methods such as events are tied to special objects. theNornJReact is a bit harder to adapt to than React.
  2. Vue’s template syntax is already pretty good, thoughNornJIt provides some unique syntax of its own, but it probably doesn’t improve the development experience as much as React does.

NornJ today has a lot of powerful features, and will continue to improve, welcome to try.