还有一点就是,虽然backbone为我们提供了sync这个模块,前面我说过它不好用,因为它是强restful风格的api形式,这个得完全看项目团队内的现有情况去考虑是否要这么干,而且它要求必须用http 200来表示成功的请求,这对于那些自己去捕获后台异常,然后对http response自定义code的后台服务来说,显然是有问题的。好在backbone的api还不错,即使我们不直接使用那些sync相关的方法,我们也可以通过手工管理请求的方式来管理数据,那么此时backbone为我们起到的作用,就真的只是让代码更漂亮,让DOM操作更简单了。当然更好地办法就是去重写一个backbone的sync模块了。
下一部分说明前面这两张图里面,这些模块之间互相影响的内部机制。
2. 数据驱动及背后的事件机制在没有将数据管理从页面逻辑中分离出来之前,能够导致DOM发生变化的大部分是2种情况:第一,是用户的输入,包括键鼠的操作,比如表单输入,窗口滚动及调整,鼠标点击拖拽等;第二,是页面中js代码执行导致的变化。拿第二种情况来说,在一个有大量表单控件的页面中,如果我们想收集表单数据同步到服务器,就必须找到合适的DOM元素然后获得它们的值,再一并组织好通过异步请求发送至后端;如果我们从服务器获取到了表单的初始数据,需要把这些数据一一回填到表单上的各个控件时,我们就必须一一找到各个数据属性对应的DOM元素然后设置它们的值。表单收集相对而言,还是比较容易,但是表单回填就比较麻烦了,尤其是表单组件很多,且有复杂逻辑的时候。依靠手工的方式一一设置value,显得非常重复而繁琐,还容易出问题。对于列表功能,这个问题也会同样存在,比如某一个更新操作,在选中列表中的一行数据后,更新了该数据的内容,当结束编辑的时候,就显然要在该列表中显示该数据条目的最新状态,如果采取手工的方式更新列表中显示的内容,显然就要找到该数据所关联的DOM元素,然后根据数据属性及其位置一一替换。
当backbone把界面逻辑拆分成Model,View和Collection三个模块之后,由于数据的变化,导致UI变化的逻辑,处理起来就特别容易。我们不用再去一一手工按数据属性找到相应的元素去做替换了,只要View层实例,监听到关联的Model实例或者Collection实例有变化(增删改)的时候,去调用View层实例的render方法,重新将界面渲染一遍即可,而这个render方法因为有模板引擎的帮助,渲染的步骤仅用一句话就能完成:
这种方式就可以看作是数据驱动式的DOM更新方式,相比原来手工的方法,它显然要省事不少。如此以来,我们在编码过程中对DOM操作的工作,就能简化不少。而在数据驱动的背后,依赖backbone提供的Events这个基础模块的功能,Model,View以及Collection三个模块都继承了Events,使得它们的实例都拥有直接进行事件管理的能力,都可以直接使用on once trigger off等方法来注册和移除事件监听,另外backbone为了方便起见,还提供了一种主动式的事件绑定方式,相关的api是listenTo stopListenTo listenToOnce,名字都起得很明了,看到的话,非常好理解。这个Events模块,除了让Model等模块的实例拥有强大的自定义事件管理,同时它还提供了一套内置的事件体系,这套事件体系其实就是前面数据驱动的关键:
注意红框的这些事件,有些是只有Model实例才会触发的,有的是只有Collection实例才会派发的,有的是都会触发的,只要当View层的实例,与之相关联的Model实例和Collection实例触发了这些事件,都可以直接通知View层实例执行相应的事件回调,我们所要做的只需要在View层实例初始化的时候,注册好跟它关联的Model实例或Collection实例的事件监听即可。如:
(关联Model实例时)
(关联Collection实例时)前面这些能够解释为什么当Model或Collection发生变化的时候,为什么能够引起View层的变化。但是还有一个方面没有说清楚,就是由于用户与浏览器交互导致的View层的变化,如何同步到数据。这个方法,backbone给出的实现,其实跟平常使用jquery绑定各类键鼠事件,然后在事件监听里面直接去更新关联的Model或Collection实例没有区别,事实上,它本身也是这么做的。比如todos这个小东西里面的TodoView,用events注册了以下事件:
相应的回调都设置成了View层的实例方法:
结合这两段代码,就能发现backbone的写法,就是直接在事件回调里面,去调用Model实例的方法,好在Model类要用的方法都可以实现封装好,所以在调用的时候并不会很麻烦,如果碰到有表单数据收集的场景,也可以考虑写个简单的方法做批量收集,毕竟只要找到表单元素即可完成这个事情。在另外一个View组件AppView里面,你依然可以找到类似的代码,这里就不再重复了。
不过这种从View层到Model层同步的方式,从我个人的角度,有一个别扭的问题,就是当用户的键鼠操作,已经改变了界面上的内容时,由于这些回调内对Model实例的同步操作,会导致Model实例触发change等事件,然后又会导致View层跟Model的change事件绑定的方法(通常是render方法)被再次调用,也就是说页面内容会做一次无意义的重新渲染。为啥无意义,因为本身用户的键鼠操作就已经改变完了界面内容阿。不过看在DOM操作被简化的份上,这个事情也就算了。但是这个得注意防止事件循环的情况。什么意思呢?就是在render过程中,触发了某些元素的事件,恰巧这些事件,你加了一些不合适的监听,在这个监听里面又做了model层的同步,导致change事件被再次调用,render方法又被触发,某些元素的事件也被触发,然后就事件死循环了。
前面的这些内容,再结合第一部分的那张图,就已经能把backbone的机制说的比较清楚了,最重要的东西无非就是Model,View,Collection对界面的解耦,事件机制,以及模板引擎而已。
3. 细说sync模块的问题