backbone.js 教程(1) View & Model & Collection
Backbone.js Overview
- 它由Jeremy Ashkenas开发,最初发行于2010-10-13
- 它是一个轻量的JavaScript类库,只依赖于underscore.js,非强制依赖于jquery,大小只有7.6Kb,作为一个框架级的js文件,它已经非常小了
- 它提供了Model-View-Presenter(MVP)的框架模式,可以帮助前端开发搭建一个层次清晰的Web应用框架
- 它提供了models和collections来封装数据结构,提供了views来操作DOM,提供了自定义事件将数据结构和DOM操作绑定在一起
Environment Setup
要完整使用BackboneJS,需要引入以下js
- Underscore.js(>= 1.8.3)或者lodash.js
- Jquery.js(>= 1.11.0)
- Json2.js(如果需要支持IE)
在没有npm的环境下,可以下载压缩包或者使用CDN。
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.0/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
Bakbone.js View
负责操作DOM,采用 OO 的思想来操作一个视图。
Backbone.js View API
- extend 每一个自定义的View都必须由Backbone.View类extend而来,override父类中的属性
- initialize() View的构造函数,每当new一个实例时,就会调用该方法
- render() 通常将view的渲染逻辑写在此方法中,并在initialize中调用它
- template() View渲染时的模板,可以使用underscore的模板,也可以使用其他任意JS模板引擎
- el View对应的DOM对象,可以使用id选择器,类选择器来定义
- $el View对应的jQuery对象,方便使用jQuery的方法来操作DOM
- tagName View对应的DOM节点的标签名称,默认是“div”
- id View对应的DOM节点的id属性
- className View对应的DOM节点的class属性
- events 给View绑定事件
- remove() 移除一个view,其el将从DOM中移出,绑定的事件也将停止监听
Create a View
只需要扩展视图构造函数 Backbone.View
, 传入Dom相关的属性。
示例 假如,需要在DOM中动态添加一个id=“root”的div。
不使用backbone.js,我们通常这样实现。
// app.js
function addRoot() {
var el = document.createElement('div');
el.id = 'root';
el.innerHTML = 'Hello Backbone!!!';
document.body.appendChild(el);
}
addRoot();
使用backbone.js,我们这样实现:
// app.js
var AppView = Backbone.View.extend({
tagName: 'div',
id: 'root',
initialize: function () {
this.render();
},
render: function () {
this.el.innerHTML = 'Hello Backbone!!!';
return this;
}
});
var appView = new AppView();
document.body.appendChild(appView.el);
- tagName 指定这个element 是一个div
- id 指定这个div的id属性值
- 当 调用 new AppView() 时,执行initialize() 函数
- render() 函数用于渲染这个element
Get Existed Element
假如,在html中已经定义了div#root这个element,想修改它的内容。
使用Backbone.js怎么来操作这个element呢?
// index.html
<body>
<div id="root">loading...</div>
</body>
var AppView = Backbone.View.extend({
el: '#root',
initialize: function () {
this.render();
},
render: function () {
this.el.innerHTML = 'Hello Backbone!!!';
return this;
}
});
var appView = new AppView();
Bind events
格式:
events: {
'event1 selector1': 'function name1',
'event2 selector2': 'function name2',
...
}
示例 有这样一个小应用,在input中输入后,回车,添加一个new goal;点击每一个goal后面的remove,移除此项目。
// index.html
<div id="root" class="color-dark">
<header>
<h2>My Life Goals</h2>
<input id="new-goal" type="text" placeholder="add a new goal">
</header>
<ul id="goal-list">
<li class="goal-item">Goal one <a class="btn-remove">remove</a></li>
<li class="goal-item">Goal two <a class="btn-remove">remove</a></li>
<li class="goal-item">Goal three <a class="btn-remove">remove</a></li>
<li class="goal-item">Goal four <a class="btn-remove">remove</a></li>
</ul>
</div>
- 给input绑定一个 keypress 事件
- 给每一个 .btn-remove 绑定一个click事件
// app.js
var AppView = Backbone.View.extend({
el: '#root',
… …
events: {
'keypress #new-goal': 'addGoal',
'click .btn-remove': 'clear',
},
addGoal: function(ev) {
if (ev.keyCode != 13) return;
console.log('addGoal');
// To do
},
clear: function() {
// To do
}
});
var appView = new AppView;
How to change view
在引入Backbone.js的Model之前,我们可以这样来实现 addGoal 方法。
addGoal: function(ev) {
if (ev.keyCode != 13) return;
var newGoal = $('#new-goal').val();
if(newGoal === '') return;
var goalHtml = '<li class="goal-item">'+ newGoal +'<a class="btn-remove">remove</a></li>';
$('#goal-list').append(goalHtml);
$('#new-goal').val('');
}
在Backbone.js 出现之前,当数据发生变化视图需要重新渲染时,我们通常使用js或jQuery来进行DOM操作,改变展示的内容。
这样做data和视图渲染混在一起,显得很乱;而且,如果视图上要显示的属性很多,拼接的代码就很长很长。
所以,使用Backbone.js 的Model和Collection 将data和View 进行分离。
Bakbone.js Model & Collection
Model的作用
- 封装数据结构
- 处理业务逻辑
- 从server 加载、保存数据
- 当data发生变化时触发事件,比如重新渲染视图
Collection的作用
Collection是Model的有序集合,和Model一样用于数据加载、保存,监听数据变化,还可以使用 Underscore.js 提供的方法来操作Collection。
主要适用于list、table等视图的渲染。在本例中,就需要定义一个Collection来渲染列表,并监听Collection的变化。
定义Model和Collection
// Goal Model
var Goal = Backbone.Model.extend({
defaults: {
title: ''
}
});
// Goal Collection
var GoalCollection = Backbone.Collection.extend({
model: Goal,
});
使用template
下面这段代码,有一些地方是相同的,为了避免重复代码,可以使用模板来渲染。
<ul id="goal-list">
<li class="goal-item">Goal one <a class="btn-remove">remove</a></li>
<li class="goal-item">Goal two <a class="btn-remove">remove</a></li>
<li class="goal-item">Goal three <a class="btn-remove">remove</a></li>
<li class="goal-item">Goal four <a class="btn-remove">remove</a></li>
</ul>
在html中定义模板
把重复的部分抽出来,定义模板时使用<script>
标签,但这里的type是text/template
,然后给它一个id,用于在View中通过id来获取它。
<body>
<div id="root" class="color-dark">
<header>
<h2>My Life Goals</h2>
<input id="new-goal" type="text" placeholder="add a new goal">
</header>
<ul id="goal-list">
<!-- template -->
</ul>
</div>
<script type="text/template" id="item-template">
<li class="goal-item"><%= title %><a class="btn-remove">remove</a></li>
</script>
</body>
- <%= %>表示插入变量
- <% %>表示插入任意JS代码段
- <%- %>表示插值并进行转义
View中定义解析函数template()
在js中定义GoalView,用于生成每一个Goal对应的<li>
节点。
BackboneJS中的template实际上调用的是underscore.js的template方法,该方法可以将 JavaScript 模板编译为可以用于页面呈现的函数,它返回的是一个函数。
_.template(templateString, [settings])
render时调用template
然后在render中调用template方法,把model对象作为参数传入。
// app.js
// Goal Model
var GoalModel = Backbone.Model.extend({
defaults: {
title: '' // 默认属性值
}
});
// Goal Collection
var GoalCollection = Backbone.Collection.extend({
model: GoalModel,
});
var GoalView = Backbone.View.extend({
tagName: 'li',
initialize: function () {
this.render();
},
template: function () {
return _.template($('#item-template').html()); //根据模板的id来获取模板定义的内容
},
render: function () {
this.$el.html(this.template()(this.model.toJSON()));
},
events: {
'click .btn-remove': 'clear', // 绑定事件
},
clear: function() {
// To do
}
});
var AppView = Backbone.View.extend({
el: '#root',
… …
events: {
'keypress #new-goal': 'addGoal',
},
addGoal: function(ev) {
if (ev.keyCode != 13) return;
console.log('addGoal');
// To do
},
});
测试效果:
var view = new GoalView({ model: {title: 'My first goal'} });
this.$("#goal-list").append(view.$el);
bind Collection to View
在AppView中,修改addGoal的添加模式,将原来的直接操作DOM,修改为通过data的变化来触发DOM的渲染。
- 在AppView中实例化一个GoalCollection,命名为goalList
- Keypress事件触发时,修改goalList,这里调用了Backbone.Collection中的push()方法
var AppView = Backbone.View.extend({
el: '#root',
initialize: function () {
this.goalList = new GoalCollection();
this.render();
},
render: function () {
return this;
},
events: {
'keypress #new-goal': 'addGoal'
},
addGoal: function (ev) {
if (ev.keyCode != 13) return;
var inputVal = $('#new-goal').val(); // 获取输入的值
if (inputVal === '') return;
this.goalList.push({ title: inputVal }); // push到Collection
$('#new-goal').val('');
},
});
但是,此时,你会发现虽然goalList发生了变化,但是页面并没有跟着渲染。
因为,View并没有对Collection的变化进行监听。
Model 和 Collection的事件监听
View 监听 Model或Collection的变化
在AppView中,通过listenTo()方法,监听Collection的变化,当Collection发生变化时,触发内部的某个方法。
object.listenTo(other, event, callback)
listenTo 用于一个对象,监听另一个对象的变化
停止监听使用stopListening
object.stopListening([other], [event], [callback])
监听add事件
var AppView = Backbone.View.extend({
el: '#root',
initialize: function () {
this.goalList = new GoalCollection();
this.render();
this.listenTo(this.goalList, 'add', this.addOne);
// or
// this.goalList.on('add', this.addOne, this);
},
render: function () {
return this;
},
events: {
'keypress #new-goal': 'addGoal'
},
addGoal: function (ev) {
if (ev.keyCode != 13) return;
var inputVal = $('#new-goal').val();
if (inputVal === '') return;
this.goalList.push({ title: inputVal });
// or this.goalList.add({ title: inputVal });
$('#new-goal').val('');
},
addOne: function (goal) {
var view = new GoalView({ model: goal });
this.$("#goal-list").append(view.$el);
}
});
这里为什么监听的event是 add,而不是 push?
因为push()方法底层其实调用的是add()方法。
this.goalList.push({ title: inputVal });
修改为
this.goalList.add({ title: inputVal });
效果相同
监听destroy事件
在上一步中,已经给GoalView绑定了Goal这个Model,那么在View中就可以使用Model来控制View的渲染。在GoalView中需要监听GoalModel的变化,goalModel移除时,销毁视图。
var GoalView = Backbone.View.extend({
tagName: 'li',
initialize: function () {
this.render();
this.listenTo(this.model, 'destroy', this.remove);
//or this.model.on('destroy', this.remove, this);
},
template: function () {
return _.template($('#item-template').html());
},
render: function () {
console.log('model', this.model.toJSON());
this.$el.html(this.template()(this.model.toJSON()));
},
events: {
'click .btn-remove': 'clear',
},
clear: function() {
this.model.destroy();
}
});
destroy model后,view 也会从DOM中移除,同时绑定的事件也会停止监听。
this.remove
是View 内置的函数。
remove()方法不仅可以从DOM中移除view对应的节点,同时还能停止节点上绑定的事件监听。
Model或Collection 自我监听变化
在AppView中,还可以通过调用on()方法,让Collection监听自己的变化。
object.on(event, callback, [context])
这种用法是自己监听自己。
如果想停止监听,使用off()方法
object.off([event], [callback], [context])
this.listenTo(this.goalList, 'add', this.addOne);
等效于
this.goalList.on('add', this.addOne, this);
this.listenTo(this.model, 'destroy', this.remove);
等效于
this.model.on('destroy', this.remove, this);
为什么要传入context?
因为调用on()方法的是this.goalList,如果不传入context,那么在addOne()调用时,默认的this指代的是this.goalList,而不是AppView的实例了。
因此,为了保证上下文都是View的实例,需要传入context。
使用 bind() 或 bindAll() 修改context
可以在使用on()时,不传入context,而使用 _.bind() 或 _.bindAll() 来绑定context
在使用bind时,必须使用bind返回的函数
// 使用 bind
initialize: function () {
this.render();
this.remove = _.bind(this.remove, this); // 返回值是一个函数
this.model.on('destroy', this.remove);
},
使用bindAll非常方便,不必考虑返回值
// 使用 bindAll
initialize: function () {
this.render();
_.bindAll(this, 'remove', 'clear'); // 可以同时改变多个函数的context
this.model.on('destroy', this.remove);
},
backbone.js 教程(1) View & Model & Collection的更多相关文章
- Backbone.js学习之View
千呼万唤始出来,终于到最后一个要点View了.照旧,先来一睹官方文档: Backbone views are almost more convention than they are code - t ...
- 用Backbone.js教程系列的链接
整理了一下用Backbone.js系列教程链接. Backbone.js入门教程 用Backbone.js创建一个联系人管理系统(一) 用Backbone.js创建一个联系人管理系统(二) 用Back ...
- Backbone.js入门教程
原文: Getting Started with Backbone.js 不像其它的Web开发语言,过去Javascript很少可用的架构.令人感到高兴的是,最近几年这种情况得到非常大的改善. 今天我 ...
- Backbone.js 使用 Collection
在前面我们的 Backbone.js 用上了 Model, 但绝大数的情况下我们处理的都是一批的 Model 数据列表,所以需要有一个 Collection 来容纳 Model, 就像 Java 里最 ...
- [backbone] Getting Started with Backbone.js
一.简介 Backbone 是一个 JavaScript MVC 框架,它属于轻量级框架,且易于学习掌握.模型.视图.集合和路由器从不同的层面划分了应用程序,并负责处理几种特定事件.处理 Ajax 应 ...
- Backbone.js的技巧和模式
Backbone.js的技巧和模式 Backbone.js的技巧和模式 本文由白牙根据Phillip Whisenhunt的<Backbone.js Tips And Patterns> ...
- [Backbone.js]如何处理Model里面嵌入的Collection?
写了近半个月的backbone.js代码,从一开始的todo到现在做仿微信的网页聊天,其中最大的困惑就在于如何处理比较复杂的Model,其内嵌了一个或者多个Collections. 假设我们有一个Pe ...
- Backbone.js学习之Backbone.View(视图)
Backbone.js为复杂WEB应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和自定义事件:集合附有可枚举函数的丰富API: 视 ...
- Backbone.js 中使用 Model
前面几篇 Backbone.js 的例子中有使用到 template, 及数据的填充,其实这已经很接近 Model 了.现在来学习怎么创建自己的 Model 类,并简单的使用.Backbone.js ...
随机推荐
- C语言的函数声明,定义,调用以及exit退出
#include <stdio.h> #include <stdlib.h> void singing(); int main() { singing(); system(&q ...
- 格子游戏x(并查集)
格子游戏 [问题描述] Alice和Bob玩了一个古老的游戏:首先画一个n * n的点阵(下图n = 3) 接着,他们两个轮流在相邻的点之间画上红边和蓝边: 直到围成一个封闭的圈( ...
- 洛谷P1576 最小花费x
题目背景 题目描述 在n个人中,某些人的银行账号之间可以互相转账.这些人之间转账的手续费各不相同.给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元 ...
- [CSP-S模拟测试]:花(DP)
题目传送门(内部题111) 输入格式 一个整数$T$,表示测试数据组数. 每组测试数据占一行,两个整数,分别表示$L$和$S$. 输出格式 对每组数据,输出一个整数表示答案. 样例 样例输入1: 13 ...
- conda install -c anaconda
有些包在conda默认的channels中不包含,比如cudatoolkit-8.0,cudnn等,这时只需要在conda install指令后加上-c anaconda即可.比如要下载cudatoo ...
- MySql中根据一列状态值查询状态的个数
最近搞报表的项目,要写数据库sql语句,根据状态值查询状态的个数,这个开始难为到我了,不过已经有解决办法了. 在数据库表中有一个字段是状态(zt),这里面有1-7这7个状态,现在查询每个状态的数量,并 ...
- CountDownLatch和CyclicBarrier的比较
1.CountDownLatch是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行:而CyclicBarrier则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后, ...
- servlet与jsp的九大内置对象
- html外部文件读取/写入
1.文件的读取 外部文件读取控件: <input type="file" id="file_jquery" onchange="file_jqu ...
- Ajax+PHP实现的进度条--实例
之前重点学习PHP,所以javascript.Ajax都比较弱一点.现在也开始补课了,今天实现了一个进度条的例子,感觉Ajax实现动态页面真的很厉害,并没有想象中的那么难理解. 进度条作为反应实时传输 ...