最近在mustache源码,先看的0.1版本。
cdn上的源码: https://cdn.bootcdn.net/ajax/libs/mustache.js/0.1/mustache.js
下面是我去掉部分无用字段后的注释,不过里面render_partial
函数参数eval之后是对象的形式,没搞懂(-.-)。有知道的大神还望指导一下;
/*
Shamless port of http://github.com/defunkt/mustache
by Jan Lehnardt <jan@apache.org>
Thanks @defunkt for the awesome code
TBD: MIT, see LICENSE
ChangeLog:
- 04.10.2009: Ininitial port at http://devhouseberlin.de/
*/
var Mustache = {
name: "mustache.js",
version: "0.1",
context: {}, // 处理的数据
// 其实就是render函数,,中间传递而已
to_html: function (template, view) {
return this.render(template, view);
},
render: function (template, view) {
// 没有双大阔号 就不用模板直接返回
if (template.indexOf("{{") == -1) {
return template;
}
// 把数据 合并,下面可能或获取第二个模板,第二个模板也有数据,数据需要合并
this.context = context = this.merge((this.context || {}), view);
// 递归 处理循环 渲染 (可以先看简单渲染,之后再来看 #循环的渲染)
var html = this.render_section(template);
// 递归可能把事情搞砸了
this.context = context;
// 简单的渲染
return this.render_tags(html);
},
render_partial: function (name) {
// 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
var evil_name = eval(name)
switch (typeof evil_name) {
case "string":
// 如果是字符串 那window下的这个evil_name 作为模板,再次渲染
return this.to_html(evil_name, "");
case "object":
// 如果是对象,那添加_template 作为模板继续渲染,既然typeof是对象, 那就只能是[] , {}, null; 但是这样不符合变量命名规范,,,不懂。。。。
var tpl = name + "_template";
return this.to_html(eval(tpl), evil_name);
default:
throw ("Unknown partial type.");
}
},
merge: function (a, b) {
// 遍历 如果是自身的属性,那添加给a
for (var name in b) {
if (b.hasOwnProperty(name)) {
a[name] = b[name];
}
}
return a;
},
render_section: function (template) {
// 如果没有{{# 说明不是遍历循环直接返回
if (template.indexOf("{{#") == -1) {
return template;
}
var that = this;
// {{# (.+) }} \s* ([\s\S]+) {{/ \1}} \s* }}
// 开始 匹配循环关键字 结束 可能有空格 全匹配 以\开始的匹配关键字 空格
return template.replace(/\{\{\#(.+)\}\}\s*([\s\S]+)\{\{\/\1\}\}\s*/mg,
// name 循环关键字 content 全匹配的内容
function (match, name, content) {
var value = that.find(name);
// 如果数据中 查找到数据是 数组
if (that.is_array(value)) {
// 遍历数组 数组每一项都渲染,以全匹配 为模板,,数组每一项为数据, 调研render函数。最后join合并
// 比如 value = [{age:'100', name:'child1'},{'age':'200', name:"child2"}]
// content = <span>姓名:{{name}}<i>年龄:{{age}}</i></span>
// that.render(content, value) 会得到 <span>姓名:child1<i>年龄:100</i></span> <span>姓名:child2<i>年龄:200</i></span>
return value.map(function (row) {
return that.render(content, row);
}).join('');
// // 如果数据中 查找到数据是 不是数组 但是存在 再调用render函数渲染 ,content 为模板
} else if (value) {
return that.render(content);
} else {
// 没有 直接返回空字符串
return "";
}
}
);
},
// 判断是数组
is_array: function (a) {
return (a &&
typeof a === 'object' &&
a.constructor === Array);
},
// 简单渲染
render_tags: function (template) {
var that = this;
// {{ (! | < | {)? ([^ / #]+?) \1 ?}}+
// !或< 或 { 非/#的数据 !或< 或 {
return template.replace(/\{\{(!|<|\{)?([^\/#]+?)\1?\}\}+/mg,
// match 匹配正则 operator 第一个括号匹配项 name第二个括号匹配项
function (match, operator, name) {
switch (operator) {
case "!": // ignore comments 注释
return match;
case "<": // render partial 渲染部分
return that.render_partial(name);
case '{': // the triple mustache is unescaped // {{{ 三个大括号是不会转义的
return that.find(name);
default: // escape the value 会转义
return that.escape(that.find(name));
}
}, this);
},
// 转义 以防有脚本
escape: function (s) {
return s.toString().replace(/[&"<>\\]/g, function (s) {
switch (s) {
case "&":
return "&";
case "\\":
return "\\\\";;
case '"':
return '\"';;
case "<":
return "<";
case ">":
return ">";
default:
return s;
}
});
},
// 找到数据中 对应的值,如果是函数执行,不存在报错,否则返回
find: function (name) {
name = this.trim(name);
var context = this.context;
if (typeof context[name] === "function") {
return context[name].apply(context);
}
if (context[name] !== undefined) {
return context[name];
}
throw ("Can't find " + name + " in " + context);
},
// 去掉空格
trim: function (s) {
return s.replace(/^\s*|\s*$/g, '');
},
};
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./mastache.js"></script>
<script>
var template = `
<div>{{name}}</div>
<div>{{age}}</div>
<div>{{!我是注释}}</div>
<div>{{{f1}}}</div>
<div>{{{f2}}}</div>
<div>{{f3}}</div>
<div>{{<template2}}</div>
{{#list}}
<span>姓名:{{name}}<i>年龄:{{age}}</i></span>
{{/list}}
`;
var template2 = "<sapn>{{sex}}</sapn"
var data = {
name: "张三",
age: 14,
f1: "abc&<",
f2: function () {
return 666;
},
f3: "abc&<",
sex: '男',
list: [{age:'100', name:'child1'},{'age':'200', name:"child2"}]
};
const str = Mustache.render(template, data);
console.log("str", str);
</script>
</body>
</html>
控制台打印结果