跳到主要内容

BackboneJS的fetch方法折腾记

Backbone的Model模型和Collection集合都有一个fetch方法,Backbone的API如是解释:collection.fetch([options]),从服务器拉取集合的默认模型,成功接收数据后会重置(reset)集合。可选参数options支持success和error回调函数,回调函数接收(collection,response)作为参数。可以委托Backbone.sync在随后处理个性化需求。处理fetch请求的服务器应当返回模型的JSON 数组。

我的折腾之旅第一步的代码如下,简单定义了一个model和一个collection。collection的url是本地用nodejs写的,直接通过res.send返回一个JSON串。

1234567891011121314151617181920212223242526272829303132window.AppModel=Backbone.Model.extend({ initialize:function(){ console.log('create a model'); this.bind("change:Name",function(){ var name=this.get('Name'); alert('你改变了name属性为:'+name); }); this.bind('error',function(model,error){ alert(error); }) }, defaults:{ Name:'初始值', ID:'初始ID' },})window.AppCollection=Backbone.Collection.extend({ model:AppModel, url:'http://localhost:8880/backbone', initialize:function(){ this.bind('reset',this.showAll); }, showAll:function(){ this.each(function(model){ alert(model.get('Name')+','+model.get('ID')) }) }})$(function(){ var test=new AppCollection(); test.fetch();})

按照这样子,正常情况下应该是能alert出返回JSON串的Name和ID的。但在chrome下,我首先碰到的问题是,不允许跨域。于是我直接忽略了,用FF打开调试。因为之前也遇到过这样的问题,知道原因是chrome的默认设置,本地静态页面不能进行跨域请求。后来证明这一刻的我傻逼了,导致后面的故事发生。接着在FF下,控制台没有显示error,但没有任何反应。控制台显示HTTP码200和OK,但响应为空。我就很纳闷,直接在地址栏输入url的地址,返回结果正常,而且fiddle也显示了这次请求。天真的我这时依然没有醒悟过来,这是JS跨域啊!于是我就查了一下API,将执行部分的代码改成如下

12345678910111213var test=new AppCollection(); test.fetch({url:'http://localhost:8880/backbone', success:function(collection,response){ collection.each(function(data){ alert(data.get('Name')); }) }, error:function(collection, response){ console.log(collection); console.log(response); alert('error'); } })

结果依然是报错,弹出error对话框。于是乎我继续去翻查API文档,增加了一个sync的方法。

123Backbone.sync = function(method, model) { alert(method + ": " + model.url);};

这时候,运行的结果是弹出对话框:read,http://localhost:8880/backbone。然后便没有反应了。通过控制台查看,也没发送get请求到url。于是看下sync的解释。Backbone.sync 是 Backbone 每次向服务器读取或保存模型时都要调用执行的函数。 默认情况下,它使用(jQuery/Zepto).ajax 方法发送 RESTful json 请求。 如果想采用不同的持久化方案,比如 WebSockets, XML, 或 Local Storage,我们可以重载该函数。好了,问题不在这里,因为服务器返回的是标准的JSON格式,所以也没有必要重写fetch方法,于是把这段删去。然后看到了parse方法。遂将代码改成如下

1234567891011121314151617window.AppCollection=Backbone.Collection.extend({ model:AppModel, url:'http://localhost:8880/backbone', initialize:function(){ this.bind('reset',this.showAll); this.bind('add',this.showAll); }, showAll:function(){ this.each(function(model){ alert(model.get('Name')+','+model.get('ID')) }) }, parse:function(data){ console.log(data); alert(data); }})

这时候依然是没有alert出窗口。后来才知道原因,因为此时并没有成功地从服务器拉回数据,所以也就没有可以parse的数据了。parse的作用是每一次调用 fetch 从服务器拉取集合的模型数据时,parse都会被调用。 本函数接收原始 response 对象,返回可以 add 到集合的模型属性数组。 默认实现是无需操作的,只需简单传入服务端返回的JSON对象。 如果需要处理遗留API,或者在返回数据定义自己的命名空间,可以重写本函数。

于是我很纳闷,为啥就是不成功呢。趁着无头绪,跑去洗澡了。在洗澡的时候突然灵光一闪,大叫了一声坑爹!原来自己写的nodejs服务端没有设置允许跨域访问啊!普通的Ajax请求是不允许跨域的啊!于是立马在nodejs里面改了header头,允许请求跨域。就如大家想到的,我的collection终于fetch成功了。通过这次的折腾,也大概搞明白了Backbonejs里的fetch流程,对几个方法也有了大概的了解。也算获益一番啦。最后贴上最终的代码。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758window.AppModel=Backbone.Model.extend({ initialize:function(){ console.log('create a model'); this.bind("change:Name",function(){ var name=this.get('Name'); alert('你改变了name属性为:'+name); }); this.bind('error',function(model,error){ alert(error); }) }, defaults:{ Name:'初始值', ID:'初始ID' },// url:'http://localhost:8880/backbone'*})window.AppCollection=Backbone.Collection.extend({ model:AppModel, url:'http://localhost:8880/backbone', initialize:function(){ this.bind('reset',this.showAll); this.bind('add',this.showAll); }, showAll:function(){ this.each(function(model){ alert(model.get('Name')+','+model.get('ID')) }) }, / parse:function(data){ console.log(data); alert(data); } /})/Backbone.sync = function(method, model) { *alert(method + ": " + model.url);};/$(function(){ var test=new AppCollection();// test.fetch(); test.fetch({url:'http://localhost:8880/backbone', success:function(collection,response){ collection.each(function(book){ alert(book.get('Name')); }) }, error:function(collection, response){ console.log(collection); console.log(response); alert('error'); } })})