首先我们先来写一个简单的模板引擎代码
var str = "My name is {{name}},I'm {{age}} years old";
var regex = /{{([a-zA-Z_$][0-9a-zA-Z\._$]*)}}/g;
//match 匹配的字串
//key 匹配正则括号中的值(如有多个括号就会有多个值)
//offset 匹配字串的偏移量
//string 整个字符串
var data = {
name: 'jimmy',
age: 25,
sex: '男',
friend: {
name: 'tom'
}
};
var tpl = str.replace(regex, function(match, key, offset, string){
return data[key] || match;
});
console.log(tpl);//"My name is jimmy,I'm 25 years old"
优化简单的模板引擎代码
上面的代码有缺陷,如果在对象中有嵌套层的话,那么是无法被上面的正则匹配替换的。
将上述代码{{name}}修改成{{friend.name}}则不替换:
var str = "My {{friend.name}} is jimmy,I'm {{age}} years old";
console.log(tpl);//"My {{friend.name}} is jimmy,I‘m 25 years old"
现在我们来修改下代码:
//match 匹配的字串
//key 匹配正则括号中的值
//offset 匹配字串的偏移量
//string 整个字符串
var a = str.replace(regex, function(match, key, offset, string){
var keys = key.split('.'),
obj = data;
while(keys.length > 0) {
key = keys.shift();
obj = obj[key];
}
return obj || match;
});
console.log(a);//"My name is tom,I'm 25 years old";
我们将{{friend.name}} 中的friend.name 通过 split('.') 截取成数组形成["friend", "name"],并且通过判断长度length,循环调出最里面的值,直到循环到最里层,取到想要的值
相当于:
//第一次循环:
obj = obj[key];//{name:"tom"}
//第二次循环:
obj = obj[key];//tom
那么现在来写成函数
function easyTpl(data, str) {
var regex = /{{([a-zA-Z_$][0-9a-zA-Z\._$]*)}}/g;
var result = str.replace(regex, function(match, key, offset, string) {
var keys = key.split('.'),
obj = data;
while (keys.length > 0) {
key = keys.shift();
obj = obj[key];
}
return obj || match;
});
return result;
}
var tpl = easyTpl(data, str);
console.log(tpl);
准备封装
首先封装的代码不仅要起到隔离作用域的作用,并且要能够在CMD、AMD、nodejs中通用使用,也就是说将这段封装好的代码移到各自环境中,自己匹配适合的代码生成模块。
我们先来定义一个自执行函数
(function(name, definition, context){
//TODO::
})(name, function(){}, this);
自执行函数将要接受 一个(定义的函数名,函数,作用域)
那么我们一步一步添加
(function(name, definition, context){
if(typeof module != 'undefined' && module.exports){
//在 node 环境中
module.exports = definition();
} else if (typeof context['define'] == 'function' && (context['define']['amd']) || typeof context['define'] == 'function' && (context['define']['cmd'])) {
//在 AMD(requirejs) 或者 在 CMD(seajs) 环境中
define(definition);
} else{
//在客户端client中
context[name] = definition();
}
})(name, function(){}, this);
在这段代码中,
第一个if判断语句
通过能力检测判断 module ,module是在node环境中才有的方法,并且module.exports存在,在将函数definition放进mudule.exports 环境中,成为node环境下的代码。
第二个判断语句
通过能力检测判断context['define']['amd']或context['define']['cmd']并且都判断context是一个函数,这样说明在CMD或者AMD环境下,他们的定义磨矿化函数的方式都是difine(),所以通用格式写成 difine(definition)。
最后一个是在不是 nodejs 和 CMD AMD 环境下 ,默认在客户端window环境下,所以context 等于 window 。在window全局作用域下添加变量名name,并放入definition函数
完整代码可以写成
(function(name, definition, context) {
if (typeof module != 'undefined' && module.exports) {
//在 node 环境中
module.exports = definition();
} else if (typeof context['define'] == 'function' && (context['define']['amd']) || typeof context['define'] == 'function' && (context['define']['cmd'])) {
//在 AMD(requirejs) 或者 在 CMD(seajs) 环境中
define(definition);
} else {
//在客户端中 client
context[name] = definition();
}
})('easyTpl', function() {
return function(data, str) {
var regex = /{{([a-zA-Z_$][0-9a-zA-Z\._$]*)}}/g;
return str.replace(regex, function(match, key, offset, string) {
var keys = key.split('.'),
obj = data;
while (keys.length > 0) {
obj = obj[keys.shift()];
}
return obj || match;
});
}
}, this);
最后 命令行操作
通过 npm init 生成packge.json
npm login
npm publish 发布上线
并通过 npm官网查找
下次自己使用通过 npm intall xxx 即可下载安装到 全局或者 当前文件夹的 node_modules中。