前端模板与渲染方式
1 页面级的渲染
再刚有web的时候,前端与后端的交互,非常直白,浏览器端发出URL,后端返回一张拼好了的HTML串。浏览器对其进行渲染。html中可能会混有一些php(或者php中混有一些html)。在服务端将数据与模板进行拼装,生成要返回浏览器端的html串。
这与我们现在做一个普通网页没什么区别。只不过现在,我们更常使用模板技术来解决前后端耦合的问题。
前端使用模板引擎,在html中写一些标签,与数据与逻辑基本无关。后端在渲染的时候,解析这些标签,生成HTML串,如smarty。其实前端与后端的交互在服务端就已经有一次了。
模板:
front.tpl
<div>
{%$a%}
</div>
后端:
// 设置变量
$smarty->assign('a', 'give data');
// 展示模板
$smarty->display("front.tpl");
到前端时是渲染好的html串:
<div>
give data
</div>
这种方式的特点是展示数据快,直接后端拼装好数据与模板,展现到用户面前。
2 异步的请求与新增模板
新的时代,由ajax引领。(Asynchronous Javascript And XML),这种技术的历史,我就不再赘述。ajax的用法也有多种。
ajax接受各种类型的返回。包括XML/JSON/String等。前端发起ajax请求,后端直接将数据返回。
但是,读者们有没有想过,ajax回来的数据是干嘛用的呢?相信大部分人使用ajax拿回的数据是用来展示的。前端得把ajax拿回来的数据与模板进行拼装。这就面临了一个问题,当你的模板非常“华丽”的时候(也就是模板代码比较多的时候)。我们在前端写的拼字符串的逻辑,会非常的复杂。
也有的人图省事,直接就在ajax的返回值中,传输拼装好的html字符串。这样可以直接把ajax拿到的html字符串,填充到页面上。
下面实例说明一下两种方式:
2.1 ajax获取字符串直接渲染方式
如图2.1.1所示:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>下面是待填充区域:</h1>
<div class="blankPlace"></div>
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
document.querySelector('.blankPlace').innerHTML = xhr.responseText;
}
};
xhr.open('GET', './a.html');
xhr.send(null);
</script>
</body>
</html>
========================================================================
a.html
<h2>我是模板</h2>
<div>这是请求回来的数据</div>
图2.1.1
2.2 ajax获取数据,前端进行拼装的方式
效果如图2.2.1所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>下面是待填充区域:</h1>
<div class="blankPlace"></div>
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
var res = JSON.parse(xhr.responseText);
document.querySelector('.blankPlace').innerHTML = ''
+'<h2>'
+ '我是模板'
+'</h2>'
+'<div>'
+ res.data
+'</div>';
}
};
xhr.open('GET', './b.json');
xhr.send(null);
</script>
</body>
</html>
================================================
b.json
{"data": "这是请求回来的数据"}
图 2.2.1
2.3 两种方式的权衡
那么,如何权衡两种方式呢?
笔者单从自己的思维考虑,得出以下结论。如果这种模板的拼装会发生多次。是一个非常频繁的行为,且模板基本一致,只是数据变动的话,最好是一开始采用客户端拼装的方法。因为,同样的模板没有必要被传到客户端好几次。这样,我们可以剩下传输同样模板的流量,请求更快。
类似于新闻流这种网站比较适合这种方式,如今日头条,如图2.3.1所示:
图2.3.1
图2.3.2
笔者在DOM上面打了断点后,找到了其拼装模板,确是在客户端所做。
不过,这种做法也有问题,就是用户同步刷新的时候,需要等页面渲染完,再发一个请求,去请求第一屏的数据,才能开始渲染。这个过程相当于发了两次请求,等待的时候还是有所感知的,如图2.3.3所示。
图2.3.3
所以这种方式也是有些不尽人意的地方的。经过查看,网易新闻的web版,今日头条的web版,天天快报的web版均是采用这种方式。
第二种方式,同步的时候,就将一段渲染好的HTML,直接输出到页面,而在异步的时候,请求的也是这段HTML,直接将请求回的HTML往页面上一塞就完成了。这样就可以达到同步页面的时候,直接输出,用户就不会看到等待中的小菊花了。
百度首页就采取了这种方式。新闻直出,无需等待如图2.3.4:
图2.3.4
但是每次请求新闻的时候,也会去请求HTML片段,如图2.3.5所示:
图2.3.5
这种方式虽然首屏较快,但是,每次传输同样的新闻模板也是需要浪费不少模板流量的。
2.4 混合方式
看过了上述两种方式,聪明的你肯定会想:如果前端的js里写一份模板,后端的html(jsp/asp/smarty)中也写一份模板呢?这样,同步的时候,直接用后端HTML(jsp/asp/smarty)中的模板。异步拉取数据的时候,每次使用js中的模板进行拼装 。同步也能保证首屏的速度,异步也能保证传输量的限制与速度。可是这样,也会面临问题,那就是,你的模板需要维护两份。如果那天产品和你说,我要改一下页面的结构。你不得不改动HTML的时候。js中与jsp/asp/smarty中的模板都需要同样的更改两次。
2.5 前端的模板引擎
如果说,后端可以将html的拼装转变为使用引擎的话,前端为什么不可以呢?这里我先给大家写一个非常简单的模板解析函数,效果如图2.5.1:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="content1"></div>
<div id="content2"></div>
<script>
// 这是我们的模板,怎么样,比直接写html字符串拼装看起来清爽多了吧?
var template = ''
+'<div>'
+ '{%=a%}'
+ '{%if (a===1){%}'
+ '<span>'
+ 'a是1'
+ '</span>'
+ '{%}%}'
+'</div>';
// 能解析输出与if条件语句的函数
function TEMPLATEparser(template, variables) {
// 语法替换
var funcStr = template
.replace(/\{\%\=(\w+)\%\}/, function (code, variable) {
return '"; str += "' + variable + '"; str += "';
})
.replace(/\{\%(if.*?)\%\}(.*?)\{\%(\})\%\}/, function (code, judge, content, end) {
return '";' + judge + 'str+="' + content + '";' + end + 'str += "';
});
// 返回拼装函数
return new