前端模板与渲染方式

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 Function(variables, ‘var str = ""; str += "‘ + funcStr + ‘";return str;‘);
            }

            // 实验使用模板引擎去解析并传入变量生成模板
            var outHTML = TEMPLATEparser(template, [‘a‘])(1);
            document.getElementById(‘content1‘).innerHTML = outHTML;
            outHTML = TEMPLATEparser(template, [‘a‘])(2);
            document.getElementById(‘content2‘).innerHTML = outHTML;
        </script>
    </body>
</html>

图2.5.1

这样就制作了一个简单的前端模板,有兴趣的读着可以看看我写的smartyMonkey前端模板引擎:

https://github.com/houyu01/smartyMonkey

2.6 前后端同构

刚刚说过了前端模板,后端模板,前端与后端都需要模板引擎。比如,我们的在后端的模板是这样写的:

// 接下来是伪代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        // 前端需要模板去渲染
        <textarea id="temp">include(‘./template.html‘)</textarea>
        <div id="content1">
            // 后端渲染模板
            include(‘./template.html‘);
        </div>
        <div id="content2"></div>
        <script>
            // 这是我们的模板,怎么样,比直接写html字符串拼装看起来清爽多了吧?
            var template = document.getElementById(‘temp‘).value;
            // 能解析输出与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 Function(variables, ‘var str = ""; str += "‘ + funcStr + ‘";return str;‘);
            }

            // 实验使用模板引擎去解析并传入变量生成模板
            var outHTML = TEMPLATEparser(template, [‘a‘])(1);
            document.getElementById(‘content1‘).innerHTML = outHTML;
            outHTML = TEMPLATEparser(template, [‘a‘])(2);
            document.getElementById(‘content2‘).innerHTML = outHTML;
        </script>
    </body>
</html>

============================
template.html

<div>
  {%=a%}
  {%if (a===1){%}
      <span>
          a是1
      </span>
  {%}%}
</div>

前端解析模板的引擎的语法,与后端j解析模板引擎语法一致。这样就达到了一份HTML前后端一起使用的效果。一改俱改,一板两用。其实这样也不算极致的完美,因为聪明的读者会发现,在页面加载的时候,我们多传了一份模板给到前端,如果用户不触发重新渲染的话,可能我们传到前端的模板就算白传了,造成了浪费。聪明的读者们可以考虑一下,如何把这份也给省下去。

3 模板的更新



有的时候,我们需要整片DOM进行更新,比如:

<div class="我需要被更新" data-att="我需要被更新">
    <span>我需要被更新</span>
    <div class="我需要被更新"></div>
</div>

这些html中的节点,需要在某次行为之后,一起被更新。那么我们的js可能会变成这样:

<script>
    // 数据更新
    $.ajax().done(function (data) {
        $(‘#wrapper‘).class(data.xxx);
        $(‘#wrapper‘).attr(‘data-attr‘, data.xxx);
        $(‘#wrapper span‘).html(data.xxx);
        $(‘#wrapper div‘).class(data.xxx);
    });
</script>

这样的维护,成本极大,还不如直接把整个html重新刷新一遍。这就遇到了我们的js拼装模板了:

<script>
    // 模板
    var template = ‘‘
    +‘<div class="{%=newclass%}" data-attr="{%=newattr%}">‘
    +    ‘<span>{%=newcontent%}</span>‘
    +    ‘<div class={%=newinnerclass%}></div>‘
    +‘</div>‘;

    // 数据更新
    $.ajax().done(function (data) {
        // 每次数据更新,直接把模板全刷一遍
        $(‘#wrapper‘)[0].outerHTMl = TEMPLATEparser(template)(data);
    });
</script>

但是,直接刷HTML的成本太高。这样浏览器不得不整颗html子树全部重新构建一下,这种方法的性能又不如上一种方法好。

好在react给了我们一种新的思路,它用最少的开销帮我们处理模板的更新,却又不用我们维护更新时繁琐的步骤。有兴趣的读者可以了解一下react-web的diff算法及其应用。

时间: 2024-10-09 09:56:56

前端模板与渲染方式的相关文章

聊一聊前端模板与渲染那些事儿

欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码): https://segmentfault.com/blog/frontenddriver 作为现代应用,ajax的大量使用,使得前端工程师们日常的开发少不了拼装模板,渲染模板.我们今天就来聊聊,拼装与渲染模板的那些事儿. 如果喜欢本文请点击右侧的推荐哦,你的推荐会变为我继续更文的动力 1 页面级的渲染 在刚有web的时候,前端与后端的交互,非常直白,浏览器端发出URL,后端返回一张拼好了的HTML串

在 FIS 中,集成了百度前端模板

有好久没有写博客了,大概是因为工作太忙,加上要不断地学习,所以忘记了... 进入正题... fis内置了百度前端模板baiduTempate,在编译过程中,会预编译生成对应文件,不需要线上编译,提高页面运行效率.所谓的预编译,开始我也是比较模糊,所以问了一些前端同事再加上自己工作体会便明白了,这里我写下个人的理解,若有不妥之处,还请大家多多指点. 使用fis构建工具时,在 JS 代码中,通过 __inline 方式进行编译处理前端模板.同时规定以 tmpl 为后缀的文件为前端模板,使用方式: /

前端模板引擎入门

模板引擎 模板引擎 起到 数据和视图分离的作用, 模板对应视图, 关注如何展示数据, 在模板外头准备的数据, 关注那些数据可以被展示. 后端模板引擎 freemarker 如下介绍,  java后台的模板引擎, freemark介绍,其图能很好标明这种关系. http://freemarker.org/ Apache FreeMarker is a template engine: a Java library to generate text output (HTML web pages, e

模版+数据分离渲染方式的设计与实现

一 背景 1 现状 模版存放于后端 php输出页面html结构进行页面渲染 ajax请求,需要重渲结构时,php输出html结构 builder制作静态页面结构 jser完成页面交互逻辑开发 2 不足 模版数据无法存储本地,导致每次打开页面请求数据量巨大 数据每次要从接入层web服务器读取,没有合理利用CDN加速静态模版内容 联调成本较大,不利于前端控制页面展示和交互开发 3 解决方案 后端直接输出json数据 试图把渲染页面的模版存放在前端 4 技术路线 5 理论意义 利用CDN保存html模

【超精简JS模版库/前端模板库】原理简析 和 XSS防范

使用jsp.php.asp或者后来的struts等等的朋友,不一定知道什么是模版,但一定很清楚这样的开发方式: <div class="m-carousel"> <div class="m-carousel-wrap" id="bannerContainer"> </div> </div> <ul class="catelist onepx" onepxset="

前端模板技术面面观(1)

此文已由作者郑海波授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验 此文的写作耗时很长,称之为雄文不为过,小心慢用 此文缘由 其实从发布regularjs之后,我发现在google搜索regularjs 不是给我这个画面 就是给我这个画面 突然发现取名字真是个大学问,当时就基本预计到了会有不明真相的朋友认为它只是一个照搬angularjs的家伙,对于这点,有兴趣的朋友可以看下[为什么要造Regularjs这个轮子]. 而在这个文章,我不会直截了当去与angular做直接的对

Django之入门 CMDB系统 (二) 前端模板

Django之入门 CMDB系统 (二) 前端模板 前言 作者: 何全,github地址: https://github.com/××× QQ交流群: ××× 通过此教程完成从零入门,能够独立编写一个简单的CMDB系统. 目前主流的方法开发方式,分为2种:mvc 和 mvvc方式.本教程为 mvc 方式,即 django负责渲染html.后面会推出 mvvc(前后端分离)的入门教程. 教程项目地址: https://github.com/×××/husky/ 教程文档地址: https://gi

Handlebars模板引擎渲染页面

基本使用,并简单列举了几种常见的数据格式的渲染方式 js: var testTpl = Handlebars.compile($('#test').html()); //模板 var arr = [1,2,3] //数据 $('#box').append(testTpl(arr)); //调用 html: <script type="text/x-handlebars-template" id="test"> {{#each this}} <li&

Yii框架学习笔记(二)将html前端模板整合到框架中

选择Yii 2.0版本框架的7个理由 http://blog.chedushi.com/archives/8988 刚接触Yii谈一下对Yii框架的看法和感受 http://bbs.csdn.net/topics/390807796 更多内容 百度:yii 前端 http://my.oschina.net/u/1472492/blog/221085 摘要 Yii框架学习笔记(二)将html前端模板整合到框架中 原文地址:http://www.ldsun.com/1309.html 上一节成功将Y