HTML字符串模板是在写页面模板时会用到的一个渲染引擎,本质是字符串替换和代码执行。格式是下面这样子的:
输出值:
<html><body> <%= name %> </body></html>
执行代码
<html><body><% for(var i = 0; i < 2; i++) {%><div><%= i %></div><% } %></body></html>
就是使用<% 和<%=分别表示执行代码和输出变量的值,用%>表示自定义的结束。语法很简单,不考虑复杂情况下,可以自己动手实现一个。
1. 替换思路
把整个HTML模板读取成字符串后,将HTML部分当作是字符串,将<% %>和<%= %>中间部分取出进行执行。由于<% %>只是执行代码,不直接输出HTML模板,不用处理返回结果。而<%= %>需要把表示式的返回值保存起来,加到输出结果中。
要根据输入对象,直接读取值,可以用with方法。虽然该方法已经不被推荐,但在这种情况下应用还是很方便。将模板解析后,使用new Function生成函数,代入输入对象,即可实现替换。
2. 实现
module.exports = function regTemplate (str, obj) { let pre = 'with(obj){var _str = "";_str +=\'' let evalStr = str .replace(/[\r\n\t]/g, ' ') .replace(/<%=([\s\S]*?)%>/g, "';_str +=$1;_str +='") .replace(/<%/g, "';") .replace(/%>/g, "_str+='") evalStr = pre + evalStr + "';return _str;}" var func = new Function ('obj', evalStr) return func(obj) }
代码如上。核心是在with时建立一个空白字符串,用str+=‘ 将第一个html片断加入到结果字符串中,然后遇到<%= %>时, html字符串结束,添加内部表达式的值,再开始加一下个字符串。遇到<%时,html字符串结束,里面的内容不用处理,直接遇到%>表示应该开始下一字符串,再进行相加HTML字符串。最后添加结尾单引号。
使用new Function生成新函数,代入输入的对象,返回执行结果。
当然,光这样还是不够的,由于HTML中代码中虽然只推荐使用双引号,但还是有可能出现单引号,这时模板就会失效。改进一下:
module.exports = function regTemplate (str, obj) { let pre = 'with(obj){var _str = "";_str +=\'' // replace last single quote let arr = str.split('%>') arr[arr.length - 1] = arr[arr.length - 1].replace(/'/g, '\\\'') str = arr.join('%>') let evalStr = str // replace first single quote .replace(/([\s\S]*?)<%/m, function (str, $1) { return $1.replace(/'/g, '\\\'') + '<%' }) // replace middle single quote .replace(/%>([\s\S]*?)<%/g, function (str, $1) { return '%>' + $1.replace(/'/g, '\\\'') + '<%' }) .replace(/[\r\n\t]/g, ' ') .replace(/<%=([\s\S]*?)%>/g, "';_str +=$1;_str +='") .replace(/<%/g, "';") .replace(/%>/g, "_str+='") evalStr = pre + evalStr + "';return _str;}" var func = new Function ('obj', evalStr) return func(obj) }
使用正则替换掉中间和第一段中的单引号。但最后一段的正则我不会写,尝试了好久也没成功,没办法只能用字符串处理了,导致程序代码一下子就多了起来,看起来很不舒服。先这样了。
3. 总结
写起来很费劲,想了好久也没想出方案,最后还是按https://www.cnblogs.com/dolphinX/p/3489269.html上面实现的。主要问题还是思路没有理清,尤其是关于<% %>这种不处理成字符串的,浪费了很多时间。再者正则使用是一知半解,一遇到复杂的就很难写。
附上练习源码地址:https://github.com/wenlonghuo/code-test/tree/master/002_htmlTemplate