深入解析JavaScript框架Backbone.js中的事件机制(2)

url: function () { var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError(); if (this.isNew()) return base; return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); },

其中的正则式/([^\/])$/是个很巧妙的处理,它解决了url最后是否包含'https://www.jb51.net/'的不确定性。

这个正则匹配的是行末的非/字符,这样,像/resources这样的目标会匹配s,然后replace中使用分组编号$1捕获了s,将s替换为s/,这样就自动加上了缺失的/;而当/resources/这样目标却无法匹配到结果,也就不需要替换了。
Model和Collection的关系
在backbone中,即便一类的模型实例的确是在一个集合里面,也并没有强制要求使用集合类。但是使用集合有一些额外的好处,这些好处包括:

url继承
Model属于Collection后,可以继承Collection的url属性。Collection沿用了underscore90%的集合和数组操作,使得集合操作极其方便:

// Underscore methods that we want to implement on the Collection. // 90% of the core usefulness of Backbone Collections is actually implemented // right here: var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', 'lastIndexOf', 'isEmpty', 'chain', 'sample']; Backbone巧妙的使用下面的代码将这些方法附加到Collection中: // Mix in each Underscore method as a proxy to `Collection#models`. _.each(methods, function (method) { Collection.prototype[method] = function () { var args = slice.call(arguments); //将参数数组转化成真正的数组 args.unshift(this.models); //将Collection真正用来维护集合的数组,作为第一个个参数 return _[method].apply(_, args); //使用apply调用underscore的方法 }; });

自动侦听和转发集合中的Model事件
集合能够自动侦听并转发集合中的元素的事件,还有一些事件集合会做相应的特殊处理,这些事件包括:

destroy 侦听到元素的destroy事件后,会自动将元素从集合中移除,并引发remove事件
change:id 侦听到元素的id属性被change后,自动更新内部对model的引用关系
自动模型构造
利用Collection的fetch,可以加载服务端数据集合,与此同时,可以自动创建相关的Model实例,并调用构造方法

元素重复判断
Collection会根据Model的idAttribute指定的唯一键,来判断元素是否重复,默认情况下唯一键是id,可以重写idAttribute来覆盖。当元素重复的时候,可以选择是丢弃重复元素,还是合并两种元素,默认是丢弃的

模型转化
有时从REST接口得到的数据并不能完全满足界面的处理需求,可以通过Model.parse或者Collection.parse方法,在实例化Backbone对象前,对数据进行预处理。大体上,Model.parse用来对返回的单个对象进行属性的处理,而Collection.parse用来对返回的集合进行处理,通常是过滤掉不必要的数据。例如:

//只挑选type=1的book var Books = Backbone.Collection.extend({ parse:function(models,options){ return _.filter(models , function(model){ return model.type == 1; }) } }) //为Book对象添加url属性,以便渲染 var Book = Backbone.Model.extend({ parse: function(model,options){ return _.extend(model,{ url : 'https://www.jb51.net/books/' + model.id }); } })

通过Collection的fetch,自动实例化的Model,其parse也会被调用。

模型的默认值
Model可以通过设置defaults属性来设置默认值,这很有用。因为,无论是模型还是集合,fetch数据都是异步的,而往往视图的渲染确实很可能在数据到来前就进行了,如果没有默认值的话,一些使用了模板引擎的视图,在渲染的时候可能会出错。例如underscore自带的视图引擎,由于使用with(){}语法,会因为对象缺乏属性而报错。

视图的el
Backbone的视图对象十分简答,对于开发者而言,仅仅关心一个el属性即可。el属性可以通过五种途径给出,优先级从高到低:

实例化View的时候,传递el

在类中声明el

实例化View的时候传入tagName

在类中声明tagName

以上都没有的情况下使用默认的'div'

究竟如何选择,取决于以下几点:

一般而言,如果模块是公用模块,在类中不提供el,而是让外部在实例化的时候传入,这样可以保持公共的View的独立性,不至于依赖已经存在的DOM元素

tagName一般对于自成体系的View有用,比如table中的某行tr,ul中的某个li

有些DOM事件必须在html存在的情况下才能绑定成功,比如blur,对于这种View,只能选择已经存在的html

视图类还有几个属性可以导出,由外部初始化,它们是:

// List of view options to be merged as properties. var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgwyps.html