前端模板引擎 与 实现 (Handlebars)

1. 前端模板引擎的优势

1. 可维护性(后期改起来方便);

2. 可扩展性(想要增加功能,增加需求方便);

3.开发效率提高(程序逻辑组织更好,调试方便);

2. 前端模板引擎 Handlebars

使用过程

<script src="./js/handlebars-1.0.0.beta.6.js"></script>

3. 创建模板

在

<script type="text/x-handlebars-template" id="myTemplate">
    <div class="demo">
        <h1>{{name}}</h1>
        <p>{{content}}</p>
    </div>
</script>

4. 在 JS 代码中编译模板

// 获取模板
let tpl = document.getElementById("myTemplate").innerHTML;
// 预编译模板
let template = Handlebars.compile(tpl);
//匹配 json 数据内容
let html = template(data);
// 插入模板
document.getElementById("box").innerHTML = html;

3. 实现一个简单的前端模板引擎

我们再总结一下思路: 首先,使用模板引擎的格式来写这么一个模板,

然后经过一个函数处理,过程就是通过while循环,然后捕获到对应的变量的规则,将变量前的内容push到数组中,然后再使用数据进行替换,然后push到数组中,

接着通过字符串方法把前面的字符串截取,留下后面的字符串继续匹配,

最后,我们就可以通过join()方法来实现html的生成了,通过appendChild()就可以顺利插入数组了。

function Template(tpl) {
  let fn, match;
  // 用字符串形式编写代码, 代码换行使用\n, 然后使用 
  // new Function(code.join("\n")) 来执行代码
  // _html 用来匹配 > < " & ' 等符号, 将其进行转换成 html 文本
  // r 是最后要返回的
  let code = ['var r=[];\nvar _html = function (str) { return str.replace(/&/g, \'&amp;\').replace(/"/g, \'&quot;\').replace(/\'/g, \'&#39;\').replace(/</g, \'&lt;\').replace(/>/g, \'&gt;\'); };']
  // 可以使用 { xxx|safe } 这样的表达式表示xxx无需转义
  let re = /\{\s*([a-zA-Z\.\_0-9()]+)(\s*\|\s*safe)?\s*\}/m;
  // 向输出的代码模板中添加命令行, 使得传入的 text 中的 ' \n \r 等被转译成字符串形式, 使 code 可执行
  let addLine = function (text) {
    // 以字符串形式的代码, 将传入的 text 中的 ' \n 
    code.push('r.push(\'' + text.replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '\');');
  };
  // 将传入模板使用 re 正则进行匹配, 得到匹配位置索引与匹配内容, 匹配的是需要替换的变量
  while (match = re.exec(tpl)) {
    if (match.index > 0) {
      // 将 变量前的内容 传入 code 中的 r 数组中
      addLine(tpl.slice(0, match.index));
    }
    // 正则的第二个子表达式 也就是判断是否有 safe
    if (match[2]) {
      code.push("r.push(String(this." + match[1] + "));");
    } else {
      // 变成 html 文本
      code.push('r.push(_html(String(this.' + match[1] + ')));');
    }
    // 裁剪传入模板, 准备匹配下一个{ 变量 }
    tpl = tpl.substring(match.index + match[0].length);
  }
  addLine(tpl);
  // 拼接 code 中定义的 r 并返回
  code.push('return r.join("\\'");')
  // 使用匿名函数执行 code 传给 fn
  fn = new Function(code.join("\n"));
  // 定义 render 函数
  this.render = function (model) {
    // 处理数据
    return fn.apply(model);
  };
}

// 使用

<script id="tpl" type="text/plain">
    <p>Today: { date }</p>
    <a href="/{ user.id|safe }">{ user.company }</a>
</script>

// 获取模板传入 Template
var tpl = new Template($('#tpl').html());
// 调用 render 函数传入数据
var s = tpl.render({
    date: 20150101,
    user: {
        id: 'A-000&001',
        company: 'AT&T'
    }
});
// 传入数据
$('#other').html(s);

来源整理自:vue3js.cn 面试官系列