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

Posted 高级前端JavaScript

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flex10使用新一代js模板引擎NornJ提升React.js开发体验相关的知识,希望对你有一定的参考价值。

阅读者reader

正文共:5519字 2 图

预计阅读时间: 14 分钟


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

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对象,或许就可以再次找到它们被重用的机会:

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


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

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

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

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

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


安装

npm install nornj

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

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



渲染html字符串

  • 在线Playground(jsfiddle)

  • 在线Playground(codepen)


渲染React组件

  • 在线Playground(jsfiddle)

  • 在线Playground(codepen)

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

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

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

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


  • 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是可以共存的!

NornJtagged 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就会走缓存了。

另外,NornJJSX还可以嵌套编写,仅仅在很小的粒度使用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-modelNornJ实现的一个行内扩展标签(类似于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在线示例(jsfiddle)

  • mobx-model在线示例(codepen)

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

结合React的各种生态

NornJ可以完美结合各种React生态,包括React-NativeReduxReact-RouterMobxAnt Design等等,它可以和任何已有的React生态共存。

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

适配各种React-Like库

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

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

渲染html字符串

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

  • 在线Playground(jsfiddle)

  • 在线Playground(codepen)

当然,NornJ也能够支持Node.js服务器ExpressKoa等。传统js模板的compilerender等方法,NornJ也支持。

更多细节请见官方文档。

NornJ的未来计划

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


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


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

  • 国际化

  • 对Vue提供适配

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

  1. Vue使用的虚拟dom对象结构并非React那样简单,它使用类似snabbdom的结构,其中的事件等方法都绑在特殊的对象上。这对NornJ来说适配起来和React比有一定难度。

  2. Vue的模板语法已然很好用,虽然NornJ可以提供一些自身独特的语法,但是提升开发体验的作用恐怕没有在React中那样明显。

NornJ发展到今天已经拥有了很多强大的功能,日后还会继续完善,欢迎试用。



以上是关于flex10使用新一代js模板引擎NornJ提升React.js开发体验的主要内容,如果未能解决你的问题,请参考以下文章

新一代 javascript 模板引擎

js模板引擎--artTemplate

性能卓越的js模板引擎--artTemplate

模板引擎

JavaScript模板引擎实例应用

JS模板引擎