自定义一个简单的前端模板引擎
Posted vcxiaohan2
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义一个简单的前端模板引擎相关的知识,希望对你有一定的参考价值。
前言
- 一次在前端技术沙龙会议上,演讲者分享主题《Node.js性能调优实践》时,提到了在node配合artTemplate渲染页面时,并发高且返回数据量大的情景下,会遇到渲染的性能瓶颈,当时演讲者有提到几种解决方法:说artTemplate在渲染模板时会使用到拷贝继承,这个效率明显低于原型继承,修改源码即可
今天去看了下最新的artTemplate源码,发现继承方法已经改为原型继承了
- 台下有人发言说也可以使用一些诸如webpack的loader加载器,将渲染的模板进行加载并预编译为实际的方法缓存起来,这样渲染的时候直接调用方法,能极大的提高效率
- 由于当时对模板引擎的原理不甚了解,听得有点蒙,事后得空来稍稍研究下
知识点普及
html嵌入模板的几种方法
- 不同方法的优劣势请参考HTML5 标签元素简介
本文选用的是方法1
<!-- 方法1 --> <script type="text/template"> <img src="xxx.png"> </script> <!-- 方法2 --> <textarea style="display: none;"> <img src="xxx.png"> </textarea> <!-- 方法3 --> <xmp style="display: none;"> <img src="xxx.png"> </xmp> <!-- 方法4 --> <template> <img src="xxx.png"> </template>
正则表达式负向前瞻
- 后文需要匹配字符串和=,这时候需要用到负向前瞻
eval和new Function的区别
- 把字符串解析为可执行的方法,据说2者运行时性能有差异,暂不讨论
浅析js模板引擎
- 不同模板间的格式以及效率对比
思路
- 在html中嵌入混有着各种原生js的模板
- 使用js读取这段模板为字符串
- 将这段字符串中特殊标记处(指、=、)进行相应的替换,最终转换为一段可执行的代码
- 这段代码执行后将返回拼接的dom结构,之后将dom结构添加到页面
实现
待渲染的数据
let data = name: 'hvb', age: 22, hobbies: ['reading', 'coding'], friends: [ name: 'hwj', age: 12 , name: 'hwb', age: 24 ]
待解析的模板(规定it为数据的来源,模板里面混入了es6语法)
<script id="template" type="text/template"> <p>姓名:= it.name </p> <p>年龄:= it.age </p> <p>爱好:= it.hobbies[0] 和= it.hobbies[1] </p> for(let name, age of it.friends) <p>姓名:= name </p> <p>年龄:= age </p> </script>
解析函数(经过大量的拼接试错,得出能使被替换后的字符串正确运行的匹配替换规则,可自行研究)
// 解析函数(注意模板中的数据来源均为it) function render(id, it) // 获取模板字符串 let html = $(`#$id`).html() // 把换行符替换为空 把替换为xx 把=替换为xx 把替换为xx html = html.replace(/\\n/g, '').replace(/(?!=)/g, '\\';').replace(/=/g, '\\'+').replace(//g, ';str+=\\'') // 进行简单的拼接,通过eval执行 return eval(`let str='';str+='$html'`)
高性能的解析函数(由于以上使用eval进行粗暴的解析,没有任何的优化,故运行效率低下,此处进行改进)
// 使用单例模式来缓存解析后的方法(对比以上粗暴的eval解析,性能提升3倍左右) let render = (function () let fn = null return function (id, it) if (!fn) let html = $(`#$id`).html() html = html.replace(/\\n/g, '').replace(/(?!=)/g, '\\';').replace(/=/g, '\\'+').replace(//g, ';str+=\\'') fn = new Function('it', `let str='';str+='$html';return str;`) return fn(it) )()
渲染结果
// 调用方法 $('#a').append(render('template', data))
思考
- 模板引擎的思路大同小异,特别是在中途发现自己的思路跟doT.js很相近
- 至于模板引擎的其他功能,如自定义模板标签、过滤xss、include等等暂不考虑
- 模板引擎最核心是渲染性能,可以参考高性能JavaScript模板引擎原理解析
源码
以下还小小的对比了下自定义模板和doT.js的渲染性能,当然这对doT.js并不公平,因为我只实现了很小的一个功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>demo</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script src="https://cdn.bootcss.com/dot/2.0.0-beta.0/doT.min.js"></script>
<style>
*
margin: 0;
padding: 0;
body,
html
width: 100%;
height: 100%;
div
border: 2px solid red;
</style>
</head>
<body>
<h2>模板解析如下:</h2>
<div id="a"></div>
<h2>模板解析如下:(使用不同数据)</h2>
<div id="b"></div>
<script id="template" type="text/template">
<p>姓名:= it.name </p>
<p>年龄:= it.age </p>
<p>爱好:= it.hobbies[0] 和= it.hobbies[1] </p>
for(let name, age of it.friends)
<p>姓名:= name </p>
<p>年龄:= age </p>
</script>
</body>
<script>
$(() =>
// 一组数据
let data1 =
name: 'hvb',
age: 22,
hobbies: ['reading', 'coding'],
friends: [
name: 'hwj', age: 12 ,
name: 'hwb', age: 24
]
// 一组不同的数据
let data2 =
name: 'hvb111',
age: 22111,
hobbies: ['reading111', 'coding111'],
friends: [
name: 'hwj111', age: 12111 ,
name: 'hwb111', age: 24111
]
// 使用单例模式来缓存解析后的方法(对比以上粗暴的eval解析,性能提升3倍左右)
let render = (function ()
let fn = null
return function (id, it)
if (!fn)
let html = $(`#$id`).html()
html = html.replace(/\\n/g, '').replace(/(?!=)/g, '\\';').replace(/=/g, '\\'+').replace(//g, ';str+=\\'')
fn = new Function('it', `let str='';str+='$html';return str;`)
return fn(it)
)()
// 使用模板渲染一组数据
$('#a').append(render('template', data1))
// 使用模板渲染一组不同的数据
$('#b').append(render('template', data2))
// 自定义模板性能测试
console.time()
for (let i = 0; i < 1000; i++)
$('body').append(render('template', data1))
console.timeEnd()
// doT模板性能测试
var evalText = doT.template($('#template').text())
console.time()
for (let i = 0; i < 1000; i++)
$('body').append(evalText(data1))
console.timeEnd()
)
</script>
</html>
以上是关于自定义一个简单的前端模板引擎的主要内容,如果未能解决你的问题,请参考以下文章