引子
在前端开发中,常常有这样的naive的代码,先是在HTML中定义了页面结构:
<div class="order-total">100</div>
在有交互时,用js通过其class来取到节点,然后将数据写进去,如$('.order-total').html(300)。
大约在三年前,这种做法在业界还颇为流行,几乎所有的书里,网上教义,都在鼓吹这种分离JS和HTML的行为,认为这么做就是天经地义。
但是当代码量多了之后,当页面交互复杂了之后,我们的代码常常就成了:
if (exist($('.selector'))) {
// do something
}
整个文件中,充斥着这样或那样的if,因为没人能确定这个节点是否存在。还充斥着另一种东西:
这不是标准的jquery代码
var quantity = $(this).parent().parent().find('.quantity-value');
这种连扔垃圾桶都不配的代码真是写的时候爽,后面维护者遭殃的典型范例,你必须不断地切换js/html来审视其强依赖的结构。
那么问题来了,为什么会写出这样的代码呢?
因为这种代码是最符合直觉的书写方式,一个初级程序员未经任何训练,就能写出这样的代码。它的名字叫:命令式编程。
存在的问题
命令式编程存在的问题就是,写的人在写的时候知道是怎么回事,试图掌控一切,后果是:
- 不知道代码中的一系例选择器对应着什么节点
- 可维护性差,你不敢轻易改动类名或删除节点,也不清楚节点的内容究竟对应着什么数据
- 代码量多
控制反转
上面说道,命令式编程试图控制一切,而解决的办法就是,交出控制权,将控制权反转,即:
不要来找我,我会去找你。
原来的工作流:
用户输入/点击/Ajax请求
数据变化 => order = 100
取节点,将内容设到页面 => $('.order-div').html(order) #JS主动来找DOM#
如果页面已经不需要这个信息了,将.order-div从html中删掉,重复上面的流程,执行到第三步时,JS将抛出异常,因为节点找不到。
控制反转的工作流:
元素监听数据变化<div class='order-dv' ng-bind='order'></div>,即#我会去找你#
用户输入/点击/Ajax请求
数据变化 => order = 100
元素的innerHTML将被自动设上100
如果页面已经不需要这个信息了,将.order-div从html中删掉,重复上面的流程,对程序没有任何影响,因为是节点自己去取数据,而不是JS主动去取节点,而这种方式就做:声明式编程。
小结
双向绑定本质上是一种IoC的思想,通过让:
页面元素声明自己依赖的model
JS逻辑不依赖具体dom
达到了解耦合的目的。