博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
More than React(四)HTML也可以静态编译?
阅读量:5862 次
发布时间:2019-06-19

本文共 6206 字,大约阅读时间需要 20 分钟。

《More than React》系列的上一篇文章比较了 Binding.scala 和其他框架的渲染机制。本篇文章中将介绍 Binding.scala 中的 XHTML 语法。

\\

一、其他前端框架的问题

\\

对 HTML 的残缺支持

\\

以前我们使用其他前端框架,比如 Cycle.js 、 Widok 、 ScalaTags 时,由于框架不支持 HTML 语法,前端工程师被迫浪费大量时间,手动把 HTML 改写成代码,然后慢慢调试。

\\

就算是支持 HTML 语法的框架,比如 ReactJS ,支持状况也很残缺不全。

\\

比如,在 ReactJS 中,你不能这样写:

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

前端工程师必须手动把 classfor 属性替换成 classNamehtmlFor,还要把内联的 style 样式从 CSS 语法改成 JSON 语法,代码才能运行:

\\
\class WorkaroundReactComponent extends React.Component {\  render() {\    return (\      \u0026lt;ol\u0026gt;\        \u0026lt;li className=\"workaround-class\"\u0026gt;被迫把 class 改成 className\u0026lt;/li\u0026gt;\        \u0026lt;li style={
{
backgroundColor: \"red\" }}\u0026gt;被迫把样式表改成 JSON\u0026lt;/li\u0026gt;\ \u0026lt;li\u0026gt;\ \u0026lt;input type=\"checkbox\" id=\"workaround-for\"/\u0026gt;\ \u0026lt;label htmlFor=\"workaround-for\"\u0026gt;被迫把 for 改成 htmlFor\u0026lt;/label\u0026gt;\ \u0026lt;/li\u0026gt;\ \u0026lt;/ol\u0026gt;\ );\ }\}
\\

这种开发方式下,前端工程师虽然可以把 HTML 原型复制粘贴到代码中,但还需要大量改造才能实际运行。比 Cycle.js 、 Widok 或者 ScalaTags 省不了太多事。

\\

不兼容原生 DOM 操作

\\

此外,ReactJS 等一些前端框架,会生成虚拟 DOM 。虚拟 DOM 无法兼容浏览器原生的 DOM API ,导致和 jQuery 、 D3 等其他库协作时困难重重。比如 ReactJS 更新 DOM 对象时常常会破坏掉 jQuery 控件。

\\

很多人讨论了这个问题。他们没有办法,只能弃用 jQuery。我司的某客户在用了 ReactJS 后也被迫用 ReactJS 重写了大量 jQeury 控件。

\\

二、Binding.scala 中的 XHTML

\\

现在有了 Binding.scala ,可以在 @dom 方法中,直接编写 XHTML。比如:

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

以上代码会被编译,直接创建真实的 DOM 对象,而没有虚拟 DOM 。

\\

Binding.scala 对浏览器原生 DOM 的支持很好,你可以在这些 DOM 对象上调用 DOM API ,与 D3 、 jQuery 等其他库交互也完全没有问题。

\\

ReactJS 对 XHTML 语法的残缺不全。相比之下,Binding.scala 支持完整的 XHTML 语法,前端工程师可以直接把设计好的 HTML 原型复制粘贴到代码中,整个网站就可以运行了。

\\

Binding.scala 中 XHTML 的类型

\\

@dom 方法中 XHTML 对象的类型是 的派生类。

\\

比如,\u0026lt;div\u0026gt;\u0026lt;/div\u0026gt; 的类型就是 ,而 \u0026lt;button\u0026gt;\u0026lt;/button\u0026gt; 的类型就是 。

\\

此外, @dom 注解会修改整个方法的返回值,包装成一个 。

\\
\@dom def typedButton: Binding[HTMLButtonElement] = {\  \u0026lt;button\u0026gt;按钮\u0026lt;/button\u0026gt;\}
\\

注意typedButton是个原生的HTMLButtonElement,所以可以直接对它调用 DOM API。比如:

\\
\@dom val autoPrintln: Binding[Unit] = {\  println(typedButton.bind.innerHTML) \}\autoPrintln.watch()
\\

这段代码中,typedButton.bind.innerHTML 调用了 DOM API 。通过autoPrintln.watch(),每当按钮发生更新,autoPrintln中的代码就会执行一次。

\\

其他 HTML 节点

\\

Binding.scala 支持 HTML 注释:

\\
\@dom def comment = {\  \}
\\

Binding.scala 也支持 CDATA 块:

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

内嵌 Scala 代码

\\

除了可以把 XHTML 内嵌在 Scala 代码中的 @dom 方法中,Binding.scala 还支持用 { ... } 语法把 Scala 代码内嵌到 XHTML 中。比如:

\\
\@dom def randomParagraph = {\  \u0026lt;p\u0026gt;生成一个随机数: { math.random.toString }\u0026lt;/p\u0026gt;\}
\\

XHTML 中内嵌的 Scala 代码可以用 .bind 绑定变量或者调用其他 @dom 方法,比如:

\\
\val now = Var(new Date)\window.setInterval(1000) { now := new Date }\@dom def render = {\  \u0026lt;div\u0026gt;\    现在时间:{ now.bind.toString }\    { introductionDiv.bind }\    { inlineStyle.bind }\    { typedButton.bind }\    { comment.bind }\    { randomParagraph.bind }\  \u0026lt;/div\u0026gt;\}
\\

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

\\

强类型的 XHTML

\\

Binding.scala 中的 XHTML 都支持静态类型检查。比如:

\\
\@dom def typo = {\  val myDiv = \u0026lt;div typoProperty=\"xx\"\u0026gt;content\u0026lt;/div\u0026gt;\  myDiv.typoMethod()\  myDiv\}
\\

由于以上代码有拼写错误,编译器就会报错:

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

内联 CSS 属性

\\

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

\\
\@dom def invalidInlineStyle = {\  \u0026lt;div style=\"color: blue; typoStyleName: typoStyleValue\"\u0026gt;\u0026lt;/div\u0026gt;\}
\\

以上代码中设置的 typoStyleName 样式名写错了,但编译器并没有报错。

\\

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

\\
\@dom def invalidInlineStyle = {\  \u0026lt;div style:color=\"blue\" style:typoStyleName=\"typoStyleValue\"\u0026gt;\u0026lt;/div\u0026gt;\}
\\

那么编译器就会报错:

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

这样一来,可以在编写代码时就知道属性有没有写对。不像原生 JavaScript / HTML / CSS 那样,遇到 bug 也查不出来。

\\

自定义属性

\\

如果你需要绕开对属性的类型检查,以便为 HTML 元素添加定制数据,你可以属性加上 data: 前缀,比如:

\\
\@dom def myCustomDiv = {\  \u0026lt;div data:customAttributeName=\"attributeValue\"\u0026gt;\u0026lt;/div\u0026gt;\}
\\

这样一来 Scala 编译器就不会报错了。

\\

三、结论

\\

本文的完整 DEMO 请访问 。

\\

从这些示例可以看出,Binding.scala 一方面支持完整的 XHTML ,可以从高保真 HTML 原型无缝移植到动态网页中,开发过程极为顺畅。另一方面,Binding.scala 可以在编译时静态检查 XHTML 中出现语法错误和语义错误,从而避免 bug 。

\\

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

\\

cf52b10f0c66ff8ded67738399809af6.png

\\

我将在下一篇文章中介绍 Binding.scala 如何实现服务器发送请求并在页面显示结果的流程。

\\

四、相关链接

\\
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \

五、More than React 系列文章

\\

\\

\\

\\

\\

\\

作者简介

\\

杨博是 Haxe 和 Scala 社区的活跃贡献者,发起和维护的开源项目包括 、、、、、、 。杨博曾在网易任主程序和项目经理,开发过多款游戏。现在ThoughtWorks任Lead Consultant,为客户提供移动、互联网、大数据、人工智能和深度学习领域的解决方案。

\\

感谢对本文的策划,对本文的审校。

\\

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博(,),微信(微信号:)关注我们。

转载地址:http://qugjx.baihongyu.com/

你可能感兴趣的文章
启锐 588 打印机每次打印都流出一部分,没有重新切换纸张
查看>>
小程序 获取openid unionid
查看>>
redis info详解
查看>>
通过构建一个区块链应用来学习区块链!
查看>>
magento eav model study
查看>>
scrapy 报错:Missing scheme in request url: h
查看>>
21.22 redis集群介绍
查看>>
使用CURL检测Clinet侧发起的HTTP请求各阶段时间
查看>>
Kubernetes源码探疑:Pod IP泄露排查及解决
查看>>
GitHub与gitee使用
查看>>
有助于提高"锁"性能的几点建议
查看>>
Hibernate入门到精通-关系映射一对多
查看>>
将solr发布到Tomcat上
查看>>
解压zip包(zip4j)
查看>>
汽车租赁系统((SSH+MYSQL+JSP))
查看>>
12.17 Nginx负载均衡 12.18 ssl原理 12.19 生成ssl密钥对 12.20 Nginx配置ssl
查看>>
高阶特性
查看>>
MongoDB最简单的入门教程之三 使用Java代码往MongoDB里插入数据
查看>>
如何处理Docker的错误消息request canceled:Docker代理问题
查看>>
react(一)基础环境搭建
查看>>