Backbone基础

  • Backbone官网:https://backbonejs.org/
  • Underscore.js中文网:https://underscorejs.net/
  • 插件下载地址:https://github.com/requirejs/requirejs/wiki/Plugins
  • 下面的例子,依次默认引入jquery.js、underscore.js、backbone.js文件,不赘述。

一、模型

(1) 初始化

创建一个新模型实例的时候,initialize()方法被调用。该方法可选。

var Todo = Backbone.model.extend({
	initialize: function(){
		console.log('模型被初始化')
	}
});

var myTodo = new Todo(); //模型被初始化

(2) 默认值

通过使用模型里的defaults 属性来设置默认值。

 var Todo = Backbone.Model.extend({
 defaults: {
        title: '',
        completed: false
    }
});

var todo1 = new Todo();
console.log(JSON.stringify(todo1)); //{completed: false, title: ''}

var todo2 = new Todo({
    title: '这是标题',
    tel: '#box'
});
console.log(JSON.stringify(todo2));// {title: '这是标题', tel: '#box', completed: false}

(3) 模型上的方法

1. Model.get() 获取属性值

通过模型的属性名获取属性值。

var Todo = Backbone.Model.extend({
  defaults: {
        title: '这是标题',
        completed: false
    }
});

var todo1 = new Todo();
console.log(todo1.get('title')); //这是标题

2. Model.toJSON() 获取模型的所有数据属性

获取模型的所有数据属性。该方法将所有属性的副本作为一个对象返回。

var Todo = Backbone.Model.extend({
    defaults: {
        title: '这是标题',
        completed: false
    }
});

var newProperty = todo1.toJSON();
console.log(newProperty); //{title: '这是标题', completed: false}
newProperty.title = '标题二';  //修改newPeoperty中的title
console.log(newProperty); //{title: '标题二', completed: false}
console.log(todo1.toJSON()); //{title: '这是标题', completed: false}

3. Model.set() 修改/创建模型属性

  • 修改模型的一个或多个属性的值。如果该属性不存在,则会自动创建出该属性。

  • 当模型的任意属性被更改是,会触发change事件。

  • 可以单独给某个属性绑定change事件(如:change:name)

  • Model.set({属性名: 属性值}, {silent:true}),将不会触发change事件

  • Model.hasChanged([attribute]) 方法来检查某一属性自上次change事件之后是否被更改过。

  • Model.changedAttributes([attributes])方法获得包含所有变化了的属性。

  • Model.previous(attribute) 方法获得属性变化前的值。

  • Model.previousAttributes()方法获得包含所有变化前的属性的一个散列。

    var Todo = Backbone.Model.extend({
       asd: '这是asd',
        defaults: {
            title: '这是标题',
            completed: false
        }
    });
    
    var todo1 = new Todo();
    todo1.set('title', '标题二'); //修改一个属性
    console.log(todo1.toJSON()); //{title: '标题二', completed: false}
    console.log(todo1.changedAttributes()); //{title: '标题二'}
    
    todo1.set({  //修改多个属性
        'title': '标题三',
        'completed': true
    });
    console.log(todo1.toJSON()); //{title: '标题三', completed: true}
    

4. Model.unset()删除一个属性

var Todo = Backbone.Model.extend({
  defaults: {
       title: '这是标题',
       completed: false,
   }
});
var todo1 = new Todo();
todo1.unset('title');
console.log(JSON.stringify(todo1)); //{"completed":false}

5. Model.clear()删除模型中的所有属性

var Todo = Backbone.Model.extend({
  defaults: {
       title: '这是标题',
       completed: false,
   }
});
var todo1 = new Todo();
todo1.clear();
console.log(JSON.stringify(todo1)); //{}

6. Model.has()检查模型中是否存在某一属性

如果模型中存在该属性则返回true,否则返回false。

var Todo = Backbone.Model.extend({
  defaults: {
       title: '这是标题',
       completed: false,
   }
});
var todo1 = new Todo();
console.log(todo1.has('title'));// true
console.log(todo1.has('title123')); //false

7. Model.escape() 将HTML转义

var Todo = Backbone.Model.extend({
    defaults: {
       html: '<div class="box">html元素</div>'
    }
});

var todo1 = new Todo();
console.log(todo1.escape('html'));// &lt;div class=&quot;box&quot;&gt;html元素&lt;/div&gt;

8. Model.clone() 模型对象深拷贝

 var Todo = Backbone.Model.extend({
    defaults: {
        id: null,
        title: '',
    }
});

var todo1 = new Todo({ id: 1, title: '这是todo1的标题' });
var todo2 = new Todo({ id: 2, title: '这是todo2的标题' });

var newTodo1 = todo1;  //浅拷贝
newTodo1.set('title', '修改newTodo1的title');
console.log(todo1.toJSON()); //{id: 1, title: '修改newTodo1的title'}

var cloneTodo = todo2.clone();//深拷贝
cloneTodo.set('title', '修改cloneTodo的title');
console.log(todo2.toJSON()); //{id: 2, title: '这是todo2的标题'}

4.destroy

(4) 监听模型的变化

通过在initialize()函数中添加change事件来监听模型的变化。也可以监听单个属性的变化。

var Todo = Backbone.Model.extend({
    initialize: function () {
        //当任意一个属性变化,都会触发该事件
        this.on('change', function () {
            console.log('change事件被触发');
        });
        
        //当completed被修改时触发该事件
        this.on('change:completed', function(model, newVal){
            console.log(arguments);
            console.log('completed发生了变化');
            console.log('新值为' + newVal);
        })
    },
    defaults: {
        title: '这是标题',
        completed: false
    }
});

var todo1 = new Todo();

todo1.set('title', '标题二');//change事件被触发
todo1.set('completed', true);//触发change事件的同时,也触发completed绑定的change事件

var todo2 = new Todo({
    completed: true  //只触发了change事件
});

(5) .attributes属性

  • .attributes属性描述了包含该模型状态的一个hash散列。

  • 如果通过.attributes属性设置值,则可以绕过该模型上绑定的触发器。

  • 尽可能使用Model.set()来修改属性值。

var Todo = Backbone.Model.extend({
    initialize: function () {
        this.on('change', function () {
            console.log('change事件被触发');
        });
    },
    defaults: {
        title: '这是标题',
        completed: false
    }
});

var todo1 = new Todo();

todo1.attributes.completed = true;//不会触发绑定的change事件
console.log(todo1.toJSON()); //{title: '这是标题', completed: true}
console.log(todo1.hasChanged()); //false

todo1.set('title', '标题二');//会触发绑定的change事件
console.log(todo1.hasChanged());//true

(6) 验证

1.基础

  • 通过model.validate()进行模型验证。

  • 默认情况下,通过调用save()方法或者带有{validate: true}参数的set()方法持久化模型时,验证就会触发。

  • 验证通过时,不会返回任何值;验证不通过时,会返回一个错误值,不会修改数据。

  • 传递给validate函数的attributes对象表示的是在执行set()或者save()以后该模型的结果,该对象有别于model当前的属性及模型操作时所传递的参数。

var Todo = Backbone.Model.extend({
   initialize: function () {
       //属性验证不通过时触发invalid函数
       this.on('invalid', function (model, error) {
           console.log(error);
       })
   },
   defaults: {
       title: '这是标题',
       completed: false
   },
   validate: function (attrs) {
       //标题必须为字符串
       if (typeof (attrs.title) !== 'string') {
           return 'title 必须为字符串';
       }

       //completed必须为布尔值
       if (typeof (attrs.completed) !== 'boolean') {
           return 'completed 必须为布尔值';
       }
   },
});

var todo1 = new Todo();

//校验不通过时返回false
if (!todo1.set({ title: 123 }, { validate: true })) {
   console.log('修改失败');
}

todo1.set('completed', 456, { validate: true });
console.log(todo1.toJSON()); //title: '这是标题', completed: false}

2.手动触发验证

  • 如果希望手动检查一个模型是否合法,可以调用Model.isValid()方法,该方法如果验证成功返回true,否则返回false。

     var Todo = Backbone.Model.extend({
       initialize: function () {
            this.on('invalid', function (model, error) {
                console.log(error);
            })
        },
        defaults: {
            title: '这是标题',
            completed: false,
        },
        validate: function (attrs) {
            if (typeof (attrs.title) !== 'string') {
                return 'title 必须为字符串';
            }
        },
    });
    
    var todo1 = new Todo({ title: 123 });
    console.log(todo1.isValid()); //false
    

(7) idAttribute 和 cid

  • idAttribute告诉Backbone服务器上的哪个数据字段用于填充id属性。默认情况下,它假定为id,但是可以根据需要进行自定义。
  • 模型有一个cid(客户端ID),在模型创建的时候由Backbone自动生成。

二、模型集合

  • 集合是模型的组合,可以通过扩展Backbone.Collection来创建集合。

  • 通常情况下,创建集合的时候要定义一个属性,指定该集合所包含的模型(model)类型,同时还包含任何所需的实例属性。

  • 在模型被存储在一个名为models的数组中。

    var Todo = Backbone.Model.extend({
    	defaults: {
    		title:'',
    		completed: false
    	}
    })
    var TodosCollection = Backbone.Collection.extend({
    	model: Todo //指定该集合所包含的模型类型
    });
    
    var myTodo = new Todo({title:'这是标题', id: 2});
    var todos = new TodosCollection([myTodo]); //将模型实例传给模型集合
    console.log(todos.length); //模型集合的长度为1
    

(1) 模型集合的方法

1. add()添加一个模型

  • 使用add()添加 集合中已经存在(以模型中的idAttribute属性设置的值,来判断数据是否已经存在。)的模型到集合,他们会被忽略, 除非你传递{merge: true}

  • 该方法会触发add事件。

  • add()可以传入{at: index}参数,在指定的位置上添加模型。

    var Todo = Backbone.Model.extend({
       // idAttribute: 'id',
        defaults: {
            id: null,
            title: '',
            completed: false
        }
    });
    
    var TodosCollection = Backbone.Collection.extend({
        model: Todo,
        initialize: function () {
            this.on('add', function () {
                console.log('触发add事件');  //触发了四次
            })
        }
    });
    
    var todos = new TodosCollection();
    
    var todo1 = new Todo({ id: 1, code: '001', title: '这是todo1的标题' });
    var todo2 = new Todo({ id: 2, code: '002', title: '这是todo2的标题' });
    var todo3 = new Todo();
    todo3.set({ id: 3 });
    todo3.set({ title: '这是todo3的标题' });
    todo3.set({ code: '003' });
    
    todos.add(todo1);//添加单个模型
    todos.add([todo2, todo3]);//添加多个模型
    
    todos.add({ id: 3, title: '不会覆盖todo3标题' });
    console.log(todos.models.at(2).toJSON()); //{id: 3, title: '这是todo3的标题', completed: false}
    
    todos.add({ id: 3, title: '会覆盖todo3标题' }, { merge: true });
    console.log(todos.models.at(2).toJSON()); //{id: 3, title: '会覆盖todo3标题', completed: false}
    
    var todo4 = new Todo({ id: 4, code: '004', title: '这是todo4的标题' });
    todos.add(todo4, { at: 0 }); //将todo4添加到位置0
    console.log(todos.models); //[todo4, todo1, todo2, todo3]
    

2. remove()移除一个模型

  • remove()方法可以传入模型型的id、cid或模型对象本身作为参数。
  • 会触发 remove 事件
 var Todo = Backbone.Model.extend({
    idAttribute: 'code', //设置模型的唯一标识符,默认为id
    defaults: {
        id: null,
        title: '',
        completed: false
    }
});

var TodosCollection = Backbone.Collection.extend({
    model: Todo,
    initialize: function () {
        this.on('remove', function () {
            console.log('触发remove事件');
        })
    }
});

var todos = new TodosCollection();

var todo1 = new Todo({ id: 1, code: '001', title: '这是todo1的标题' });
var todo2 = new Todo({ id: 2, code: '002', title: '这是todo2的标题' });
todos.add([todo1, todo2]);
todos.remove('001'); //删除code 为 001的模型
console.log(todos.at(0).get('id')); //2
todos.remove(todo2); //删除todo2
console.log(todos.length); //0

3. at() 使用索引从集合中获取模型

  • Collection.at(索引) 从集合中获取处于特定索引位置的模型。

  • 第一个元素的索引下标为0.

    var Todo = Backbone.Model.extend({
        defaults: {
            id: null,
            title: '',
        }
    });
    
    var TodosCollection = Backbone.Collection.extend({
        model: Todo,
    });
    
    
    var todo1 = new Todo({ id: 1, title: '这是todo1的标题' });
    var todo2 = new Todo({ id: 2, title: '这是todo2的标题' });
    
    var todos = new TodosCollection([
        todo1,
        todo2
    ]);
    console.log(todos.at(1).get('title')); //这是todo2的标题
    

4. indexOf() 获取模型在集合中的索引

var Todo = Backbone.Model.extend({
    defaults: {
        id: null,
        title: '',
    }
});

var TodosCollection = Backbone.Collection.extend({
    model: Todo,
});


var todo1 = new Todo({ id: 1, title: '这是todo1的标题' });
var todo2 = new Todo({ id: 2, title: '这是todo2的标题' });

var todos = new TodosCollection([
    todo1,
    todo2
]);

console.log(todos.indexOf(todo2)); //1

5. length() 获取集合的长度

todos.length();

6. set()

  • Collection.set(models, [options])接收一个模型数据参数,如果列表中的一个模型尚不在集合中,那么它将被添加; 如果模型已经在集合中,其属性将被合并; 并且如果集合中的模型不存在于列表中,将被删除。

  • set()方法将触发相应的"add", “remove”, 和 "change"事件。

  • 如果您想自定义的行为, 你可以设置选项:{add: false}, {remove: false}, 或 {merge: false},将不会执行相关。

    var TodosCollection = Backbone.Collection.extend({
      initialize: function(){
       this.on('add', function(model){
         console.log('title为“'+ model.get('title') + '”被添加');
       })
      }
    });
    
    var todoList = new TodosCollection();
    
    //添加监听remove事件,和添加的监听add事件只是添加的方式不一样而已
    todoList.on('remove', function(model){
     console.log('title为“'+ model.get('title') + '”被删除');
    });
    
    todoList.on('change:completed', function(model){
     console.log('title为“'+ model.get('title') + '”的completed属性被改变');
    });
    
    todoList.add([  
      {id: 1, title: '标题1', completed: false}, //触发add事件
      {id: 2, title: '标题2', completed: false}, //触发add事件
      {id: 3, title: '标题3', completed: true}, //触发add事件
    ]);
    
    
    todoList.set([  //id为2的model被删除,触发remove事件
     {id: 1, title: '标题1', completed: true}, //触发change:completed事件
     {id: 3, title: '标题3', completed: true}, 
     {id: 4, title: '标题4', completed: false}, //不触发add事件,该项也不会添加到集合中
    ], {add: false});
    
    console.log(todoList.length); //2
    

7. reset() 替换集合中的全部模型

  • 使用 Collection.reset(),会先把集合中的所有模型都删除,然后再为它添加其他的模型。最后触发一个单独的"reset"事件。

  • 不参数的.reset()可以清空集合。

  • 监听reset事件时可以从options.previousModels访问reset之前的模型列表。

  • Collection.reset()不会触发任何add和remove事件。

    //注意:TodosCollection 是Colection的实例
    var TodosCollection = new Backbone.Collection();
    
    TodosCollection.on('reset', function (model, options) {
      console.log(model);
      console.log(options);
      console.log('reset事件被触发');
    });
    
    TodosCollection.add([  
     {id: 1, title: '标题1', completed: false},
     {id: 2, title: '标题2', completed: false}
    ]);
    
    TodosCollection.reset([{id: 3, title: '标题3', completed: true}, ]);
    
    console.log(TodosCollection); //1
    

8. get() 从集合中获取模型

var Todo = Backbone.Model.extend({
    idAttribute: 'code',  //设置模型的唯一标识,默认为id,这里修改为code
    defaults: {
        id: null,
        code: '',
        title: '',
    }
});

var TodosCollection = Backbone.Collection.extend({
    model: Todo,
});


var todo1 = new Todo({ id: 1, code: '001', title: '这是todo1的标题' });
var todo2 = new Todo({ id: 2, code: '002', title: '这是todo2的标题' });

var todos = new TodosCollection([
    todo1,
    todo2
]);

//通过模型的唯一标识获取
console.log(todos.get('001').toJSON()); //{id: 1, code: '001', title: '这是todo1的标题'}.
//当一个模型被创造出来时,默认会有一个唯一表示 cid 
console.log(todos.get(todo2.cid).toJSON()); //{id: 2, code: '002', title: '这是todo2的标题'}

9. 以栈或队列的方式使用集合

  • 调用push()方法来把模型添加到集合的末端。

    todos.push(todo5);
    
  • 调用pop()方法来从集合中移除它的最后一个模型,并将其返回。

    var todo5 = todos.pop();
    
  • 调用unshift()方法把一个模型添加到集合的前端。

    todos.unshift(todo5);
    
  • 调用shift()方法来从集合中移除它的第一个模型,并将其返回

    var todo5 = todos.shift();
    

9. where()对集合中的模型进行过滤

  • where() 方法接受一个搜索条件作为参数,返回一个满足条件的模型的数组。
   
  var todos = new Backbone.Collection();

  todos.add([  
    {id: 1, title: '标题1', completed: false, className: 'box'},
    {id: 2, title: '标题2', completed: false, className: 'box2'}, 
    {id: 3, title: '标题3', completed: true, className: 'box'}, 
  ]);
  var results = todos.where({completed:false});
  console.log(results);
  var results2 = todos.where({completed:false, className: 'box'});
  console.log(results2);

(3) 事件监听

1. add和remove事件

从集合中添加或移除模型的时候会触发这些事件。

var Todo = Backbone.Model.extend({
     defaults: {
         id: null,
         title: '',
         completed: false
     }
 });

 var TodosCollection = Backbone.Collection.extend({
     model: Todo,
     initialize: function () {
         this.on('remove', function () {
             console.log('集合的remove事件被触发');
         })
     }
 });

 var todo1 = new Todo({ id: 1, title: '这是title' });
 var todos = new TodosCollection();
 todos.on('add', function () { //也可以放在集合的initialize中
     console.log('集合的add事件被触发');
 });

 todos.add(todo1); //触发集合上的add事件
 todos.remove(todo1); //触发集合上的remove事件

2. change事件

在集合中任意模型的属性上,都可以绑定change事件,用于监听这些模型属性的变化。

 var Todo = Backbone.Model.extend({
    defaults: {
        id: null,
        title: '',
        completed: false
    }
});

var TodosCollection = Backbone.Collection.extend({
    model: Todo,
});

var todo1 = new Todo({ id: 1, title: '这是title' });
var todos = new TodosCollection();
todos.on('change:title', function () {  //也可以放在集合initialize中
    console.log('模型中的title被修改,触发集合中的change事件');
});

todos.add(todo1); //触发集合上的add事件

todo1.set('title', '修改title');

3. once事件

用法跟on很像,区别在于绑定的回调函数触发一次后就会被移除(只执行一次)。

var Todo = Backbone.Model.extend({
   defaults: {
       id: null,
       title: '',
       completed: false
   }
});

var TodosCollection = Backbone.Collection.extend({
    model: Todo,
    initialize: function () {
        this.once('add', function () {
            console.log('once中的add被触发'); //只触发一次
        })
    }
});

var todos = new TodosCollection();
var todo1 = new Todo({ id: 1, title: '这是todo1的标题' });
var todo2 = new Todo({ id: 2, title: '这是todo2的标题' });

todos.add(todo1);
todos.add(todo2);

三、REST及存储

表述性状态转移(REST)是一种用来设计网络应用程序间通信的架构风格。可以简单地直通托单纯的HTTP协议来实现。
例子采用PHP作为后台:

<?php
header('Content-Type:application/json; charset=utf-8');
$arr1 = array("id" => 1, "title" => "数据1", "completed" => false);
$arr2 = array("id" => 2, "title" => "数据2", "completed" => false);
$arr3 = array("id" => 3, "title" => "数据3", "completed" => false);

echo json_encode(array($arr1, $arr2, $arr3));

(1). fetch()从服务器获取数据

  • Collection.fetch()方法的功能是从服务器获取数据,常用于数据模型初始化或数据恢复。

  • Collection.fetch()通过发送HTTP GET请求到URL上(集合的url属性或url函数),从服务器获取JSON数组形式的模型数据集。一旦数据接收,Backbone将执行set()函数来更新集合。

  • 运行fetch() 方法的时候会触发read事件;当fetch()方法成功结束时会触发sync事件。

  • fetch()中可以传入sucess和error两个回调函数作为参数。当一个同步操作成功或失败的时候,会以异步的方式分别调用相应的回调函数。

     var Todo = Backbone.Model.extend({
       defaults: {
            title: '',
            completed: false
        }
    });
    
    var TodosCollection = Backbone.Collection.extend({
        model: Todo,
        //如果需要带参数,就要将url改为方法,不需要带参数可以直接为字符串
        url: function () {
            if (_.isUndefined(this.id)) {
                return 'http://localhost/work/backbone2/data.php';
            } else {
                return 'http://localhost/work/backbone2/data.php?id=' + this.id
            }
        }
    });
    
    //模型集合发送GET请求并携带id参数
    var collection = new TodosCollection();
    collection.id = 123; 
    
    //获取的数据自动通过set进行数据的更新
    collection.fetch({
        success: function (collection, response, options) {
            console.log('集合请求发送成功,返回的数据:');
            console.dir(response);
        },
        error: function (collection, response, options) {
            console.log('集合请求失败,返回的数据:' + response);
        },
    });
    

(2). save([attributes], [options])保存模型到服务器

  • Backbone更新模型是通过单独调用Model.save()方法来实现的。

  • 如果模型是从服务器上获取而来,则Backbone通过在集合的URL上附件一个id来构建一个新的URL,然后发送HTTP PUT请求到服务器上;如果模型是在浏览器上创建的新实例(没有id),则HTTP POST请求会发送到集合的URL上。Collections.create() 可以用于创建一个新模型,并将其添加到集合里,然后通过一个单独的方法调用,再将其发送到服务器上。

  • 默认情况下,Model.save()方法会以异步的方式工作,因此我们必须要在success或者error这两个回调函数中处理返回的结果。但是如果我们需要同步的方式来运行save()方法,可以为选项添加一个{wait: true}

  • 如果你只想将改变属性发送到服务器, 调用model.save(attrs, {patch: true})。

  • 通过模型中的isNew方法可以检查数据是新建还是更新。

    var Todo = Backbone.Model.extend({
         //模型修改数据用的url
        url: 'http://localhost/work/backbone2/data.php?method=save',
        defaults: {
            title: '',
            completed: false
        }
    });
    
    var TodosCollection = Backbone.Collection.extend({
        model: Todo,
        //集合从服务器获取数据用的url
        url: 'http://localhost/work/backbone2/data.php',
    });
    
    var collection = new TodosCollection();
    //从服务器获取数据,现在集合中已经有了三个数据
    collection.fetch({
        success: function () {
            var newTodo = new Todo({ title: '新建的数据' });
            collection.add(newTodo); //集合添加一个新的模型
    
    
            //1. 发送一个从服务器获取的未修改的数据
            var firstModel = collection.at(0);//通过索引获取集合中的模型
            firstModel.save(); //PUT方式,firstModel的所有属性都发送到了服务器
    
            //2.发送一个从服务器获取的修改的数据
            var secondModel = collection.at(1);
            secondModel.set('title', '修改标题2');
            secondModel.save();//PUT方式,secondModel的所有属性都发送到了服务器
    
            // 3.发送一个从服务器获取的修改的数据,只发送修改得属性
            var thirdlyModel = collection.at(2);//通过索引获取集合中的模型
            //PATCH方式,thirdlyModel的title属性都发送到了服务器
            thirdlyModel.save({ 'title': '修改标题3' }, { patch: true });
    
            //4.发送一个新数据到服务器
            newTodo.save();//POST方式,newTodo的title属性都发送到了服务器
        }
    });
    

(3). destroy([options])从服务器上删除模型

  • 调用Model.destroy()方法时,将以DELETE请求方式向服务器发送对象的ID。在调用destroy()方法时,如果没有ID号属性,可以通过idAttribute属性进行设置,否则不会发送数据请求操作。

  • 调用destroy()方法的过程中,可以在配置对象中添加success、error函数或将wait属性值设为true。

  • 可以通过调用 Model.destroy()方法从集合和服务器中删除一个存在的模型。

  • 与Collection.remove()不同,remove()方法只是从集合中删除一个模型,而Model.destroy()还会向集合的URL发送一个HTTP DELETE请求。

  • 如果模型是新建的,在该模型上调用destroy() 会返回false。

    var Todo = Backbone.Model.extend({
      url: 'http://localhost/work/backbone2/data.php?delete=true',
        idAttribute: 'id', //如果别的属性是唯一的,也可以用作idAttribute的值
        defaults: {
            title: '',
            completed: false
        }
    });
    
    var TodosCollection = Backbone.Collection.extend({
        model: Todo,
        url: 'http://localhost/work/backbone2/data.php'
    });
    
    var collection = new TodosCollection();
    //从服务器获取数据,现在集合中已经有了三个数据
    collection.fetch({
        success: function () {
            var newTodo = new Todo({ title: '新建的数据' });
            collection.add(newTodo); //集合添加一个新的模型
    
            //1.删除一个从服务器获取到的数据
            var firstModel = collection.at(0);
            //DELETE方式,没有找到发送的参数
            firstModel.destroy({
                success: function () {
                    console.log('删除成功');
                }
            });
    
            //2.删除一个从新增的还没有发送到服务器的数据
            console.log(newTodo.destroy()); //返回false
        }
    });
    

四、视图

//定义视图
var TodoView = Backbone.View.extend({
  el:'body',//html元素名称,将在该属性值内部渲染视图
  initialize: function(){ //初始化视图对象值
    this.html = '创建一个视图';
  },
  render: function(){//渲染视图
    //用jQery设置视图元素的html
    $(this.el).html(this.html)
  }
});

//实例化视图
var todoView = new TodoView();
//手动调用render()方法来输出HTML代码
todoView.render();

(1) el是什么

  • el是DOM元素的一个引用,所有视图都必须有一个el(如果没有手动设置el属性的值,Backbone则会自动设置为当前元素)。视图可以使用el构成它的元素内容,然后将所有的内容一次性插入到DOM中,使页面渲染更快,因为浏览器执行了最小次数的重排和重绘。

  • 有两种方式可以将DOM元素与视图相关联:为视图创建一个新元素,随后将它添加到DOM中((2)中将介绍);用页面已经存在的元素给视图做一个引用。

  • 视图的.$el属性等价于$(view.el).$(selector)则等价于$(view.el).find(selector)

    <div id="box"></div>
    
    <script>
        var TodosView = Backbone.View.extend({
          el: '#box', //el也可以指向页面上已经存在的元素
          render: function () {
            this.$el.html('<p></p>'); //this.$el  等于 $(this.el)
            this.$('p').text('填充文本'); //this.$('p')  等于 this.$el.find('p')
          }
        });
    
        var todosView = new TodosView();
        todosView.render();
    
        console.log(todosView.el); //<div id="box"><p>填充文本</p></div>
    </script>
    

(2) 为视图创建一个新的HTML元素

  • 如果想对一个视图创建一个新的元素,可以在视图上对下面的属性做任意组合的设置:tagName、id、attributes和className。Backbone就会创建一个新的元素,新创建的元素引用将在el属性上可用。tagName如果不指定的话,默认是div。

  • 定义一个视图的时候,如果想在运行的时候指定相关的值,可以将options、el、tagName、id和className定义为函数。

    var TodosView = Backbone.View.extend({
      tagName: 'p',
      className: 'container text-center', //多个class用空格分开
      id: 'todos',
      attributes: {
        'data-value': '123'
      }
    });
    
    var todosView = new TodosView();
    //创建一个p元素,在页面上并不存在
    console.log(todosView.el); //<p data-value="123" id="todos" class="container text-center"></p>
    

(3) 理解render()和视图模板

  • render()是渲染模板逻辑中的一个可选函数。Underscore里的_.template方法将JavaScript模板编译成可以渲染页面的可执行函数。

  • Underscore模板的具体用法:https://underscorejs.net/#template

    <ul id="todo"></ul> 
    
    <!-- 定义一个模板 -->
    <script type="text/template" id="item-template">
      <div>
        <input id="todo_complete" type="checkbox" <%= completed ? 'checked="checked"' : '' %>
        <%= title %>
      </div>
    </script>
    
    <script>
      var Todo = Backbone.Model.extend({
        defaults: {
          title: '这是标题',
          completed: true
        }
      });
    
      var TodoView = Backbone.View.extend({
        tagName: 'li',  //该视图创建一个li元素
        todoTpl: _.template($('#item-template').html()), //获取模板中的html,保存到todoTpl中(todoTpl可以随便命名)
        render: function () {
        	//将model中的数据传入到模板中(模板中需要的数据,model传过去的必须有)
        	//<li><div><input id="todo_complete" type="checkbox" checked="checked" 这是标题="" <="" div=""></div></li>
          this.$el.html(this.todoTpl(this.model.toJSON()));
          return this;
        }
      });
    
      var todo1 = new Todo();
      var todoView = new TodoView({
        model: todo1 //将视图与模型相对应
      });
    
      //调用视图的渲染方法,也可以将该步骤放到视图的initialize方法中
      todoView.render();
      $('#todo').html(todoView.el);
    </script>
    

(4) setElement() 动态地改变视图元素

如果想将现有的Backbone属兔应用于另外一个不同的DOM元素,可以使用setElement()。Backone会取消设置在前一个元素上的事件,并把他们设置给新的元素。

var btn1 = $('<button>按钮1</button>');
var btn2 = $('<button>按钮2</button>');
var View = Backbone.View.extend({
  events: {
    click: function (e) {
      console.log(e.target);
    }
  }
});

//将视图事件绑定到btn1上
var view = new View({
  el: btn1
});
//触发btn1的click事件
btn1.trigger('click'); //<button>按钮1</button>

//将视图应用于btn2
view.setElement(btn2);
//触发btn1的click事件
btn1.trigger('click'); //未触发click事件
//触发btn2的click事件
btn2.trigger('click');//<button>按钮12</button>

(5) remove() 移除视图

通过调用 remove() 方法将视图从当前页面中移除。同时停止对该视图的事件监听。

var TodosView = Backbone.View.extend({
  el: '#box',
  render: function () {
    this.$el.html('<p></p>');
    this.$('p').text('填充文本');
  },
  events: {
    'click p': function () {
      this.remove();
      console.log('移除视图');
    }
  }
});

var todosView = new TodosView();
todosView.render();

(6) 视图与模型

当实例化一个新的视图对象的时候,通过设置model属性值与需要访问的模型对象进行关联。随后在这个视图的所有方法中,可以使用this.model这个属性访问到所指定的模型对象。

var TodoItem = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});
var TodosView = Backbone.View.extend({
  el: 'body',
  render: function () {
    var str = '标题为:' + this.model.get('title') + '是否完成:' + (this.model.get('completed') ? '完成' : '未完成');

    this.$el.html(str);
  }
});


var todo = new TodoItem({title: '读书'});

//创建视图实例,并把创建的模型作为参数传入
var todosView = new TodosView({model:todo});
todosView.render();

(7) 视图与模型集合

在实例化一个新的视图对象的时候,将collection 属性值设置为关联的集合对象,在构建视图类时,可以使用this.collection的方式获取被关联集合对象。

var TodoItem = Backbone.Model.extend({
defaults: {
   title: '',
   completed: false
 }
});

var TodoList = Backbone.Collection.extend({
 model: TodoItem
});


var TodosView = Backbone.View.extend({
 el: 'body',
 render: function () {
   var str = ''
   _.each(this.collection.models, function(model, index, list){
     str += '标题为:' + model.get('title') + '是否完成:' + (model.get('completed') ? '完成' : '未完成') + '<br />';
  	});
   
   this.$el.html(str);
 }
});


var todo1 = new TodoItem({title: '读书'});
var todo2 = new TodoItem({title: '看报', completed: true});
var todo3 = new TodoItem({title: '写字'});
var todo4 = new TodoItem({title: '上课', completed: true});

var todos = new TodoList([todo1, todo2, todo3, todo4]);


//创建视图实例,并把创建的模型作为参数传入
var todosView = new TodosView({collection:todos});
todosView.render();

(8) 视图中的事件

通过视图对象的events属性,我们可以告诉Backbone如何把事件映射到其相关的处理函数上。映射关系为:{"event selector": "callback"},Backbone.js在内部使用了jQuery的on()函数为视图中的DOM事件声明回调函数。如果没有为selector指定值,默认地使用当前视图的根元素(this.el)

<div id="box">
  <button type="button" class="edit">修改</button>
</div>

<script>
  var TodosView = Backbone.View.extend({
    el: '#box',
    events: {
      'click .edit': 'boxClick'
    },
    boxClick: function(){
      console.log('触发修改事件');
    }
  });

  var todosView = new TodosView();
</script>

(7) delegateEvents 事件委托

  • 当我们只希望视图从应用的某一特定部分开始对DOM事件

五、事件

Backbone 支持的所有的内置事件清单:https://www.backbonejs.com.cn/#Events-catalog

Backbone.Events混入到了其他Backbone类,其中包括:

  • Backbone;
  • Backbone.Model;
  • Backbone.Collection;
  • Backbone.View;
  • Backbone.Router;
  • Backbone.History;

Backbone.Events也混入到了Backbone对象中。鉴于Backbone是一个全局都可以访问的对象,它可以作为一个简单的事件总线:

Backbone.on('event', function(){
	console.log('触发一个事件总线上的事件');
})

1. on(eventName:attrName, callback, [context])

  • 使用on()方法可以给一个对象的自定义事件绑定触发该事件时执行的函数,当自定义事件触发时,绑定的函数将被执行。

  • on()方法内部,Backbone.Events会把回调函数保存在一个名为_events的关联数组中。

  • 如果在页面上有很多事件,可以使用冒号对事件进行命名空间的设定。

  • 一个对象上发生的所有事件都会被all事件捕获。

    var obj = {};
    _.extend(obj, Backbone.Events);
    
    //obj上的任何事件触发,该事件都会捕获
    obj.on('all', function (eventName, args) {
        console.log('事件名称:' + eventName, '事件参数:' + args);
    })
    
    //绑定一个自定义事件
    obj.on('shijian', function (msg) {
        console.log(msg);
    });
    
    //给相同事件一个命名空间
    obj.on('click:tap', function (msg) {
        console.log(msg);
    });
    
    obj.on('click:break', function (msg) {
        console.log(msg);
    });
    
    //触发自定义事件
    obj.trigger('shijian', '事件的消息1');
    obj.trigger('click:tap', '事件的消息2');
    obj.trigger('click:break', '事件的消息3');
    

2. off([event], [callback], [context])

  • off可以删除之前绑定在对象上的回调函数。

  • 可以通过省略第一个参数的方式从所有事件上取消某一回调函数的绑定。

  • 可以通过省略第二个参数的方式来从事件上取消所有回调函数的绑定。

  • 如果没有指定context,所有上下文下的这个回调函数将被移除。

  • 如果省略所有参数,那么它会从所有事件上取消所有回调函数的绑定。

    // 只移除change事件的onChange回调函数.
    obj.off("change", onChange);
    
    // 移除change事件的所有回调函数.
    obj.off("change");
    
    // 移除所有事件的onChange回调函数
    obj.off(null, onChange);
    
    // 移除context上下文中的所有事件和回调函数
    obj.off(null, null, context);
    
    // 移除obj上的所有事件和所有回调函数
    obj.off();
    

3. trigger(event, [*args])

trigger 为指定事件(或用空格分隔的一组事件列表)触发回调函数。

//trigger 同时触发三个事件,同时给这三个事件传递两个参数
obj.trigger('dance jump skip', '参数1', '参数2');

4. listenTo(other, event, callback)

  • object 监听 另一个(other)对象上的一个特定事件。不使用other.on(event, callback, object),而使用这种形式的优点是:listenTo允许 object来跟踪这个特定事件,并且以后可以一次性全部移除它们。callback总是在object上下文环境中被调用。

    var a = _.extend({}, Backbone.Events);
    var b = _.extend({}, Backbone.Events);
    var c = _.extend({}, Backbone.Events);
    
    //a监听b和c的事件
    a.listenTo(b, 'anything', function () {
        console.log('b 的anything事件被触发');
    });
    
    a.listenTo(c, 'everything', function () {
        console.log('c 的everything事件被触发');
    });
    
    //b和c触发相应的事件
    b.trigger('anything');
    c.trigger('everything');
    

5. stopListening([other], [eventName], [callback])

  • eventName参数为被加你听对象所触发的事件名称,如果要停止监听多个事件,可以在这个参数中使用空格分隔。callback参数为停止被监听对象触发后,监听对象执行的自定义函数。

  • View.remove()的默认实现里调用了一次stopListening()。

    a.stopListening();
    

6. this

  • 在一个视图中,有两种类型的事件可以监听:DOM事件和使用事件API注册的事件。

    • 1.可以使用视图的events属性或jQuery.on()来绑定DOM事件。使用events绑定的事件,回调函数中的this指向的是视图对象。而jQuery绑定的事件,this都会被jQuery设置为该DOM元素。
    • 2.使用事件API注册的事件绑定,如果使用on()绑定事件可以将上下文参数作为第三个参数进行传入;如果使用listenTo()绑定事件,回调函数里的this执行的是监听器自身。
    var View = Backbone.View.extend({
       el: '#todo',
        events: {
            //单选按钮绑定一个DOM事件
            'click [type="checkbox"]': 'clicked'
        },
        initialize: function () {
            //div添加一个DOM事件
            this.$el.click(this.jqueryClicked);
    
            //使用事件API给视图绑定一个事件
            this.on('apiEvent', this.callback);
        },
        clicked: function (event) {
            console.log(this); //this 视图实例 view
    
            //触发使用API绑定的事件
            this.trigger('apiEvent', event.type)
        },
        jqueryClicked: function () {
            console.log(this);//this div的DOM对象
        },
        callback: function (eventType) {
            console.log(this);//this 视图实例 view
            console.log(eventType); //click
        }
    });
    
    var view = new View();
    

六、路由

  • 路由允许我们把一个URL映射到负责渲染某一视图的特定的回调函数上边。
  • router对象的导航功能实际上是由router和history两个类共同完成的。前者用于定义和解析hash属性的匹配规则,并将规则中的url映射到对应的动作(action)函数;后者则是监听url的变化,并执行对应的动作(action)函数。
  • 在绝大部分应用程序里只用一个路由,来让程序保持组织性,而不会显得臃肿。

(1). action方式绑定URL

  • 在构建router类时,通过添加routes属性,在该属性中声明url或hash属性和函数的对应关系。

  • 当所有的 Routers 创建并设置完毕,调用 Backbone.history.start() 开始监控 hashchange 事件并分配路由。

  • 结尾的斜杠会被当作URL的一部分, docsdocs/ 将触发不同的回调。如果你不能避免产生这两种类型的URLs时, 你可以定义一个docs(/)来匹配捕捉这两种情况。

  • routes中*表示零个或多个任意字符,它可以与反斜杠字符或冒号组合。

    <a href="">首页</a>
    <a href="#search">查询列表</a>
    <a href="#search/abc">关键字查询</a>
    <a href="#search/abc/p2">页码关键字查询</a>
    <a href="#other">其他页</a>
    <a href="#error">错误页</a>
    
    <script>
    var testrouter = Backbone.Router.extend({
      routes: {
        '': 'main', //默认触发main函数
        'search': 'search_list', // '/#search'时触发search_list函数
        'search/:key': 'search_key', //'search/变量key'
        'search/:key/p:page1': 'search_key_page', //'search/变量key/p变量page'
        '*path': 'default',
      },
      main: function () {
        console.log('首页');
      },
      search_list: function () {
        console.log('查询列表');
      },
      search_key: function (key) {
        console.log('关键字查询,查询的关键字为:' + key);
      },
      search_key_page: function (key, page1) {
        console.log('页码关键字查询,查询的关键字为:'  + key + ',页码为:' + page1);
      },
      default: function (params) {
        console.log( params + '页');
      },
    });
    
    var router = new testrouter();
    Backbone.history.start();
    </script>
    

(2). event方式绑定URL

<a href="">首页</a>
<a href="#search/abc/p2">页码关键字查询</a>
<a href="#error">错误页</a>

<script>
  var testrouter = Backbone.Router.extend({
    routes: {
      '': 'main', //默认触发main函数
      'search/:key/p:page1': 'search_key_page', //'search/变量key/p变量page'
      '*path': 'default',
    }
  });

  var router = new testrouter();

  router.on('route:main', function () {
    console.log('首页');
  });

  router.on('route:search_key_page', function (key, page1) {
    console.log('页码关键字查询,查询的关键字为:' + key + ',页码为:' + page1);
  });

  router.on('route:default', function () {
    console.log('其他页/默认页');
  });

  Backbone.history.start();
 </script>

(3) route(route, name, callback)方法

  • 在router类中,route方法的功能是动态修改url中的hash属性的匹配规则和动作(action)函数。

    <a href="">首页</a>
    <a href="#search">查询列表</a>
    <a href="#search/abc">关键字查询</a>
    
    <script>
        var testrouter = Backbone.Router.extend({
          routes: {
            '': 'main', //默认触发main函数
            'search': 'search_list', // '/#search'时触发search_list函数
          },
          main: function () {
            console.log('首页');
          },
          search_list: function () {
            console.log('查询列表');
          },
        });
    
        var router = new testrouter();
    
        router.route('search', 'search_list', function(){
          console.log('覆盖查询列表');
        });
    
        router.route('search/:key', 'search_list', function(key){
          console.log('添加关键字查询,关键字为:' + key);
        });
        
        Backbone.history.start();
    </script>
    

(4) navigate(fragment, option)方法

  • 在router类中,navigate方法的功能是自动跳转到指定的hash属性中,并通过方法中的配置对象设置是否执行与hash属性匹配规则对应的动作(action)函数。

  • 在参数option中,设置trigger为true,将执行与hash属性匹配规则对应的动作(action)函数,否则不执行该函数。 设置 replace选项设置为true,则此时的url会被新的url替换。

    var testrouter = Backbone.Router.extend({
      routes: {
        'search1/:key': 'search1_key',
        'search2/:key': 'search2_key',
      },
      search1_key: function (key) {
        console.log('查询1事件被触发:' + key);
    
        this.navigate('search2/def', {trigger: true});
      },
      search2_key: function (key) {
        console.log('查询2事件被触发:' + key);
      },
    });
    
    var router = new testrouter();
    Backbone.history.start();  //注意historystart()的位置
    
    router.navigate('search1/abc', {trigger: true});
    

(5) history对象的stop方法

Backbone.history.stop()方法的功能是停止监听url的变化。

<a href="#index">首页</a>
<a href="#search">查询列表</a>

<button type="button" id="stop-btn">停止监听url</button>

<script src="./../../js/jquery-3.4.1.min.js"></script>
<script src="./../../js/underscore-min.js"></script>
<script src="./../../js/backbone-min.js"></script>

<script>
  var testrouter = Backbone.Router.extend({
    routes: {
      '*index': 'index', //默认触发main函数
      'search': 'search_list', // '/#search'时触发search_list函数
    },
    index: function () {
      console.log('首页');
    },
    search_list: function () {
      console.log('查询列表');
    },
  });

  var router = new testrouter();
  //开始监听url的变化
  Backbone.history.start();

  $('#stop-btn').click(function(){
    Backbone.history.stop();
  })
</script>
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值