backbone 介绍
代码例子都摘自这里点我
有些人早已知道这个框架(然而你们却不会用!),有些人不知道这个框架 我在这里都说一下
backbone 是一个很轻量级的框架(其实我觉得这句话真是扯),是的相比较于Angularjs Ember来说 确实很轻量级 但是 backbone 这个框架并不能单独运行,他需要两个帮手 第一个Jquery 第二个 underscore 这两个JS库 呵呵哒!
想了解 Angularjs 点这里
backbone 能干嘛呢?额 可以做单页应用 SPA 听说过吧 它是一个MVC框架 2010年出来的 距今已经有6岁了 算是MV*框架中 元老级别的了。
backbone 基础
这一小节 我们将讲解一下backbone 的 models, views,collections,events,还有 routers
首先 我们来搭建一个开发环境
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="jquery.min.js"></script>
<script src="underscore-min.js"></script>
<script src="backbone-min.js"></script>
<script>
// Your code goes here
</script>
</body>
</html>
脚本什么的 自己放到相应的文件夹中 就这样开发环境就搭建完成了
MODEL
backbone 中的model 就是处理数据的 记住这一条就可以了
下面我们创建一个model
var Todo = Backbone.Model.extend({});
//开始创建自己的model
// 新的model 没有啥数据:
var todo1 = new Todo();
// 输出log: {}
console.log(JSON.stringify(todo1));
// 我们来写一点数据:
var todo2 = new Todo({
title: 'Check the attributes of both model instances in the console.',
completed: true
});
// logs: {"title":"Check the attributes of both model instances in the console.","completed":true}
console.log(JSON.stringify(todo2));
model 的初始化
在backbone 的model中有一个initialize()方法
也就是在model 每次初始化的时候会执行这里面的代码
var Todo = Backbone.Model.extend({
initialize: function(){
console.log('This model has been initialized.');
}
});
var myTodo = new Todo();
// Logs: This model has been initialized.
设置model 的默认值
你可以 给你的 model 设置一个默认值,在你没有给model 赋值的时候 model中都会存放这些默认值
但是要注意的是 如果你包含一个对象作为默认值,他会被所有的实例共享,如果你不想这样可以定义一个函数取代 嘿嘿嘿
var Todo = Backbone.Model.extend({
// Default todo attribute values
defaults: {
title: '',
completed: false
}
});
// Now we can create our concrete instance of the model
// with default values as follows:
var todo1 = new Todo();
// Following logs: {"title":"","completed":false}
console.log(JSON.stringify(todo1));
// Or we could instantiate it with some of the attributes (e.g., with custom title):
var todo2 = new Todo({
title: 'Check attributes of the logged models in the console.'
});
// Following logs: {"title":"Check attributes of the logged models in the console.","completed":false}
console.log(JSON.stringify(todo2));
// Or override all of the default attributes:
var todo3 = new Todo({
title: 'This todo is done, so take no action on this one.',
completed: true
});
// Following logs: {"title":"This todo is done, so take no action on this one.","completed":true}
console.log(JSON.stringify(todo3));
Getters&Setters
顾名思义 取数据 设置数据
Model.get();
从当前model中获取当前的属性
var Todo = Backbone.Model.extend({
// Default todo attribute values
defaults: {
title: '',
completed: false
}
});
var todo1 = new Todo();
console.log(todo1.get('title')); // empty string
console.log(todo1.get('completed')); // false
var todo2 = new Todo({
title: "Retrieved with model's get() method.",
completed: true
});
console.log(todo2.get('title')); // Retrieved with model's get() method.
console.log(todo2.get('completed')); // true
如果你想获得model中所有的 数据属性 有一个toJSON()方法
var Todo = Backbone.Model.extend({
// Default todo attribute values
defaults: {
title: '',
completed: false
}
});
var todo1 = new Todo();
var todo1Attributes = todo1.toJSON();
// Following logs: {"title":"","completed":false}
console.log(todo1Attributes);
var todo2 = new Todo({
title: "Try these examples and check results in console.",
completed: true
});
// logs: {"title":"Try these examples and check results in console.","completed":true}
console.log(todo2.toJSON());
Model.set()
向model设置一个或多个hash属性(attributes)。
var Todo = Backbone.Model.extend({
// Default todo attribute values
defaults: {
title: '',
completed: false
}
});
// Setting the value of attributes via instantiation
var myTodo = new Todo({
title: "Set through instantiation."
});
console.log('Todo title: ' + myTodo.get('title')); // Todo title: Set through instantiation.
console.log('Completed: ' + myTodo.get('completed')); // Completed: false
// Set single attribute value at a time through Model.set():
myTodo.set("title", "Title attribute set through Model.set().");
console.log('Todo title: ' + myTodo.get('title')); // Todo title: Title attribute set through Model.set().
console.log('Completed: ' + myTodo.get('completed')); // Completed: false
// Set map of attributes through Model.set():
myTodo.set({
title: "Both attributes set through Model.set().",
completed: true
});
console.log('Todo title: ' + myTodo.get('title')); // Todo title: Both attributes set through Model.set().
console.log('Completed: ' + myTodo.get('completed')); // Completed: true
如果任何一个属性改变了model的状态,在不传入 {silent: true} 选项参数的情况下,会触发 “change” 事件,更改特定属性的事件也会触发。 可以绑定事件到某个属性,例如:change:title,及 change:content。
var Person = new Backbone.Model();
Person.on("change:name", function() { console.log('Name changed'); });
Person.set({name: 'Andrew'});
// log entry: Name changed
Person.set({name: 'Jeremy'}, {silent: true});
// no log entry
console.log(Person.hasChanged("name"));
// true: change was recorded
console.log(Person.hasChanged(null));
// true: something (anything) has changed
如果你想在model 发生变化的时候侦听这个变化你也可以这么做把侦听的方法放到initialize中去
var Todo = Backbone.Model.extend({
// Default todo attribute values
defaults: {
title: '',
completed: false
},
initialize: function(){
console.log('This model has been initialized.');
this.on('change', function(){
console.log('- Values for this model have changed.');
});
}
});
var myTodo = new Todo();
myTodo.set('title', 'The listener is triggered whenever an attribute value changes.');
console.log('Title has changed: ' + myTodo.get('title'));
myTodo.set('completed', true);
console.log('Completed has changed: ' + myTodo.get('completed'));
myTodo.set({
title: 'Changing more than one attribute at the same time only triggers the listener once.',
completed: true
});
// Above logs:
// This model has been initialized.
// - Values for this model have changed.
// Title has changed: The listener is triggered whenever an attribute value changes.
// - Values for this model have changed.
// Completed has changed: true
// - Values for this model have changed.
model validation
默认情况下validate在save之前调用, 但如果传递了 {validate:true},也可以在set之前调用。
var Person = new Backbone.Model({name: 'Jeremy'});
// Validate the model name
Person.validate = function(attrs) {
if (!attrs.name) {
return 'I need your name';
}
};
// Change the name
Person.set({name: 'Samuel'});
console.log(Person.get('name'));
// 'Samuel'
// Remove the name attribute, force validation
Person.unset('name', {validate: true});
// false
这里顺带说说unset()方法
从内部属性散列表中删除指定属性(attribute)。 如果未设置 silent 选项,会触发 “change” 事件。
看完上面的代码 恩大家有点疑惑 验证完以后 怎么通知我呢
其实有一个invalid事件 当你的验证不通过的时候这个事件会被触发
.save()方法 就不会继续执行
var Todo = Backbone.Model.extend({
defaults: {
completed: false
},
validate: function(attributes){
if(attributes.title === undefined){
return "Remember to set a title for your todo.";
}
},
initialize: function(){
console.log('This model has been initialized.');
this.on("invalid", function(model, error){
console.log(error);
});
}
});
var myTodo = new Todo();
myTodo.set('completed', true, {validate: true}); // logs: Remember to set a title for your todo.
console.log('completed: ' + myTodo.get('completed')); // completed: false
还有一种简易的方法 可以知道验证返回的错误
var emptyTodo = new Todo(null, {validate: true});
console.log(emptyTodo.validationError);
Model 中还有一些其他的方法和属性在这里就不一一介绍了 大家可以去看看API 好像一半的方法还是underscore里面的 呵呵哒
VIEW
backbone中的view和其他框架一样 通过一套模板框架来实现数据的绑定和交互这里我们使用的是 underscore microtemplates
view 中有一个render()方法可以 监听绑定到model 中的 change()事件 这样当modal 改变的时候 view也会改变 就不用刷新整个页面了
创建一个VIEW
创建一个新VIEW其实和创建一个model是差不多的
我们用 Backbone.View来创建
var TodoView = Backbone.View.extend({
tagName: 'li',
// Cache the template function for a single item.
todoTpl: _.template( "An example template" ),
events: {
'dblclick label': 'edit',
'keypress .edit': 'updateOnEnter',
'blur .edit': 'close'
},
initialize: function (options) {
// In Backbone 1.1.0, if you want to access passed options in
// your view, you will need to save them as follows:
this.options = options || {};
},
// Re-render the title of the todo item.
render: function() {
this.$el.html( this.todoTpl( this.model.attributes ) );
this.input = this.$('.edit');
return this;
},
edit: function() {
// executed when todo label is double clicked
},
close: function() {
// executed when todo loses focus
},
updateOnEnter: function( e ) {
// executed on each keypress when in todo edit mode,
// but we'll wait for enter to get in action
}
});
var todoView = new TodoView();
// log reference to a DOM element that corresponds to the view instance
console.log(todoView.el); // logs <li></li>
首先我们来说一说试图中的el 你们看代码的最后一行
所有的视图都拥有一个 DOM 元素(el 属性),即使该元素仍未插入页面中去。 视图可以在任何时候渲染,然后一次性插入 DOM 中去,这样能尽量减少 reflows 和 repaints 从而获得高性能的 UI 渲染。 this.el 可以从视图的 tagName, className, id 和 attributes 创建,如果都未指定,el 会是一个空 div。
上面的代码 tagName 设置的是 li 所以最后创建一个 li 元素 下面我们再来创建一个
var TodosView = Backbone.View.extend({
tagName: 'ul', // required, but defaults to 'div' if not set
className: 'container', // optional, you can assign multiple classes to
// this property like so: 'container homepage'
id: 'todos' // optional
});
var todosView = new TodosView();
console.log(todosView.el); // logs <ul id="todos" class="container"></ul>
我们创建了一个 ul 元素 class 是 container id 是 todos
但是上面的代码其实并没有插入到DOM中 也就是 我们在浏览器中是看不到的哦
如果某个元素是存在的 可以用el 向用jquery 那样匹配它意思就是 这样
el: '#footer'
有时候 我们需要 jquery 的 一些内置的 方法 比如什么 append()啊 之类的backbone 给我们提供了一个 elview. el 等同于 (view.el)view. (selector) 等同于 $(view.el).find(selector)
setElement
如果你想应用一个Backbone视图到不同的DOM元素, 使用setElement, 这也将创造缓存$el引用,视图的委托事件从旧元素移动到新元素上。
// We create two DOM elements representing buttons
// which could easily be containers or something else
var button1 = $('<button></button>');
var button2 = $('<button></button>');
// Define a new view
var View = Backbone.View.extend({
events: {
click: function(e) {
console.log(view.el === e.target);
}
}
});
// Create a new instance of the view, applying it
// to button1
var view = new View({el: button1});
// Apply the view to button2 using setElement
view.setElement(button2);
button1.trigger('click');
button2.trigger('click'); // returns true
了解 render()
render()实际上是一个配置方法 通过这些配置来渲染你的模板
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
</head>
<body>
<div id="todo">
</div>
<script type="text/template" id="item-template">
<div>
<input id="todo_complete" type="checkbox" <%= completed ? 'checked="checked"' : '' %>>
<%= title %>
</div>
</script>
<script src="underscore-min.js"></script>
<script src="backbone-min.js"></script>
<script src="jquery-min.js"></script>
<script src="example.js"></script>
</body>
</html>
.template是 underscore 编译渲染模板的方法在上面的例子中 我们传入ID为 item-template 给 .template()方法
render 默认实现是没有操作的。 重载本函数可以实现从模型数据渲染视图模板,并可用新的 HTML 更新 this.el。 推荐的做法是在 render 函数的末尾 return this 以开启链式调用。
其实这么说大家应该还是不明白 ,其实我也不明白 但是还是继续等会儿我们再来说一下这个问题
VIEW事件
大家都知道onclick 这些操作事件吧 在backbone中是这样的
// A sample view
var TodoView = Backbone.View.extend({
tagName: 'li',
// with an events hash containing DOM events
// specific to an item:
events: {
'click .toggle': 'toggleCompleted',
'dblclick label': 'edit',
'keypress .edit': 'updateOnEnter',
'click .destroy': 'clear',
'blur .edit': 'close'
},
格式就是这样 ‘eventName selector’: ‘callbackFunction’
简单明了,然后 绑定了事件有了回调 事件执行完成以后我们要更新视图啊 所有可以这样
var TodoView = Backbone.View.extend({
initialize: function() {
this.model.bind('change', _.bind(this.render, this));
this.model.on('change:a', this.renderA, this);
}
});
上面两种方法都可以 不过第一个是老版本的方法 还要借助underscore中的方法
Collection
你可以理解为 model 的集合
var Todo = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var TodosCollection = Backbone.Collection.extend({
model: Todo
});
var myTodo = new Todo({title:'Read the whole book', id: 2});
// pass array of models on collection instantiation
var todos = new TodosCollection([myTodo]);
console.log("Collection size: " + todos.length); // Collection size: 1
我们可以使用add(), remove()方法对集合中的模型 进行添加和删除
var Todo = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var TodosCollection = Backbone.Collection.extend({
model: Todo
});
var a = new Todo({ title: 'Go to Jamaica.'}),
b = new Todo({ title: 'Go to China.'}),
c = new Todo({ title: 'Go to Disneyland.'});
var todos = new TodosCollection([a,b]);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 2
todos.add(c);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 3
todos.remove([a,b]);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 1
todos.remove(c);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 0
如果您要添加 集合中已经存在的模型 到集合,他们会被忽略, 除非你传递{merge: true}, 在这种情况下,它们的属性将被合并到相应的模型中.
var items = new Backbone.Collection;
items.add([{ id : 1, name: "Dog" , age: 3}, { id : 2, name: "cat" , age: 2}]);
items.add([{ id : 1, name: "Bear" }], {merge: true });
items.add([{ id : 2, name: "lion" }]); // merge: false
console.log(JSON.stringify(items.toJSON()));
// [{"id":1,"name":"Bear","age":3},{"id":2,"name":"cat","age":2}]
我们可以使用clllection.get()方法获取集合中的model 只要传个ID就可以了 这个ID可以是你自己定义的ID 也可以是 backbone自动生成的ID
var myTodo = new Todo({title:'Read the whole book', id: 2});
// pass array of models on collection instantiation
var todos = new TodosCollection([myTodo]);
var todo2 = todos.get(2);
// Models, as objects, are passed by reference
console.log(todo2 === myTodo); // true
这里使用的CID
// extends the previous example
var todoCid = todos.get(todo2.cid);
// As mentioned in previous example,
// models are passed by reference
console.log(todoCid === myTodo); // true
当我们修改了collection中的model时 我们也可以侦听这些状态方式和view中的一样
var TodosCollection = new Backbone.Collection();
TodosCollection.on("add", function(todo) {
console.log("I should " + todo.get("title") + ". Have I done it before? " + (todo.get("completed") ? 'Yeah!': 'No.' ));
});
TodosCollection.add([
{ title: 'go to Jamaica', completed: false },
{ title: 'go to China', completed: false },
{ title: 'go to Disneyland', completed: true }
]);
// The above logs:
// I should go to Jamaica. Have I done it before? No.
// I should go to China. Have I done it before? No.
// I should go to Disneyland. Have I done it before? Yeah!
var TodosCollection = new Backbone.Collection();
// log a message if a model in the collection changes
TodosCollection.on("change:title", function(model) {
console.log("Changed my mind! I should " + model.get('title'));
});
TodosCollection.add([
{ title: 'go to Jamaica.', completed: false, id: 3 },
]);
var myTodo = TodosCollection.get(3);
myTodo.set('title', 'go fishing');
// Logs: Changed my mind! I should go fishing
我们也可以使用 set() reset()方法 设置或者重置 collection中的值
var TodosCollection = new Backbone.Collection();
TodosCollection.add([
{ id: 1, title: 'go to Jamaica.', completed: false },
{ id: 2, title: 'go to China.', completed: false },
{ id: 3, title: 'go to Disneyland.', completed: true }
]);
// we can listen for add/change/remove events
TodosCollection.on("add", function(model) {
console.log("Added " + model.get('title'));
});
TodosCollection.on("remove", function(model) {
console.log("Removed " + model.get('title'));
});
TodosCollection.on("change:completed", function(model) {
console.log("Completed " + model.get('title'));
});
TodosCollection.set([
{ id: 1, title: 'go to Jamaica.', completed: true },
{ id: 2, title: 'go to China.', completed: false },
{ id: 4, title: 'go to Disney World.', completed: false }
]);
// Above logs:
// Completed go to Jamaica.
// Removed go to Disneyland.
// Added go to Disney World.
var TodosCollection = new Backbone.Collection();
// we can listen for reset events
TodosCollection.on("reset", function() {
console.log("Collection reset.");
});
TodosCollection.add([
{ title: 'go to Jamaica.', completed: false },
{ title: 'go to China.', completed: false },
{ title: 'go to Disneyland.', completed: true }
]);
console.log('Collection size: ' + TodosCollection.length); // Collection size: 3
TodosCollection.reset([
{ title: 'go to Cuba.', completed: false }
]);
// Above logs 'Collection reset.'
console.log('Collection size: ' + TodosCollection.length); // Collection size: 1
由于collection 是个集合自然也集成了许多underscore对集合的操作方法
例如map each等等 这里不介绍了大家可以看underscore的文档
点这里看文档
Backbone与服务器的交互
第一个 Collection.fetch()
从服务器拉取集合的默认模型设置 ,成功接收数据后会setting(设置)集合。 options 支持 success 和 error 回调函数,两个回调函数接收 (collection, response, options)作为参数。当模型数据从服务器返回时, 它使用 set来(智能的)合并所获取到的模型, 除非你传递了 {reset: true}, 在这种情况下,集合将(有效地)重置。 可以委托Backbone.sync 在幕后自定义持久性策略 并返回一个jqXHR。 fetch请求的服务器处理器应该返回模型JSON数组。
var Todo = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var TodosCollection = Backbone.Collection.extend({
model: Todo,
url: '/todos'
});
var todos = new TodosCollection();
todos.fetch(); // sends HTTP GET to /todos
第二个向服务器发送数据 collections.create() 与 save()方法不同的是
它方便的在集合中创建一个模型的新实例。 相当于使用属性哈希(键值对象)实例化一个模型, 然后将该模型保存到服务器, 创建成功后将模型添加到集合中。 返回这个新模型。 如果客户端验证失败,该模型将不会被保存, 与验证错误。 为了能正常运行,需要在集合中设置 model 属性。 create 方法接收键值对象或者已经存在尚未保存的模型对象作为参数。
创建一个模型将立即触发集合上的”add”事件, 一个”request”的事件作为新的模型被发送到服务器, 还有一个 “sync” ”事件,一旦服务器响应成功创建模型。 如果你想在集合中添加这个模型前等待服务器响应,请传递{wait: true}。
var Todo = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var TodosCollection = Backbone.Collection.extend({
model: Todo,
url: '/todos'
});
var todos = new TodosCollection();
todos.fetch();
var todo2 = todos.get(2);
todo2.set('title', 'go fishing');
todo2.save(); // sends HTTP PUT to /todos/2
todos.create({title: 'Try out code samples'}); // sends HTTP POST to /todos and adds to collection
我们也可以通过 collection.destory()从服务器删除数据
var Todo = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var TodosCollection = Backbone.Collection.extend({
model: Todo,
url: '/todos'
});
var todos = new TodosCollection();
todos.fetch();
var todo2 = todos.get(2);
todo2.destroy(); // sends HTTP DELETE to /todos/2 and removes from collection