small-tpl

A small lightweight but powerful template engine.

Usage no npm install needed!

<script type="module">
  import smallTpl from 'https://cdn.skypack.dev/small-tpl';
</script>

README

small-tpl

A small lightweight but powerful template engine.

Install:npm install small-tpl or yarn add small-tpl

模板语法

<? 开始标签

?> 结束标签

each 遍历数组

endeach 结束遍历

echo 在JS脚本里输出数据

$data 模板内嵌变量,渲染模板的数据对象

$fn 模板内嵌变量,渲染模板的helper对象

$encodeChars 模板内嵌变量,输出内容时需要编码的字符,默认包含4个字符<>"'

$encode 模板内嵌函数,输出内容时对内容进行编码

$indent 模板内嵌函数,输出内容时对内容保持当前缩进量

$item 模板内嵌变量,遍历数组时指向当前数组元素

$i 模板内嵌变量,遍历数组时指向当前数组元素的下标

$count 模板内嵌变量,遍历数组时指向当前数组的长度

= 在“each”外时输出$data对象的属性,在“each”里时输出$item对象的属性;属性值为null或undefined时输出空字符串

=: 与“=”相似,区别在于会用$encode编码内容

=] 与“=”相似,区别在于会用$indent保持当前缩进量

+ 直接字符串连接整个语句块,不做任何处理

API

  • compile(template, [options])

    编译模板字符串为渲染函数。

    options有3个选项:openTag自定义开始标签;closeTag自定义结束标签;uglify是否去除HTML里无用的空白字符,默认true去除。

  • setTag(openTag, closeTag)

    全局设置开始标签和结束标签。

Demo

Hello world

const { compile } = require('small-tpl')

// for pure Object
const render = compile('<?= hello ?>, <?= name ?>!')
render({hello: 'Hello', name: 'girl'})
// => Hello, girl!

// for Array
const render = compile('<?+ $data[0] ?>, <?+ $data[1] ?>!')
render(['Hello', 'girl'])
// => Hello, girl!

标签内可以使用原生JS语法:

<div>
<? if ($data.someProp) { ?>
  <h1>哈哈哈</h1>
<? } else { ?>
  <i>嘿嘿嘿</i>
<? } ?>
</div>

JS代码内可以使用echo语句,上面的代码等同于:

<div>
<?
  if ($data.someProp) {
    echo '<h1>哈哈哈</h1>' // 也可以写成 echo('<h1>哈哈哈</h1>')
  } else {
    echo '<i>嘿嘿嘿</i>'
  }
?>
</div>

三目运算:

<h1><?+ ($data.someProp ? '哈哈哈' : '嘿嘿嘿') ?></h1>

遍历数组:

<h1><?= title ?></h1> <!-- 输出 $data.title -->
<ul>
<? each $data.someList ?>
  <li>
    <?= title ?> <!-- 输出 someList 里每一项的 title,即 $item.title -->
  <? if ($item.subTitle) { ?> <!-- $item 指向 someList 的元素 -->
    <span class="sub-title">
      <?+ $fn.encodeXssChar($item.subTitle) ?>
    </span>
  <? } ?>
  </li>
<? endeach ?>
</ul>

特别注意eachendeach只能放在独立的<? ?>标签内,不能跟其他语句同处一个标签内。除了下面这种情况。

在上面的模板中,如果不确定someList这个属性是否存在,可以像下面这样:

<? each $data.someList || [] ?>
  TODO
<? endeach ?>

对输出内容进行编码,预防XSS攻击:

const render = compile('<?=: content ?>')
render({content: '<script>alert("1")</script>'})
// => &lt;script&gt;alert(&quot;1&quot;)&lt;/script&gt;
// 即可在页面上正常显示:<script>alert("1")</script>

扩充默认的$encodeChars,扩充只影响当前模板:

const render = compile(`<? $encodeChars['b'] = 'B' ?><?=: content ?>`)
render({content: 'abc'})
// => aBc

手动调用$encode函数:

const render = compile(`<?+ $encode('Encode: ' + $data.content) ?>`)
render({content: '<b>abc</b>'})
// => Encode: &lt;b&gt;abc&lt;/b&gt;
// 页面上正常显示:Encode: <b>abc</b>

Deep

compile('<p><?= hello ?>, <?= name ?>!</p>')

// 模板将被编译为如下函数
function anonymous($data, $fn) {
  'use strict';
  var $_get = function (data, key) {
    return data[key] || (data[key] == null ? '' : data[key]);
  };
  var echo = '<p>'+ $_get($data, 'hello')+ ', '+ $_get($data, 'name')+ '!</p>';
  return echo
}

compile(`
  <h1><?= title ?></h1>
  <ul>
  <? each $data.someList ?>
    <li>
      <?= title ?>
    </li>
  <? endeach ?>
  </ul>
`)

// 模板将被编译为如下函数
function anonymous($data, $fn) {
  'use strict';
  var $_get = function (data, key) {
    return data[key] || (data[key] == null ? '' : data[key]);
  };
  var echo = '<h1>'+ $_get($data, 'title')+ '</h1><ul>';
  ~function () {
    var $_list = $data.someList, $count = $_list.length, $i = 0, $item;
    for (; $i < $count; $i++) {
      $item = $_list[$i];
      echo += '<li>'+ $_get($item, 'title')+ '</li>'
    }
  }();
  echo += '</ul>';
  return echo
}

compile('<p><?=: content ?></p>')

// 模板将被编译为如下函数
function anonymous($data, $fn) {
  'use strict';
  var $_get = function (data, key) {
    return data[key] || (data[key] == null ? '' : data[key]);
  };
  var $encodeChars = {
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  };
  var $encode = function (s) {
    if (typeof s === 'string') {
      var res = '', i = 0, l = s.length;
      for (; i < l; i++) {
        res += $encodeChars[s[i]] || s[i];
      }
      return res;
    }
    return s == null ? '' : s;
  };
  var echo = '<p>'+ $encode($_get($data, 'content'))+ '</p>';
  return echo
}