• 前端模板技术面面观(2)


    此文已由作者郑海波授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验


    Living Template Engine

    String-based 和 Dom-based的模板技术都或多或少的依赖与innerHTML, 它们的区别是一个是主要是为了Rendering 一个是为了 Parsing 提取信息

    所以为什么不结合它们两者来完全移除对innerHTML的依赖呢?

    事实上,值得庆幸的是,已经有几个现实例子在这么做了。

    例子

    1. htmlbar: 运行在handlebar 之后的二次编译

    2. ractivejs: 独立

    3. Regularjs 独立, 本文作者结精之一

    基本原理


    就如图中所示,parse和compile的过程分别类似于String-based 模板技术 和 Dom-based模板技术。

    下面来完整讲述下这两个过程

    1 . Parsing

    首先我们使用一个内建DSL来解析模板字符串并输出AST。

    例如,在regularjs中,下面这段简单的模板字符串

    <button {{#if !isLogin}} on-click={{this.login()}} {{/if}}>
      {{isLogin? 'Login': 'Wellcome'}}
    </button>'

    会被解析为以下这段数据结构

    [
      {
        "type": "element",
        "tag": "button",
        "attrs": [
          {
            "type": "if",
            "test": {
              "type": "expression",
              "body": "(!_d_['isLogin'])",
              "constant": false,
              "setbody": false
            },
            "consequent": [
              [
                {
                  "type": "attribute",
                  "name": "on-click",
                  "value": {
                    "type": "expression",
                    "body": "_c_['login']()",
                    "constant": false,
                    "setbody": false
                  }
                }
              ]
            ],
            "alternate": []
          }
        ],
        "children": [
          {
            "type": "expression",
            "body": "_d_['isLogin']?'Login':'Wellcome'",
            "constant": false,
            "setbody": false
          }
        ]
      }
    ]

    这个过程有以下特点

    1. 灵活强大的语法,因为它与基于字符串的模板一般,DSL是自治的,完全不依赖与html,你可以想像下dom-based的模板的那些语法相关的指令,事实上它们甚至无法表达上述那段简单的模板的逻辑。

    2. Living模板技术需要同时处理dsl元素 与 xml元素来实现最终视图层的活动性,即它们是dom-aware的,而在字符串模板中其实xml元素完全可以无需关心,它们被统一视为文本元素。

    2 Compiler

    结合特定的数据模型(在regularjs中,是一个裸数据), 模板引擎层级游历AST并递归生成Dom节点(不会涉及到innerHTML). 与此同时,指令、事件和插值等binder也同时完成了绑定,使得最终产生的Dom是与Model相维系的,即是活动的.

    事实上,Living template的compile过程相对与Dom-based的模板技术更加纯粹, 因为它完全的依照AST生成,而不是在原Dom上的改写。

    以上面的模板代码的一个插值为例:{{isLogin? 'Login': 'Wellcome'}}。一旦regularjs的引擎遇到这段模板与代表的语法元素节点,会进入如下函数处理

    // some sourcecode from regularjs
    walkers.expression = function(ast){
      var node = document.createTextNode("");
      this.$watch(ast, function(newval){
        dom.text(node, "" + (newval == null? "": String(newval)));
      })
      return node;
    }

    正如我们所见, 归功于$watch函数,一旦表达式发生改变,文本节点也会随之改变,这一切其实与angularjs并无两样(事实上regularjs同样也是基于脏检查)

    与Dom-based 模板技术利用Dom节点承载信息所不同的是,它的中间产物AST 承载了所有Compile过程中需要的信息(语句, 指令, 属性...等等). 这带来几个好处

    1. 轻量级, 在Dom中进行读写操作是低效的.

    2. 可重用的.

    3. 可序列化 , 你可以在本地或服务器端预处理这个过程。

    4. 安全, 因为安全不需要innerHTML帮我们生成初始Dom

    如果你查看Living Template的输出,你会发现是这样的


    只有需要的内容被输出了

    总结Living templating

    我们可以发现Living templating几乎同时拥有String-based和Dom-based模板技术的优点

    利用一个如字符串模板的自定义DSL来描述结构来达到了语法上的灵活性,并在Parse后承载信息(AST)。而在Compile阶段,利用AST和Dom API来完成View的组装,在组装过程中,我们同样可以引入Dom-based模板技术的诸如Directive等优良的种子。

    living template's 近亲 —— React

    React当然也可以称之为一种模板解决方案,它同样也巧妙规避了innerHTML,不过却使用的是截然不同的策略:react使用一种virtual dom 的技术,它也同样基于脏检查,不过与众不同的是,它的脏检查发生在view层面,即发生在virtual dom上,从而可以以较小的开销来实现局部更新。

    Example

    var MyComponent = React.createClass({
     render: function() {
       if (this.props.first) {
         return <div className="first"><span>A Span</span></div>;
       } else {
         return <div className="second"><p>A Paragraph</p></div>;
       }
     }
    });

    同样的逻辑使用regularjs描述

    {{#if first}}
      <div className="first"><span>A Span</span></div>
    {{#else}}
      <div className="second"><p>A Paragraph</p></div>;
    {{/if}}

    仁者见仁智者见智, 反正我倾向于使用模板来描述结构,而不是杂糅Virtual dom和js语句。你呢?

    值得一提的是,由于React的特性,它两次render之间,内部节点的替换是无法预计的(参考这里),所以无法有效的保留信息,所以它也有大量的关于id的placeholder存在。你可以同样查看react-todomvc生成的节点

    一个全面的对照表

    Contrast /SolutionsString-based templatingDom-based templatingLiving templating
    例子Mustache,DustjsAngularjs, VuejsRegularjs 、Ractivejs、htmlbars
    语法♦♦♦♦♦♦♦♦♦
    活动性X♦♦♦♦♦♦
    性能初始: ♦♦♦
    更新: ♦
    初始: ♦ 
    更新: ♦♦♦
    初始: ♦ 
    更新: ♦♦♦
    安全性♦♦♦♦♦♦♦
    Dom 无关♦♦♦♦♦X♦♦
    SVG support(*1)X♦♦♦♦♦
    1. 任何一类无法被另一类全面替代

    2. 它们并不是无法同时存在的,比如你可以使用字符串模板来生成Dom-based的模板需要的模板字符串。

    参考资料

    1. Template Engines by @Sendhil

    2. string-templating-considered-harmful


    免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

    更多网易技术、产品、运营经验分享请点击


    相关文章:
    【推荐】 知物由学 | AI时代,那些黑客正在如何打磨他们的“利器”?(一)
    【推荐】 HBase基准性能测试报告
    【推荐】 【大数据之数据仓库】选型流水记

  • 相关阅读:
    Inno Setup 6.0.4
    使用Microsoft Enterprise Library 5.0记录日志信息
    Log4net用法
    继续接着上一篇写:使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)[搜片神器]
    磁力王:种子磁力搜索神器使用教程
    C# WebBrowser 网页缩放的方法
    Mysql5.7修改root密码教程
    【MAVEN】maven项目下载更新pom jar包速度慢 解决方案
    C# DataGridView自动保存列的宽度和位置
    Java实现敏感词过滤
  • 原文地址:https://www.cnblogs.com/163yun/p/10065015.html
Copyright © 2020-2023  润新知