前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >技术干货丨Web前端字符串模板浅析

技术干货丨Web前端字符串模板浅析

作者头像
腾讯专有云
发布2022-06-24 18:02:44
6810
发布2022-06-24 18:02:44
举报
文章被收录于专栏:腾讯专有云

前端框架日新月异,而其中的数据绑定已经成为了一个框架最基础的功能。我们常常使用的单向绑定、双向绑定、事件绑定、样式绑定等,里面具体怎么实现,而当我们数据变动的时候又会触发怎样的底部流程呢?

前言

从事前端的朋友可能都知道,Web 前端领域里用到的模板引擎技术主要有三种,它们分别是:

  • 基于字符串的模板
  • 基于Dom操作的模板
  • 基于虚拟Dom的模板

而今天我们会着重介绍基于字符串的模板引擎的实现原理,分析它的优点缺点以及使用的场景。

石器时代

在进入正文之前,我们先回顾一下在模板引擎出现之前的时代,我们暂且称之为“石器时代”,我们是如何利用JS改变页面结构的。比如说下面的代码:

代码语言:javascript
复制
<div id="container">
    我们正处于刀耕火种的石器时代
</div>

如果我们需要修改 container 里面的内容,一般有2种方法:

一种是通过 JS 的 DOM API 直接操作 DOM

代码语言:javascript
复制
  var newTxt = '石器时代需要自己撸工具,摩擦摩擦,似魔鬼的步伐...';
  var container = document.getElementById('container');
  var desc = document.createElement('H1');
  var txt = document.createTextNode(newTxt);
  desc.appendChild(txt);
  container.replaceChild(desc, container.childNodes[0]);

另一种是通过 innerHTML 批量修改 DOM 结构

代码语言:javascript
复制
  var newTxt = '石器时代需要自己撸工具,摩擦摩擦,似魔鬼的步伐...';
  var template = '<H1>' + newTxt + '</H1>';
  var container = document.getElementById('container');
  container.innerHTML=template;

相比之下,第二种方式通过 innerHTML 更新 DOM 要简单许多,它无需考虑 DOM 的层级结构,只要做简单的字符串拼接就能实现需求。但这种方式的问题是代码可读性很差,开发中还必须同时保证最终拼接的字符串的正确性。不然,当你需要作出修改时,面对一坨的字符无疑是一件非常痛苦的事。

青铜时代

在上面的例子中,我们的需求是将一个变量注入到模板当中,类似 ES6 的模板字符串:

代码语言:javascript
复制
var newTxt = '石器时代需要自己撸工具,摩擦摩擦,似魔鬼的步伐...';
var template = `<H1>${newTxt}</H1>`;

但 ES6 这种现代化的常规武器,对石器时代而言已经是天方夜谭了。当然了,部落里的老司机可以凭借深厚的 JS 功底,撸出了各种基于字符串的模板。这些模板又可以细分为两种情况:一种是不包含逻辑处理,只作数据绑定用的,如 mustache.js ;另一种是既有逻辑处理,也有数据绑定的,如 EJS 。

下面,我以 EJS 的语法为例,实现一个简单的字符串模板引擎。模版引擎的编译流程如下:

首先,需要编译模板字符串,将其转换为 JS 能够理解的语法。

第一步是利用正则表达式,区分出字符串中哪些是模板语法,哪些是正常的 HTML 标签。以下是一个 EJS 语法的例子:

代码语言:javascript
复制
<ul>
    <% for(var i=0; i<supplies.length; i++) {%>
        <li><%= supplies[i] %></li>
    <% } %>
</ul>

在‘<%=’和‘%>’之间是 JS 的表达式,而在‘<%’和’%>‘之间是普通的 JS 语句,可以进行逻辑判断和条件循环等操作。可以使用以下正则表达式抽取:

代码语言:javascript
复制
// 匹配表达式,只能有一行
let evalExpr = /\<\%\=(.+?)\%\>/g;

// 匹配语句,可以有多行
let expr = /\<\%([\s\S]+?)\%\>/g;

对于普通的 HTML 标签,需要用自定义的 echo 函数包裹一下,在使用 eval 函数编译的时候直接输出字符串。echo 函数的定义如下:

代码语言:javascript
复制
// 临时变量,保存编译后的模板字符串
let output = "";
  
// 直接将html字符串拼接到output后面
function echo(html){
    output += html;
}

完整的 compile 函数代码如下:

代码语言:javascript
复制
function compile(template){
  
    // 匹配表达式,只能有一行
    let evalExpr = /\<\%\=(.+?)\%\>/g;
  
    // 匹配语句,可以有多行
    let expr = /\<\%([\s\S]+?)\%\>/g;
  
    // 内容为空的部分
    let empty = /echo\(\"\"\);/g;
  
    template = template
        // 转换JS表达式
        .replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
    
        // 转换JS语句
        .replace(expr, '`); \n $1 \n echo(`');
  
    // 在模板的最外层包裹一个echo
    template = 'echo(`' + template + '`);';
  
    // 清除空的echo
    template = template
        .replace(empty, "");
  
    // 保存编译后的字符串,此处用了ES6的模板字符串特性,相当于eval了一下
    let script = 
        `(function parse(data){
  
            // 临时变量,保存编译后的模板字符串
            var output = "";
  
            // 直接将html字符串拼接到output后面
            function echo(html){
                output += html;
            }
  
            // 包含echo的模板字符串
            ${ template }
  
            return output;
        })`;
    
    return script;
}

经过正则表达式处理后,下面这段代码:

代码语言:javascript
复制
<ul>
    <% for(var i=0; i<supplies.length; i++) {%>
        <li><%= supplies[i] %></li>
    <% } %>
</ul>

会转化为以下形式:

代码语言:javascript
复制
    echo(`<ul>`); 
        for(var i=0; i<data.supplies.length; i++) { 
            echo(`<li>`); 
                echo( data.supplies[i] ); 
            echo(`</li>`); 
        }  
    echo(`</ul>`);

第二步是将模板中用到的数据 data 注入到 compile 函数的 parse 子函数中,生成最终的字符串。

最后,我们再通过 innerHTML ,把字符串插入到 DOM 合适的位置。

字符串模板的优缺点

字符串模板之所以能够更新页面,最核心的原理是使用 innerHTML 这个 api 将字符串直接插入到 DOM 节点中。因此,我们分析字符串模板的优缺点就离不开使用 innerHTML 更新DOM 的优缺点。

先谈谈优点:

  • 直观,容易理解。更新后的 DOM 结构可以一目了然的反映在字符串当中。
  • 容易维护。当需要更改模板时,直接改相应字符串就可以,新人也容易上手。
  • 可用于服务端渲染。简单的字符串拼接,不依赖 DOM ,对应的字符串可由服务器端直接生成。

再来谈谈缺点:

  • 安全隐患。模板字符串中完全可以出现此类代码:<img src="69" onerror="alert('xss')">
  • 慢!特别对于需要频繁更新的场景。由于 innerHTML 是直接替换掉原有元素,因此就涉及到相应节点和对应事件的卸载,然后再装载新的节点和事件。在这个过程中,界面也会被重排和重绘,对性能是严重的损耗。
  • 不智能。当只需要修改模板里面的某一部分数据时,整个模板页都需要被刷新。
  • 维护困难。这不是打脸嘛,上面才说了容易维护,这里又讲维护困难!?这当然是有原因的嘛。当不需要考虑性能的时候,一个页面可能只需要维护一个模板,这难道不简单?但考虑到性能的时候,就需要对模板进行拆分和拼装,维护这些相互依赖的模板会让人很崩溃。

总结

综上所述,我们可以很简单的总结出字符串模板引擎的使用场景:如果你的应用比较简单,交互也不多,也希望有一个快速的首屏时间,请使用字符串模板引擎。反之,更先进的基于Dom 或者虚拟 Dom 操作的模板引擎可能是更好的选择。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯专有云 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档