您的位置:澳门新葡8455最新网站 > Web前端 > HTML也可以静态编译,损害了复用性

HTML也可以静态编译,损害了复用性

发布时间:2019-12-09 15:19编辑:Web前端浏览(195)

    HTML也足以静态编写翻译?

    2016/11/30 · HTML5 · 1 评论 · binding.scala, React, 前端

    正文小编: 伯乐在线 - ThoughtWorks 。未经我许可,幸免转发!
    招待参预伯乐在线 专辑作者。

    More than React连串文章:

    《More than React(风姿罗曼蒂克)为何ReactJS不适合复杂的前端项目?》

    《More than React(二)React.Component损害了复用性?》

    《More than React(三)虚拟DOM已死?》

    《More than React(四)HTML也能够静态编写翻译?》


    《More than React》体系的上风流倜傥篇小说《虚拟DOM已死?》相比较了Binding.scala和任何框架的渲染机制。本篇随笔上将介绍Binding.scala中的XHTML语法。

    React.Component 损伤了复用性?

    2016/09/07 · 底工本领 · binding.scala, data-binding, React, scala.js

    正文作者: 伯乐在线 - ThoughtWorks 。未经小编许可,禁绝转发!
    迎接参与伯乐在线 专栏审核人。

    本连串的上后生可畏篇文章《干什么ReactJS不切合复杂的前端项目》列举了前端开采中的种种痛点。本篇文章上将详细探求个中“复用性”痛点。大家将用原生 DHTML API 、 ReactJS 和 Binding.scala 达成同一个亟需复用的竹签编辑器,然后比超级多个标签编辑器哪个落成难度更低,哪个越来越好用。

    此外前端框架的难点

    标签编辑器的效率供给

    在InfoQ的居多篇章都有标签。例如本文的竹签是“binding.scala”、“data-binding”、“scala.js”。

    万一你要付出二个博客系统,你也冀望博客小编能够加上标签。所以你大概会提供标签编辑器供博客作者利用。

    如图所示,标签编辑器在视觉上分为两行。

    图片 1

    先是行展示已经增进的具备标签,每个标签旁边有个“x”按键能够去除标签。第二行是贰个文本框和一个“Add”按键能够把文本框的内容丰盛为新标签。每便点击“Add”按键时,标签编辑器应该检查标签是或不是曾经增添过,防止再次加多标签。而在中标增加标签后,还应清空文本框,以便客户输入新的价签。

    而外顾客分界面以外,标签编辑器还应当提供 API 。标签编辑器所在的页面能够用 API 填入开头标签,也足以调用 API 任何时候增加和删除查改标签。就算顾客增加和删除了标签,应该有某种机制文告页面包车型地铁别的一些。

    对HTML的不尽扶助

    先前我们接纳任何前端框架,举例Cycle.js 、Widok、ScalaTags时,由于框架不辅助HTML语法,前端程序员被迫浪费大量岁月,手动把HTML改写成代码,然后稳步调节和测量试验。

    尽管是援助HTML语法的框架,比如ReactJS,帮衬情况也很四分五裂。

    诸如,在ReactJS中,你不可能这么写:

    JavaScript

    class BrokenReactComponent extends React.Component { render() { return ( <ol> <li class="unsupported-class">不支持 class 属性</li> <li style="background-color: red">不支持 style 属性</li> <li> <input type="checkbox" id="unsupported-for"/> <label for="unsupported-for">不支持 for 属性</label> </li> </ol> ); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class BrokenReactComponent extends React.Component {
      render() {
        return (
          <ol>
            <li class="unsupported-class">不支持 class 属性</li>
            <li style="background-color: red">不支持 style 属性</li>
            <li>
              <input type="checkbox" id="unsupported-for"/>
              <label for="unsupported-for">不支持 for 属性</label>
            </li>
          </ol>
        );
      }
    }

    前端程序员必得手动把 classfor 属性替换来 classNamehtmlFor,还要把内联的 style 样式从CSS语法改成JSON语法,代码技艺运作:

    JavaScript

    class WorkaroundReactComponent extends React.Component { render(卡塔尔 { return ( <ol> <li className="workaround-class">被迫把 class 改成 className</li> <li style={{ backgroundColor: "red" }}>被迫把体制表改成 JSON</li> <li> <input type="checkbox" id="workaround-for"/> <label htmlFor="workaround-for">被迫把 for 改成 htmlFor</label> </li> </ol> 卡塔尔国; } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class WorkaroundReactComponent extends React.Component {
      render() {
        return (
          <ol>
            <li className="workaround-class">被迫把 class 改成 className</li>
            <li style={{ backgroundColor: "red" }}>被迫把样式表改成 JSON</li>
            <li>
              <input type="checkbox" id="workaround-for"/>
              <label htmlFor="workaround-for">被迫把 for 改成 htmlFor</label>
            </li>
          </ol>
        );
      }
    }

    这种开拓方式下,前端工程师就算能够把HTML原型复制粘贴到代码中,但还索要大批量改建技巧实际运营。比Cycle.js、Widok恐怕ScalaTags省不断太多事。

    原生 DHTML 版

    先是,作者试着不用任何前端框架,直接调用原生的 DHTML API 来兑现标签编辑器,代码如下:

    JavaScript

    <!DOCTYPE html> <html> <head> <script> var tags = []; function hasTag(tag) { for (var i = 0; i < tags.length; i++) { if (tags[i].tag == tag) { return true; } } return false; } function removeTag(tag) { for (var i = 0; i < tags.length; i++) { if (tags[i].tag == tag) { document.getElementById("tags-parent").removeChild(tags[i].element); tags.splice(i, 1); return; } } } function addTag(tag) { var element = document.createElement("q"); element.textContent = tag; var removeButton = document.createElement("button"); removeButton.textContent = "x"; removeButton.onclick = function (event) { removeTag(tag); } element.appendChild(removeButton); document.getElementById("tags-parent").appendChild(element); tags.push({ tag: tag, element: element }); } function addHandler() { var tagInput = document.getElementById("tag-input"); var tag = tagInput.value; if (tag && !hasTag(tag)) { addTag(tag); tagInput.value = ""; } } </script> </head> <body> <div id="tags-parent"></div> <div> <input id="tag-input" type="text"/> <button onclick="addHandler()">Add</button> </div> <script> addTag("initial-tag-1"); addTag("initial-tag-2"); </script> </body> </html>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    &lt;!DOCTYPE html&gt;
    &lt;html&gt;
    &lt;head&gt;
      &lt;script&gt;
        var tags = [];
     
        function hasTag(tag) {
          for (var i = 0; i &lt; tags.length; i++) {
            if (tags[i].tag == tag) {
              return true;
            }
          }
          return false;
        }
     
        function removeTag(tag) {
          for (var i = 0; i &lt; tags.length; i++) {
            if (tags[i].tag == tag) {
              document.getElementById("tags-parent").removeChild(tags[i].element);
              tags.splice(i, 1);
              return;
            }
          }
        }
     
        function addTag(tag) {
          var element = document.createElement("q");
          element.textContent = tag;
          var removeButton = document.createElement("button");
          removeButton.textContent = "x";
          removeButton.onclick = function (event) {
            removeTag(tag);
          }
          element.appendChild(removeButton);
          document.getElementById("tags-parent").appendChild(element);
          tags.push({
            tag: tag,
            element: element
          });
        }
     
        function addHandler() {
          var tagInput = document.getElementById("tag-input");
          var tag = tagInput.value;
          if (tag &amp;&amp; !hasTag(tag)) {
            addTag(tag);
            tagInput.value = "";
          }
        }
      &lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
      &lt;div id="tags-parent"&gt;&lt;/div&gt;
      &lt;div&gt;
        &lt;input id="tag-input" type="text"/&gt;
        &lt;button onclick="addHandler()"&gt;Add&lt;/button&gt;
      &lt;/div&gt;
      &lt;script&gt;
        addTag("initial-tag-1");
        addTag("initial-tag-2");
      &lt;/script&gt;
    &lt;/body&gt;
    &lt;/html&gt;
     

    为了完成标签编辑器的效果与利益,小编用了 45 行 JavaScript 代码来编排 UI 逻辑,外加若干的 HTML <div> 外加两行 JavaScript 代码填入开首化数据。

    HTML 文件中硬编码了多少个 <div>。这些<div> 本人并非动态成立的,但能够用作容器,放置任何动态成立的成分。

    代码中的函数来会把网页内容动态更新到这几个 <div> 中。所以,要是要在同二个页面展现八个标签编辑器,id 就能够冲突。由此,以上代码未有复用性。

    就算用 jQuery 代替 DHTML API,代码复用依旧很难。为了复用 UI ,jQuery 开辟者平常务必附加扩充代码,在 onload 时扫描整个网页,寻找全体一定 class 属性的要素,然后对那么些成分举办修改。对于复杂的网页,那些 onload 时运维的函数比较轻便就能够冲突,比方三个函数改良了一个 HTML 成分,日常以致另生龙活虎处代码受影响而里面景色错乱。

    不相称原生DOM操作

    别的,ReactJS等局地前端框架,会生成设想DOM。虚构DOM不可能合营浏览器原生的DOM API ,招致和jQuery、D3等其他库合作时举步维艰。举个例子ReactJS更新DOM对象时平常会损坏掉jQuery控件。

    Reddit多三个人商量了那个难题。他们从没章程,只好弃用jQuery。小编司的某客商在用了ReactJS后也被迫用ReactJS重写了大气jQeury控件。

    ReactJS 完结的价签编辑器组件

    ReactJS 提供了能够复用的零零件,即 React.Component 。假使用 ReactJS 达成标签编辑器,大约能够这么写:

    JavaScript

    class TagPicker extends React.Component { static defaultProps = { changeHandler: tags => {} } static propTypes = { tags: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, changeHandler: React.PropTypes.func } state = { tags: this.props.tags } addHandler = event => { const tag = this.refs.input.value; if (tag && this.state.tags.indexOf(tag) == -1) { this.refs.input.value = ""; const newTags = this.state.tags.concat(tag); this.setState({ tags: newTags }); this.props.changeHandler(newTags); } } render() { return ( <section> <div>{ this.state.tags.map(tag => <q key={ tag }> { tag } <button onClick={ event => { const newTags = this.state.tags.filter(t => t != tag); this.setState({ tags: newTags }); this.props.changeHandler(newTags); }}>x</button> </q> ) }</div> <div> <input type="text" ref="input"/> <button onClick={ this.addHandler }>Add</button> </div> </section> ); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    class TagPicker extends React.Component {
     
      static defaultProps = {
        changeHandler: tags =&gt; {}
      }
     
      static propTypes = {
        tags: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
        changeHandler: React.PropTypes.func
      }
     
      state = {
        tags: this.props.tags
      }
     
      addHandler = event =&gt; {
        const tag = this.refs.input.value;
        if (tag &amp;&amp; this.state.tags.indexOf(tag) == -1) {
          this.refs.input.value = "";
          const newTags = this.state.tags.concat(tag);
          this.setState({
            tags: newTags
          });
          this.props.changeHandler(newTags);
        }
      }
     
      render() {
        return (
          &lt;section&gt;
            &lt;div&gt;{
              this.state.tags.map(tag =&gt;
                &lt;q key={ tag }&gt;
                  { tag }
                  &lt;button onClick={ event =&gt; {
                    const newTags = this.state.tags.filter(t =&gt; t != tag);
                    this.setState({ tags: newTags });
                    this.props.changeHandler(newTags);
                  }}&gt;x&lt;/button&gt;
                &lt;/q&gt;
              )
            }&lt;/div&gt;
            &lt;div&gt;
              &lt;input type="text" ref="input"/&gt;
              &lt;button onClick={ this.addHandler }&gt;Add&lt;/button&gt;
            &lt;/div&gt;
          &lt;/section&gt;
        );
      }
     
    }
     

    如上 51 行 ECMAScript 2015代码达成了贰个标签编辑器组件,即TagPicker。固然代码量比 DHTML 版长了一丢丢,但复用性大大晋级了。

    尽管您不用 ECMAScript 二〇一四 的话,那么代码还组织首领一些,何况亟需管理局部JavaScript 的坑,举个例子在回调函数中用持续 this

    ReactJS 开垦者能够每二十三日用 ReactDOM.render 函数把 TagPicker 渲染到任何空白元素内。其余,ReactJS 框架能够在 stateprops 改换时触发 render ,进而防止了手动矫正现成的 DOM。

    假诺不思忖冗余的 key 属性,单个组件内的并行 ReactJS 还算差强人意。不过,复杂的网页构造往往须要多少个构件层层嵌套,这种老爹和儿子组件之间的人机联作,ReactJS 就很伤脑筋了。

    比如说,固然要求在 TagPicker 之外显示全部的竹签,每当顾客增加和删除标签,那个标签也要自动更新。要兑现这些成效,必要给 TagPicker 传入 changeHandler 回调函数,代码如下:

    JavaScript

    class Page extends React.Component { state = { tags: [ "initial-tag-1", "initial-tag-2" ] }; changeHandler = tags => { this.setState({ tags }卡塔尔; }; render(卡塔尔 { return ( <div> <TagPicker tags={ this.state.tags } changeHandler={ this.changeHandler }/> <h3>全体标签:</h3> <ol>{ this.state.tags.map(tag => <li>{ tag }</li> 卡塔尔 }</ol> </div> 卡塔尔(英语:State of Qatar); } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Page extends React.Component {
     
      state = {
        tags: [ "initial-tag-1", "initial-tag-2" ]
      };
     
      changeHandler = tags =&gt; {
        this.setState({ tags });
      };
     
      render() {
        return (
          &lt;div&gt;
            &lt;TagPicker tags={ this.state.tags } changeHandler={ this.changeHandler }/&gt;
            &lt;h3&gt;全部标签:&lt;/h3&gt;
            &lt;ol&gt;{ this.state.tags.map(tag =&gt; &lt;li&gt;{ tag }&lt;/li&gt; ) }&lt;/ol&gt;
          &lt;/div&gt;
        );
      }
     
    }
     

    为了能接触页面别的一些更新,作者被迫扩张了叁个 21 行代码的 Page 组件。

    Page 组件必得贯彻 changeHandler 回调函数。每当回调函数触发,调用 Page 自己的 setState 来触发 Page 重绘。

    从那个事例,大家得以看来, ReactJS 能够轻松的撤销轻松的标题,但碰撞档案的次序复杂、交互作用频仍的网页,实现起来就很麻烦。使用 ReactJS 的前端项目充满了各类 xxxHandler 用来在组件中传递新闻。笔者参预的某国外客商项目,平均各类组件大约供给传入三个回调函数。如果档期的顺序嵌套深,创制网页时,平常须要把回调函数从最顶层的机件风度翩翩薄薄传入最尾部的零零件,而当事件触发时,又需求风姿罗曼蒂克稀少把事件消息往外传。整个前端项目有超过常规八分之四代码都在如此绕圈子。

    Binding.scala中的XHTML

    到现在有了Binding.scala ,能够在@dom主意中,直接编写XHTML。比如:

    JavaScript

    @dom def introductionDiv = { <div style="font-size:0.8em"> <h3>Binding.scala的优点</h3> <ul> <li>简单</li> <li>概念少<br/>功能多</li> </ul> </div> }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @dom def introductionDiv = {
      <div style="font-size:0.8em">
        <h3>Binding.scala的优点</h3>
        <ul>
          <li>简单</li>
          <li>概念少<br/>功能多</li>
        </ul>
      </div>
    }

    上述代码会被编写翻译,间接创立真实的DOM对象,而从未虚构DOM。

    Binding.scala对浏览器原生DOM的扶持很好,你能够在此些DOM对象上调用DOM API,与 D3、jQuery等别的库交互作用也全然没不平日。

    ReactJS对XHTML语法的不尽不全。比较之下,Binding.scala扶植完整的XHTML语法,前端程序猿能够一向把设计好的HTML原型复制粘贴到代码中,整个网址就能够运行了。

    Binding.scala 的为主用法

    在授课 Binding.scala 怎么着得以达成标签编辑器在此以前,小编先介绍一些 Binding.scala 的根底知识:

    Binding.scala 中的最小复用单位是数码绑定表明式,即 @dom 方法。每个 @dom 方法是大器晚成段 HTML 模板。比如:

    JavaScript

    // 两个 HTML 换行符 @dom def twoBr = <br/><br/>

    1
    2
    3
    // 两个 HTML 换行符
    @dom def twoBr = &lt;br/&gt;&lt;br/&gt;
     

    JavaScript

    // 一个 HTML 标题 @dom def myHeading(content: String) = <h1>{content}</h1>

    1
    2
    3
    // 一个 HTML 标题
    @dom def myHeading(content: String) = &lt;h1&gt;{content}&lt;/h1&gt;
     

    每种模板还足以选取bind语法满含其余子模板,比如:

    JavaScript

    @dom def render = { <div> { myHeading("Binding.scala的特点").bind } <p> 代码短 { twoBr.bind } 概念少 { twoBr.bind } 功能多 </p> </div> }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @dom def render = {
      &lt;div&gt;
        { myHeading("Binding.scala的特点").bind }
        &lt;p&gt;
          代码短
          { twoBr.bind }
          概念少
          { twoBr.bind }
          功能多
        &lt;/p&gt;
      &lt;/div&gt;
    }
     

    您能够参见附录:Binding.scala飞速上手指南,学习上手Binding.scala开采的具体步骤。

    其它,本连串第四篇小说《HTML也足以编写翻译》还将列出Binding.scala所支持的全部HTML模板本性。

    Binding.scala中XHTML的类型

    @dom措施中XHTML对象的连串是Node的派生类。

    比如,<div></div> 的门类正是HTMLDivElement,而 <button></button> 的品种正是 HTMLButtonElement。

    此外, @dom 阐明会改良总体艺术的重返值,包装成一个Binding。

    JavaScript

    @dom def typedButton: Binding[HTMLButtonElement] = { <button>按钮</button> }

    1
    2
    3
    @dom def typedButton: Binding[HTMLButtonElement] = {
      <button>按钮</button>
    }

    注意typedButton是个原生的HTMLButtonElement,所以能够向来对它调用 DOM API。举例:

    JavaScript

    @dom val autoPrintln: Binding[Unit] = { println(typedButton.bind.innerHTML卡塔尔国 // 在调整桃园打字与印刷开关内部的 HTML } autoPrintln.watch(卡塔尔(قطر‎

    1
    2
    3
    4
    @dom val autoPrintln: Binding[Unit] = {
      println(typedButton.bind.innerHTML) // 在控制台中打印按钮内部的 HTML
    }
    autoPrintln.watch()

    这段代码中,typedButton.bind.innerHTML 调用了 DOM API HTMLButtonElement.innerHTML。通过autoPrintln.watch(),每当开关爆发更新,autoPrintln中的代码就能够推行三遍。

    Binding.scala完成的竹签编辑器模板

    末段,下文将显得什么用Binding.scala实现标签编辑器。

    标签编辑器要比刚刚牵线的HTML模板复杂,因为它不可是静态模板,还蕴涵人机联作。

    JavaScript

    @dom def tagPicker(tags: Vars[String]) = { val input: Input = <input type="text"/> val addHandler = { event: Event => if (input.value != "" && !tags.get.contains(input.value)) { tags.get += input.value input.value = "" } } <section> <div>{ for (tag <- tags) yield <q> { tag } <button onclick={ event: Event => tags.get -= tag }>x</button> </q> }</div> <div>{ input } <button onclick={ addHandler }>Add</button></div> </section> }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @dom def tagPicker(tags: Vars[String]) = {
      val input: Input = &lt;input type="text"/&gt;
      val addHandler = { event: Event =&gt;
        if (input.value != "" &amp;&amp; !tags.get.contains(input.value)) {
          tags.get += input.value
          input.value = ""
        }
      }
      &lt;section&gt;
        &lt;div&gt;{
          for (tag &lt;- tags) yield &lt;q&gt;
            { tag }
            &lt;button onclick={ event: Event =&gt; tags.get -= tag }&gt;x&lt;/button&gt;
          &lt;/q&gt;
        }&lt;/div&gt;
        &lt;div&gt;{ input } &lt;button onclick={ addHandler }&gt;Add&lt;/button&gt;&lt;/div&gt;
      &lt;/section&gt;
    }
     

    本条标签编辑器的 HTML 模板少年老成共用了 18 行代码就完结好了。

    标签编辑器中必要出示当前全部标签,所以这里用tags: Vars[String]封存全体的价签数据,再用for/yield循环把tags中的各类标签渲染成UI成分。

    Vars 是支撑数据绑定的列表容器,每当容器中的数据产生退换,UI就能自行改换。所以,在x按键中的onclick事件中剔除tags中的数据时,页面上的标签就能自动随之消逝。近似,在Add按钮的onclick中向tags中增加数据时,页面上也会活动发出相应的价签。

    Binding.scala不但达成标签编辑器比 ReactJS 轻松,並且用起来也比 ReactJS 轻巧:

    JavaScript

    @dom def render(卡塔尔国 = { val tags = Vars("initial-tag-1", "initial-tag-2"卡塔尔国<div> { tagPicker(tags卡塔尔国.bind } <h3>全部标签:</h3> <ol>{ for (tag <- tags卡塔尔(英语:State of Qatar) yield <li>{ tag }</li> }</ol> </div> }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @dom def render() = {
      val tags = Vars("initial-tag-1", "initial-tag-2")
      &lt;div&gt;
        { tagPicker(tags).bind }
        &lt;h3&gt;全部标签:&lt;/h3&gt;
        &lt;ol&gt;{ for (tag &lt;- tags) yield &lt;li&gt;{ tag }&lt;/li&gt; }&lt;/ol&gt;
      &lt;/div&gt;
    }
     

    假定用 9 行代码另写三个 HTML 模板,在模板中调用刚才完结好的 tagPicker 就行了。

    完整的 DEMO 请访问 。

    在 Binding.scala 没有必要像 ReactJS 那样编写 changeHandler 之类的回调函数。每当顾客在 tagPicker 输入新的价签时,tags 就能够改善,网页也就能自动随之转移。

    相比较 ReactJS 和 Binding.scala 的代码,可以窥见以下分别:

    • Binding.scala 的开拓者能够用相同 tagPicker 这样的 @dom 方法表示 HTML 模板,而无需组件概念。
    • Binding.scala 的开垦者能够在方式之间传递 tags 那样的参数,而不必要 props 概念。
    • Binding.scala 的开垦者能够在情势钦命义局地变量表示情状,而无需 state 概念。

    总的看 Binding.scala 要比 ReactJS 精简不菲。

    倘诺您用过 ASP 、 PHP 、 JSP 之类的服务端网页模板语言, 你会意识和 Binding.scala 的 HTML 模板很像。

    选择 Binding.scala 一点也不必要函数式编制程序知识,只要把规划工具中生成的 HTML 原型复制到代码中,然后把会变的生机勃勃对用花括号代替、把重复的一些用 for / yield 代替,网页就搞好了。

    其他HTML节点

    Binding.scala支持HTML注释:

    JavaScript

    @dom def comment = { <!-- 你看不见小编 --> }

    1
    2
    3
    @dom def comment = {
      <!-- 你看不见我 -->
    }

    Binding.scala也支持CDATA块:

    JavaScript

    @dom def inlineStyle = { <section> <style><![CDATA[ .highlight { background-color:gold } ]]></style> <p class="highlight">Binding.scala真好用!</p> </section> }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @dom def inlineStyle = {
      <section>
        <style><![CDATA[
          .highlight {
            background-color:gold
          }
        ]]></style>
        <p class="highlight">Binding.scala真好用!</p>
      </section>
    }

    结论

    本文相比了分裂技术栈中完成和选拔可复用的价签编辑器的难度。

    原生 HTML ReactJS Binding.scala
    实现标签编辑器需要代码行数 45行 51行 17行
    实现标签编辑器的难点 在代码中动态更新HTML页面太繁琐 实现组件的语法很笨重
    使用标签编辑器并显示标签列表需要代码行数 难以复用 21行 8行
    阻碍复用的难点 静态HTML元素难以模块化 交互组件之间层层传递回调函数过于复杂

    Binding.scala 不注明“组件”之类的噱头,而以更轻易的“方法”为最小复用单位,让编制程序体验越来越一箭穿心,获得了越来越好的代码复用性。

    本种类下后生可畏篇文章将相比 ReactJS 的假造 DOM 机制和 Binding.scala 的准确数据绑定机制,报料 ReactJS 和 Binding.scala 雷同用法背后掩藏的不等算法。

    内嵌Scala代码

    除去能够把XHTML内嵌在Scala代码中的 @dom 方法中,Binding.scala 还帮助用 { ... } 语法把 Scala 代码内嵌到XHTML中。比如:

    JavaScript

    @dom def randomParagraph = { <p>生成贰个私行数: { math.random.toString }</p> }

    1
    2
    3
    @dom def randomParagraph = {
      <p>生成一个随机数: { math.random.toString }</p>
    }

    XHTML中内嵌的Scala代码能够用 .bind 绑定变量只怕调用别的 @dom 方法,比如:

    JavaScript

    val now = Var(new Date卡塔尔国 window.setInterval(1000卡塔尔国 { now := new Date } @dom def render = { <div> 今后时间:{ now.bind.toString } { introductionDiv.bind } { inlineStyle.bind } { typedButton.bind } { comment.bind } { randomParagraph.bind } </div> }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    val now = Var(new Date)
    window.setInterval(1000) { now := new Date }
     
    @dom def render = {
      <div>
        现在时间:{ now.bind.toString }
        { introductionDiv.bind }
        { inlineStyle.bind }
        { typedButton.bind }
        { comment.bind }
        { randomParagraph.bind }
      </div>
    }

    上述代码渲染出的网页中,时间会动态改换。

    连锁链接

    • Binding.scala 项目主页
    • Binding.scala • TodoMVC 项目主页
    • Binding.scala • TodoMVC DEMO
    • Binding.scala • TodoMVC 以外的任何 DEMO
    • JavaScript 到 Scala.js 移植指南
    • Scala.js 项目主页
    • Scala API 参谋文书档案
    • Scala.js API 参考文书档案
    • Scala.js DOM API 参谋文书档案
    • Binding.scala火速上手指南
    • Binding.scala API参考文书档案
    • Binding.scala 的 Gitter 聊天室

      1 赞 1 收藏 评论

    强类型的 XHTML

    Binding.scala中的XHTML 都帮衬静态类型检查。举例:

    JavaScript

    @dom def typo = { val myDiv = <div typoProperty="xx">content</div> myDiv.typoMethod() myDiv }

    1
    2
    3
    4
    5
    @dom def typo = {
      val myDiv = <div typoProperty="xx">content</div>
      myDiv.typoMethod()
      myDiv
    }

    鉴于上述代码有拼写错误,编写翻译器就能够报错:

    JavaScript

    typo.scala:23: value typoProperty is not a member of org.scalajs.dom.html.Div val myDiv = <div typoProperty="xx">content</div> ^ typo.scala:24: value typoMethod is not a member of org.scalajs.dom.html.Div myDiv.typoMethod() ^

    1
    2
    3
    4
    5
    6
    typo.scala:23: value typoProperty is not a member of org.scalajs.dom.html.Div
            val myDiv = <div typoProperty="xx">content</div>
                         ^
    typo.scala:24: value typoMethod is not a member of org.scalajs.dom.html.Div
            myDiv.typoMethod()
                  ^

    关于小编:ThoughtWorks

    图片 2

    ThoughtWorks是一家中外IT咨询集团,追求杰出软件品质,致力于科学技术驱动商业变革。长于创设定制化软件出品,支持客商高效将概念转变为价值。同时为客商提供客户体验设计、技艺战术咨询、协会转型等咨询服务。 个人主页 · 笔者的小说 · 84 ·   

    图片 3

    内联CSS属性

    style 属性设置内联样式时,style 的值是个字符串。比方:

    JavaScript

    @dom def invalidInlineStyle = { <div style="color: blue; typoStyleName: typoStyleValue"></div> }

    1
    2
    3
    @dom def invalidInlineStyle = {
      <div style="color: blue; typoStyleName: typoStyleValue"></div>
    }

    以上代码中安装的 typoStyleName 样式名写错了,但编写翻译器并未报错。

    要想让编写翻译器能检查内联样式,能够用 style: 前缀而不用 style 属性。比如:

    JavaScript

    @dom def invalidInlineStyle = { <div style:color="blue" style:typoStyleName="typoStyleValue"></div> }

    1
    2
    3
    @dom def invalidInlineStyle = {
      <div style:color="blue" style:typoStyleName="typoStyleValue"></div>
    }

    那么编写翻译器就能够报错:

    JavaScript

    typo.scala:28: value typoStyleName is not a member of org.scalajs.dom.raw.CSSStyleDeclaration <div style:color="blue" style:typoStyleName="typoStyleValue"></div> ^

    1
    2
    3
    typo.scala:28: value typoStyleName is not a member of org.scalajs.dom.raw.CSSStyleDeclaration
            <div style:color="blue" style:typoStyleName="typoStyleValue"></div>
             ^

    那样一来,能够在编写制定代码时就领会属性有未有写对。不像原生JavaScript / HTML / CSS那样,蒙受bug也查不出去。

    自定义属性

    若是您须求绕开对质量的类型检查,以便为HTML成分增多定制数据,你能够属性加上 data: 前缀,比如:

    JavaScript

    @dom def myCustomDiv = { <div data:customAttributeName="attributeValue"></div> }

    1
    2
    3
    @dom def myCustomDiv = {
      <div data:customAttributeName="attributeValue"></div>
    }

    那样一来Scala编写翻译器就不会报错了。

    结论

    本文的完整DEMO请访谈 ScalaFiddle。

    从那个示例能够看来,Binding.scala 一方面辅助完全的XHTML ,能够从高保真HTML 原型无缝移植到动态网页中,开垦进程颇为通畅。其他方面,Binding.scala 能够在编写翻译时静态检查XHTML中现身语法错误和语义错误,进而防止bug 。

    以下表格相比较了ReactJS和Binding.scala对HTML语法的支撑程度:

    ReactJS Binding.scala
    是否支持HTML语法? 残缺支持
    是否支持标准的style属性? 不支持,必须改用 JSON 语法
    是否支持标准的class属性? 不支持,必须改用className
    是否支持标准的for属性? 不支持,必须改用htmlFor
    是否支持HTML注释? 不支持
    是否兼容原生DOM操作? 不兼容
    是否兼容jQuery? 不兼容
    能否在编译时检查出错误? 不能

    自己将要下生龙活虎篇小说中牵线 Binding.scala 怎么样贯彻服务器发送央求并在页面呈现结果的流水线。

    相关链接

    • Binding.scala 项目主页
    • Binding.scala • TodoMVC 项目主页
    • Binding.scala • TodoMVC DEMO
    • Binding.scala • TodoMVC 以外的别的DEMO
    • JavaScript 到 Scala.js 移植指南
    • Scala.js 项目主页
    • Scala API 参谋文书档案
    • Scala.js API 参照他事他说加以考察文书档案
    • Scala.js DOM API 参照他事他说加以考察文书档案
    • Binding.scala急迅上手指南
    • Binding.scala API参照他事他说加以考察文书档案
    • Binding.scala 的 Gitter 聊天室

      1 赞 1 收藏 1 评论

    关于小编:ThoughtWorks

    图片 4

    ThoughtWorks是一家中外IT咨询公司,追求优良软件品质,致力于科技(science and technology卡塔尔(قطر‎驱动商业变革。长于塑造定制化软件出品,协理客户火速将定义转变为价值。同期为客户提供客商体验设计、技艺战略咨询、协会转型等咨询服务。 个人主页 · 作者的稿子 · 84 ·   

    图片 5

    本文由澳门新葡8455最新网站发布于Web前端,转载请注明出处:HTML也可以静态编译,损害了复用性

    关键词:

上一篇:没有了

下一篇:没有了