在 Wikipedia 上数据绑定的定义是:
Data binding is the process that establishes a connection between the application UI (User Interface) and business logic. If the settings and notifications are correctly set, the data reflects changes when made. It can also mean that when the UI is changed, the underlying data will reflect that change.
就是说,数据绑定是用来给视图层和业务逻辑层建立连接的,把业务逻辑的数据变化体现到视图的展示上面去,反之亦然。其实质,不妨这样理解,就是将数据对象序列化并传递给视图的过程。
XML 数据绑定是最原始的种类的,比如 WPF 里面的数据绑定:
<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:SDKSample"> <DockPanel.Resources> <c:MyData x:Key="myDataSource"/> </DockPanel.Resources> <DockPanel.DataContext> <Binding Source="{StaticResource myDataSource}"/> </DockPanel.DataContext> <Button Background="{Binding Path=ColorName}" Width="150" Height="30">I am bound to be RED!</Button> </DockPanel>
这种语法无比啰嗦累赘,我不喜欢,虽说很多 DSL 都是这样设计的。JSP 中,JSTL 定义了一系列标签库,不过本质上没有什么区别:
<c:set var="bookId" value="${param.Remove}"/> <jsp:useBean id="bookId" type="java.lang.String" /> <% cart.remove(bookId); %> <sql:query var="books" dataSource="${applicationScope.bookDS}"> select * from PUBLIC.books where id = ? <sql:param value="${bookId}" /> </sql:query>
基于 XML 的标签之后是各种表达式,比如 JSTL 表达式、OGNL 表达式,都有一个 “上下文” 的概念,需要展示的数据从上下文中根据输入的 key 去寻找:
person.{?#this.age>20}
这样的表达式在执行前会被编译引擎编译成最终执行的代码。虽然看起来有些生涩,但是已经比啰嗦的 XML 好多了。
上面这两种形式结合起来,已经是我们最常用的情形了。
在 Grails 里面,提供了基于规约+特殊匹配的绑定形式(说到规约,我在 《MVC 框架的映射和解耦》这篇文章里面提到了基于规约的数据绑定,不需要配置文件,只需要遵守约定):
// binds request parameters to a target object bindData(target, params) // exclude firstName and lastName bindData(target, params, [exclude: ['firstName', 'lastName']]) // only use parameters starting with "author." e.g. author.email bindData(target, params, "author") bindData(target, params, [exclude: ['firstName', 'lastName']], "author") // using inclusive map bindData(target, params, [include: ['firstName', 'lastName']], "author")
还有那种在代码中利用注解搞定的:
class SomeClass { @BindUsing({ obj, source -> source['name']?.toUpperCase() }) String name }
当然,API 调用+代码注解这种方式肯定不如页面模板这样来得直观。
现在,我们不妨把 CSS 选择器也理解成一种将样式数据绑定到 DOM 树的方法:
table tr td.tdxx{ background: red; }
这样理解的话,你也一定会觉得,CSS 选择器在当时出现的那个年代,已经足够先进了,代码非常精简。
如果你用过 AngularJS,你应该对其中数据绑定到 DOM 树的语法记忆犹新:
<li ng-repeat="image in images"> <img ng-src="{{image.thumbnail}}" alt="{{image.description}}"> </li>
上面的代码相当于把数个 for 循环中 img 标签的输出非常优雅地展示出来了。
在很多情况下,我们并不需要把数据和 DOM 的展示分开,DOM 是可以任意扩展的,把数据和展示放在一起,很大程度上会增加可读性,并且便利操作。我们曾经见证过随着软件的发展,写一个 web 应用逐渐解耦的过程,但是现在也有很多人跳出来说,不,太罗嗦了,又是 model 又是 view 又是 viewmodel 的,数据应该最天然地和展示结合起来,不需要额外定义专门的类和对象。
上面是我总结的常见数据绑定种类,以及我的理解,如果你有其它的数据绑定方式,不妨告诉我。:)
PS:JSR-227 作为 Java 尝试定义的标准数据绑定和数据访问设施,最终还是流产了,感兴趣的话可以参阅。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》