如何静态编译HTML?
对HTML的残缺支持以前我们使用其他前端框架,比如Cycle.js 、Widok、ScalaTags时,由于...
对HTML的残缺支持
以前我们使用其他前端框架,比如Cycle.js 、Widok、ScalaTags时,由于框架不支持 HTML语法,前端工程师被迫浪费大量时间,手动把HTML改写成代码,然后慢慢调试。就算是支持HTML语法的框架,比如ReactJS,支持状况也很残缺不全。
比如,在ReactJS中,你不能这样写:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
classBrokenReactComponent extendsReact.Component{
render(){
return(
不支持class属性
不支持style属性
不支持for属性
);
}
}
前端工程师必须手动把
class和
for属性替换成
className和
htmlFor,还要把内联的
style样式从CSS语法改成JSON语法,代码才能运行:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
classWorkaroundReactComponent extendsReact.Component{
render(){
return(
被迫把class改成className
被迫把样式表改成JSON
被迫把for改成htmlFor
);
}
}
这种开发方式下,前端工程师虽然可以把HTML原型复制粘贴到代码中,但还需要大量改造才能实际运行。比Cycle.js、Widok或者ScalaTags省不了太多事。
不兼容原生DOM操作
此外,ReactJS等一些前端框架,会生成虚拟DOM。虚拟DOM无法兼容浏览器原生的DOM API ,导致和jQuery、D3等其他库协作时困难重重。比如ReactJS更新DOM对象时常常会破坏掉jQuery控件。Reddit很多人讨论了这个问题。他们没有办法,只能弃用jQuery。我司的某客户在用了ReactJS后也被迫用ReactJS重写了大量jQeury控件。
Binding.scala中的XHTML
现在有了Binding.scala ,可以在@dom方法中,直接编写XHTML。比如:
JavaScript
1
2
3
4
5
6
7
8
9
@dom def introductionDiv={
Binding.scala的优点
简单
概念少功能多
}
以上代码会被编译,直接创建真实的DOM对象,而没有虚拟DOM。
Binding.scala对浏览器原生DOM的支持很好,你可以在这些DOM对象上调用DOM API,与 D3、jQuery等其他库交互也完全没有问题。
ReactJS对XHTML语法的残缺不全。相比之下,Binding.scala支持完整的XHTML语法,前端工程师可以直接把设计好的HTML原型复制粘贴到代码中,整个网站就可以运行了。
Binding.scala中XHTML的类型
@dom方法中XHTML对象的类型是Node的派生类。
比如, 的类型就是HTMLDivElement,而 的类型就是 HTMLButtonElement。
此外,
@dom注解会修改整个方法的返回值,包装成一个Binding。
JavaScript
1
2
3
@dom def typedButton:Binding[HTMLButtonElement]={
按钮
}
注意
typedButton是个原生的
HTMLButtonElement,所以可以直接对它调用 DOM API。比如:
JavaScript
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中的代码就会执行一次。
其他HTML节点
Binding.scala支持HTML注释:JavaScript
1
2
3
@dom def comment={
}
Binding.scala也支持CDATA块:
JavaScript
1
2
3
4
5
6
7
8
9
10
@dom def inlineStyle={
Binding.scala真好用!
}
内嵌Scala代码
除了可以把XHTML内嵌在Scala代码中的@dom方法中,Binding.scala 还支持用
{ ... }语法把 Scala 代码内嵌到XHTML中。比如:
JavaScript
1
2
3
@dom def randomParagraph={
生成一个随机数:{math.random.toString}
}
XHTML中内嵌的Scala代码可以用
.bind绑定变量或者调用其他
@dom方法,比如:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
val now=Var(newDate)
window.setInterval(1000){now:=newDate}
@dom def render={
现在时间:{now.bind.toString}
{introductionDiv.bind}
{inlineStyle.bind}
{typedButton.bind}
{comment.bind}
{randomParagraph.bind}
}
上述代码渲染出的网页中,时间会动态改变。
强类型的 XHTML
Binding.scala中的XHTML 都支持静态类型检查。比如:JavaScript
1
2
3
4
5
@dom def typo={
val myDiv=content
myDiv.typoMethod()
myDiv
}
由于以上代码有拼写错误,编译器就会报错:
JavaScript
1
2
3
4
5
6
typo.scala:23:value typoProperty isnotamember of org.scalajs.dom.html.Div
val myDiv=content
^
typo.scala:24:value typoMethod isnotamember of org.scalajs.dom.html.Div
myDiv.typoMethod()
^
内联CSS属性
用style属性设置内联样式时,
style的值是个字符串。比如:
JavaScript
1
2
3
@dom def invalidInlineStyle={
}
以上代码中设置的
typoStyleName样式名写错了,但编译器并没有报错。
要想让编译器能检查内联样式,可以用
style:前缀而不用
style属性。比如:
JavaScript
1
2
3
@dom def invalidInlineStyle={
}
那么编译器就会报错:
JavaScript
1
2
3
typo.scala:28:value typoStyleName isnotamember of org.scalajs.dom.raw.CSSStyleDeclaration
^
这样一来,可以在编写代码时就知道属性有没有写对。不像原生JavaScript / HTML / CSS那样,遇到bug也查不出来。
自定义属性
如果你需要绕开对属性的类型检查,以便为HTML元素添加定制数据,你可以属性加上data:前缀,比如:
JavaScript
1
2
3
@dom def myCustomDiv={
}
这样一来Scala编译器就不会报错了。
结论
从这些示例可以看出,Binding.scala 一方面支持完整的XHTML ,可以从高保真HTML 原型无缝移植到动态网页中,开发过程极为顺畅。另一方面,Binding.scala 可以在编译时静态检查XHTML中出现语法错误和语义错误,从而避免bug 。以下表格对比了ReactJS和Binding.scala对HTML语法的支持程度:
ReactJSBinding.scala是否支持HTML语法?残缺支持完整支持是否支持标准的
style属性?不支持,必须改用 JSON 语法支持,既支持标准的
style属性也支持
style:前缀是否支持标准的
class属性?不支持,必须改用
className支持,既支持
class也支持
className是否支持标准的
for属性?不支持,必须改用
htmlFor支持,既支持
for也支持
htmlFor是否支持HTML注释?不支持支持是否兼容原生DOM操作?不兼容兼容是否兼容jQuery?不兼容兼容能否在编译时检查出错误?不能能
专注IT, 专注创新,更多资讯和最新动态,请您关注该微信号!
微信扫一扫
关注 IT专家
微信扫一扫关注公众号