使用新一代js模板引擎NornJ提升React.js开发体验

VioletBblythe 发布于6月前 阅读107次
0 条评论

当前的前端世界中有很多著名的开源 javascript模板引擎HandlebarsNunjucksEJS 等等,相信很多人对它们都并不陌生。

js模板引擎的现状

通常来讲,这些js模板引擎项目都有一个共同的特性: 只专注渲染字符串(html)

早在几年前Backbone等 mv* 框架流行的时候,js模板引擎遇到了它们的春天,因为Backbone可以支持选配用户自己喜好的模板,并提供了接入方案。但是在新一代前端 mv* 框架盛行的今天,人们更多的关注点在于React的 JSX 支持的逻辑何等地强大、Vue的 v-show 等指令使用起来多么地方便,而js模板引擎呢?它们似乎只能在Node.js服务器端找到它们的归宿,而且还被React及Vue的 SSR(服务端渲染) 继续蚕食着仅有的市场。

为什么各种各样的js模板引擎都只专注于渲染html字符串?这或许跟历史原因有关,毕竟5、6年前的时候并没有 虚拟dom ,使用jQuery等框架的 $('div').html(str) 方法渲染dom是理所当然的事情。

新型js模板引擎

我们不妨试想一下,其实js模板引擎在当前的时代只要也能做到渲染 虚拟dom 对象,或许就可以再次找到它们被重用的机会:

+---------------------+
         ¦ <Template string /> ¦
         +---------------------+
                    |
                    |
         +---------------------+
         |      render to      |
         |                     |
  +-------------+    +-------------------+
  ¦ html string ¦    ¦ React virtual dom ¦
  +-------------+    +-------------------+

然而目前有一个新的js模板引擎可以做到上述的 同时支持渲染html和React组件 ,它就是 NornJ

NornJ 是我们京东Y事业部供应链协同部前端团队开发的新一代js模板引擎,它已经在京东Y事业部下的几十上百个项目中经历了两年的战火洗礼。如今,它的API已稳定,各种功能也趋于完善。

github: https://github.com/joe-sky/nornj

官方文档(github pages): https://joe-sky.github.io/nornj-guide/

官方文档(gitbook): https://joe-sky.gitbooks.io/nornj-guide/

安装

npm install nornj
npm install nornj-react   # React开发请一起安装此包
npm install nornj-loader  # webpack环境请一起安装此包

在线演示地址

渲染html字符串

渲染React组件

在React开发中的基本使用方法

React在介绍自己时常说JSX是"可选的",但实际上,脱离了JSX的React根本就几乎无法正常地开发。如果有了另一种DSL(js模板引擎)可适配React开发,那么JSX才能真正地成为可选的技术。

NornJ 的模板语法在参考自 Handlebars 、 Nunjucks 、 Vue 等多个著名项目的基础上,也有很多自己独特的语法如 tagged template string 、 自定义语句与运算符 等等,与html+js非常相似可快速上手。需要提一下另一个 React 的模板项目 react-templates ,它是 React 生态中唯一一个比较完善的模板项目,但很可惜的是它现在已经几乎不维护了,而且功能非常有限。

每个React组件都须要在render返回组件的标签代码,如在 HelloWorld 组件中渲染一个下拉框,用 JSX 和 NornJ 的语法分别实现:

  • 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>
    );
  }
}
  • NornJ
import nj from 'nornj';
import 'nornj-react';
import { Input } from 'antd';

export default class HelloWorld extends Component {
  render() {
    return nj`
      <div class="hello" style="width:300px;height:200px;">
        <input type="text">
        <select>
          <#each ${[1, 2, 3]}>
            <#if {@index > 1}>
              <option>{this + 1}</option>
              <#else><option>{{this}}</option></#else>
            </#if>
          </#each>
        </select>
      </div>
      <${Input} placeholder="Basic usage" />
    `();
  }
}

如上例,这就是 NornJ 最基本的使用方法了,开箱即用。它可以使用 ES6+ 的 tagged template string 语法在js文件中描述模板,模板语法在处理逻辑时的结构比 JSX 更加易读,且语法和html更为接近:

  • 不必写className,用class就好。
  • style不必非写成对象形式,可以使用html中写style属性的方式。当然写对象也同样支持。
  • 模板语法提供了 #if 、 #each 等扩展标签用于处理逻辑,不必再写 三目运算符 与 数组map方法 。
  • input和img等标签支持只写开标签,如 <input type="text"> ,JSX中一定要写为 <input type="text" /> 。
  • 可直接在组件的render中返回同一级别的多个标签,外面不用套上数组。
  • 双花括号 {{}} 和单花括 {} 号语法在React开发中都支持,除特殊场景外依个人喜好而定。
  • 模板和JSX一样支持嵌入任意js变量,这当然也包含第三方React组件和JSX变量, NornJ 模板和JSX是可以共存的!

NornJ 的 tagged template string 语法更多细节请查看 官方文档

上面的例子也可以这样改写:

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() {
    return tmplFn();
  }
}

可以看出,实质上 tagged template string 语法就是创建了一个模板函数,然后再在render中执行了而已。这时不难想到,使用第一种方法将模板函数放到render中执行,这样会不会每次执行render时都进行模板编译(内部涉及各种正则析取)会造成性能下降?并不会,因为 NornJ 模板在编译时会进行缓存,只有第一次render时会进行模板编译,之后的每次render就会走缓存了。

另外, NornJ 和 JSX 还可以嵌套编写,仅仅在很小的粒度使用 NornJ 模板也完全没有问题,具体请见 官方文档

单文件模板

NornJ 模板除了可以在js文件中编写之外,还可以编写在单独的模板文件中,用来做组件(或页面)展现层与结构层的分离( 具体请参考官方文档 )。例如编写一个 helloWorld.nj.html 文件:

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

<template name="helloWorld">
  <div class={styles.hello}>
    <select>
      <#each {[1, 2, 3]}>
        <#if {this > 1}>
          <option>{this + 1}</option>
          <#else><option>{this}</option></#else>
        </#if>
      </#each>
    </select>
  </div>
  <#include name="partial" />
</template>

然后可以在js文件中引入后使用:

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

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

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

如上,每个 *.nj.html 文件内都可以定义一个或多个 template 标签。这些 template 标签会在引用它的js文件中通过 nornj-loader 进行解析,生成一个以 template 标签的 name 属性为key的模板函数集合对象,在各个组件的render中调用它们就会生成相应的 React vdom 对象。

针对 NornJ 的 单文件模板 ,我们也提供了一些IDE的 语法高亮与提示工具

扩展模板

NornJ 与 Handlebars 比较类似具有非常强大的可扩展性, #if 、 #each 等实际上都是扩展出来的语法,您完全可以自己扩展出 #customIf 、 #customEach 等新语句。在可扩展性这一点上,不难想到JSX也可以通过babel进行扩展,也可以搞新的语法出来,例如 jsx-control-statements 。但是babel扩展上手门槛不低,要学各种babel AST的用法,开发一个完美的插件出来似乎并非易事。

由于 NornJ 继承于 Handlebars 的扩展方式,它内部的每个扩展都可以用一个函数简单地开发出来,例如为 NornJ 扩展一个 ** 运算符,作用是乘方运算:

import nj from 'nornj';

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

然后就可以直接使用了:

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

当然上述只是个最简单的例子,更多模板扩展描述请参考 官方文档

结合Mobx创建双向数据绑定

利用 NornJ 的可扩展性,模板语法在理论上可以实现无限的可能性。 #mobx-model 是 NornJ 实现的一个行内扩展标签(类似于vue及ng的指令),具体用法如下:

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

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

  render() {
    return nj`
      <input #mobx-model={inputValue} />
    `(this);
  }
}

#mobx-model 的底层实现方式和Vue的 v-model 是比较类似的。React也有其他双向绑定的实现如 Mota ,但该项目的实现方式是通过高阶组件。利用 NornJ 的扩展语法,我们还能实现更多类似于 #mobx-model 的扩展功能。

结合React的各种生态

NornJ 可以完美结合各种React生态,包括 React-Native 、 Redux 、 React-Router 、 Mobx 、 Ant Design 等等,它可以和任何已有的React生态共存。

更多详细文档请见 官方文档

适配各种React-Like库

NornJ 在理论上可以适配任意React-Like库,包括 Preactinfernoanu ,我司近期开源的 Nerv 也可以适配。

具体适配方式请见 官方文档

渲染html字符串

NornJ 同时还支持渲染html字符串,这和传统的js模板引擎就完全一样了。使用方法和React中几乎完全一样,具体请看这个在线实例:

当然, NornJ 也能够支持Node.js服务器 Express 及 Koa 等。传统js模板的 compile 、 render 等方法, NornJ 也支持。

更多细节请见 官方文档

NornJ的未来计划

NornJ 在未来我们还有很多可以增强的方面,例如:

  • 开发eslint插件

NornJ 目前虽然内部有语法错误警告机制,但只是将错误打印在控制台。对于静态语法错误检测, NornJ 将来有必要开发一个eslint插件。

  • 性能持续优化

虽然 NornJ 目前的模板渲染效率已然不低(请见 模板渲染效率测试 ),但仍尚有很大的优化空间,会持续进行优化工作。

  • 国际化

  • 对Vue提供适配

NornJ 适配Vue暂时是个设想,理论上是可以实现的。但有几个难题:

  1. Vue使用的 虚拟dom 对象结构并非React那样简单,它使用类似 snabbdom 的结构,其中的事件等方法都绑在特殊的对象上。这对 NornJ 来说适配起来和React比有一定难度。
  2. Vue的模板语法已然很好用,虽然 NornJ 可以提供一些自身独特的语法,但是提升开发体验的作用恐怕没有在React中那样明显。

查看原文: 使用新一代js模板引擎NornJ提升React.js开发体验

  • biglion
  • blackkoala
  • blackdog
  • goldenswan
  • whitewolf
  • redsnake
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。