前端模板引擎

什么是前端模板引擎? 

  通俗地说,前端模板引擎就是你须要根据不一样数据重复生成结构相同的html的时候,模板能够大大节省你的代码量,以及提升可维护性html

 

为何要用前端模板引擎?

  • 节省代码量。
  • 减小拼接HTML的麻烦。
  • 可维护性好,后期修改起来比较方便。
  • 开发效率高(程序逻辑组织更好,调试方便)。 
  • 看起来舒服(不容易写错)。 
  • 有利于先后端分离。 

 

实现前端模板引擎。

  在编写前端模板引擎代码以前,咱们应该想好如何来调用它,即这个模板引擎的接口应该是什么样的。咱们但愿这样调用它:前端

// 建立一个模板引擎:
var tpl = new Template('<p>Today: { date }</p>\n<a href="/{ user.id|safe }">{ user.company }</a>');
// 渲染获得HTML片断:
var model = {
    date: 20150316,
    user: {
        id: 'A-000&001',
        company: 'AT&T'
    }
};
var html = tpl.render(model);
console.log(html);
// <p>Today: 20150316</p>
// <a href="/A-000&001">AT&amp;T</a>

  这个思路仍是很明确的,一个模板引擎就是把一个字符串中的变量使用 medel 的变量替换掉,就完成了。 规则也不难,就是对于通常的标签正常表示,对于变量,咱们使用{}来包裹。 git

  咱们选用{ model.prop }来实现咱们本身的变量替换,基本思想是用一个正则表达式来匹配{ xxx.xxx }github

 

var re = /\{\s*([a-zA-Z\.\_0-9()]+)\s*\}/m
var match = re.exec('a { template } string');

 

编写一个简单的JavaScript模板引擎

廖雪峰 / 编程 / 2015-3-16 21:33 / 阅读: 607正则表达式

随着Nodejs的流行,JavaScript在前端和后端都开始流行起来。有许多成熟的JavaScript模板引擎,例如Swig,既能够用在后端,又能够用在前端。编程

不过不少时候,前端模板仅仅须要简单地建立一个HTML片断,用Swig这种全功能模板有点大材小用。咱们来尝试本身编写一个简单的前端模板引擎,实际上并不复杂。后端

在编写前端模板引擎代码以前,咱们应该想好如何来调用它,即这个模板引擎的接口应该是什么样的。咱们但愿这样调用它:数组

// 建立一个模板引擎:
var tpl = new Template('<p>Today: { date }</p>\n<a href="/{ user.id|safe }">{ user.company }</a>'); // 渲染获得HTML片断: var model = { date: 20150316, user: { id: 'A-000&001', company: 'AT&T' } }; var html = tpl.render(model); console.log(html); // <p>Today: 20150316</p> // <a href="/A-000&001">AT&amp;T</a> 

所以,一个模板引擎就是把一个字符串中的变量用model的变量替换掉,就完成了。app

像Swig这种类Jinja2的模板引擎,它能够替换{{ model.prop }}这样的变量。前后端分离

咱们选用{ model.prop }来实现咱们本身的变量替换,基本思想是用一个正则表达式来匹配{ xxx.xxx }

var re = /\{\s*([a-zA-Z\.\_0-9()]+)\s*\}/m
var match = re.exec('a { template } string');

(其中 \s 为空格, () 用来捕获(使用execf方法),()中间的就是插入的变量)

若是正则匹配成功,则match不为空,match[0]是匹配到的字符串{ template }match[1]是捕获的变量templatematch.index是匹配的索引。

  经过这种方式,咱们就能够不断的匹配到变量,而后用model的内容来替换了,最后获得HTML,这就是模板引擎的原理。 

  可是,想要分析 user.addr.zipcode 而后去model中查找并不容易,并且模板应该能够预编译,这样,后续的渲染速度就会很快。

  JavaScript容许用new Function('source')来经过字符串建立一个函数,这个函数和咱们用function ()定义的函数是如出一辙的,所以,一个模板引擎的编译过程就是建立一个函数,而后调用该函数就实现了模板渲染。

  须要编译的函数代码应该像这样:

function () {
    var r = [];
    r.push('<p>Today: ');
    r.push(this.date);
    r.push('</p>\n<a href="/');
    r.push(this.user.id);
    r.push('">');
    r.push(this.user.company);
    r.push('</a>');
    return r.join('');
}

  注意到变量名从variable.prop变成了this.variable.prop,是由于调用该函数时咱们会把model绑定到this变量上. 

  所以,模板引擎的代码以下:

function Template(tpl) {
    var
        fn,
        match,
        code = ['var r=[];'],
        re = /\{\s*([a-zA-Z\.\_0-9()]+)\s*\}/m,
        addLine = function (text) {
            code.push('r.push(\'' + text.replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '\');');
        };
    while (match = re.exec(tpl)) {
        if (match.index > 0) {
            addLine(tpl.slice(0, match.index));
        }
        code.push('r.push(this.' + match[1] + ');');
        tpl = tpl.substring(match.index + match[0].length);
    }
    addLine(tpl);
    code.push('return r.join(\'\');');
    // 建立函数:
    fn = new Function(code.join('\n'));
    // 用render()调用函数并绑定this参数:
    this.render = function (model) {
        return fn.apply(model);
    };
}

如今,这个简单的模板引擎已经能够工做了。可是它还有几个小问题须要解决,一是默认的变量在替换时应该作HTML转义,二是若是某些不须要转义的变量,能够用{ user.id|safe }这样的表达式表示user.id无需转义。

 

通过HTML转义和{ variable|safe }处理的最终代码以下

function Template(tpl) {
    var
        fn,
        match,
        code = ['var r=[];\nvar _html = function (str) { return str.replace(/&/g, \'&amp;\').replace(/"/g, \'&quot;\').replace(/\'/g, \'&#39;\').replace(/</g, \'&lt;\').replace(/>/g, \'&gt;\'); };'],
        re = /\{\s*([a-zA-Z\.\_0-9()]+)(\s*\|\s*safe)?\s*\}/m,
        addLine = function (text) {
            code.push('r.push(\'' + text.replace(/\'/g, '\\\'').replace(/\n/g, '\\n').replace(/\r/g, '\\r') + '\');');
        };
    while (match = re.exec(tpl)) {
        if (match.index > 0) {
            addLine(tpl.slice(0, match.index));
        }
        if (match[2]) {
            code.push('r.push(String(this.' + match[1] + '));');
        }
        else {
            code.push('r.push(_html(String(this.' + match[1] + ')));');
        }
        tpl = tpl.substring(match.index + match[0].length);
    }
    addLine(tpl);
    code.push('return r.join(\'\');');
    fn = new Function(code.join('\n'));
    this.render = function (model) {
        return fn.apply(model);
    };
}

 

如今就能够用咱们预设的代码来使用这个模板引擎了。不过,把模板写在字符串中也不是一个好办法。最佳解决方案是利用<script>标签,把模板写在里面,注意必定要加上type="text/plain"

 

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

而后,用jQuery来得到模板内容并渲染:

var tpl = new Template($('#tpl').html());
var s = tpl.render({
    date: 20150101,
    user: {
        id: 'A-000&001',
        company: 'AT&T'
    }
});
$('#other').html(s);

这样,咱们就用不到30行代码实现了一个简单的JavaScript模板引擎。

 

 

最后,咱们再总结一下思路: 首先,使用模板引擎的格式来写这么一个模板,而后通过一个函数处理,过程就是经过while循环,而后捕获到对应的变量的规则,将变量前的内容push到数组中,而后再使用数据进行替换,而后push到数组中,接着经过字符串方法把前面的字符串截取,留下后面的字符串继续匹配,最后,咱们就能够经过join()方法来实现html的生成了,经过appendChild()就能够顺利插入数组了。

相关文章
相关标签/搜索