• 前端模板技术面面观(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


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

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


    相关文章:
    【推荐】 应对羊毛党的老手段不管用了,但有些公司依然有办法,他们是怎么做的?
    【推荐】 用双十一的故事串起碎片的网络协议(上)

  • 相关阅读:
    ZOJ 1002 Fire Net (火力网)
    UVa OJ 117 The Postal Worker Rings Once (让邮差只走一圈)
    UVa OJ 118 Mutant Flatworld Explorers (变体扁平世界探索器)
    UVa OJ 103 Stacking Boxes (嵌套盒子)
    UVa OJ 110 MetaLoopless Sorts (无循环元排序)
    第一次遇到使用NSNull的场景
    NSURL使用浅析
    从CNTV下载《小小智慧树》
    NSDictionary and NSMutableDictionary
    Category in static library
  • 原文地址:https://www.cnblogs.com/zyfd/p/10065016.html
Copyright © 2020-2023  润新知