View视图故名思义,它控制的是界面。我们可以把一个大的网页分成很多部分的视图,按照backbone的架构,每一个视图对应都是一个对象,我们可以通过元素的钩子(id或者class或者其他选择器)把它们封装到View对象中集中操作。由于传统项目中界面视图和数据不分层,使得各种操作数据逻辑和视图逻辑的代码混杂在一起,提高了代码的耦合度。backbone就是将这种情况作了很好的处理。下面我们通过一个简单的例子一步步地讲明白它的具体设计思路:

首先我们编写基础的html:

<div id="m" style="height:200px; width:200px; background:red;">
<div class="c" style="height:100px; width:100px; background:green;"></div>
</div>

两个div,父元素的id为m,子元素的class为c。我们要做的是利用bk框架,将model里面一段数据(‘hello’)放到子元素的innerHTML里面,通过点击事件弹出它的内容。下面我们看下js代码:

//M扩展了一个模型,给定一个初始值
var M = Backbone.Model.extend({
defaults : {
text : 'hello world'
}
});
//实例化一个模型
var m = new M();
//继承并且扩展一个View
var V = Backbone.View.extend({
//初始化函数
initialize : function() {
this.render();
},
//渲染模版,在这里我们为了简化使用的事直接给html赋值的方法
render : function() {
var t = this.model.toJSON().text;
$(this.el).find('.c').html(t);
},
//添加内部事件
events : {
'click .c' : 'click'
},
//内部时间具体的实现方法
click : function(e) {
var text = $(e.target).html();
alert(text);
}
});
//创建View的实例,并且给定基础值。
var v = new V({model : m, el : '#m'})

在上面我们新建了一个模型,给它赋默认的一个值,我们还建立了一个视图,并且指向了节面的元素。通过对html源码的查询,我们可以看到,子元素已经填充了model中的hello world 数据:

并且绑定了click事件:

那么它是怎么样实现的呢?首先,我们从实例化V这个对象说起,下面是关于View的构造函数的源码:

e.View = function(a) {
this.cid = f.uniqueId("view");
this._configure(a || {});
this._ensureElement();
this.delegateEvents();
this.initialize.apply(this, arguments)
};
var u = /^(\S+)\s*(.*)$/,
n = ["model", "collection", "el", "id", "attributes", "className", "tagName"];

View的构造函数主要做了几件事情,也就是说在实例化View的时候它1、首先配置默认的值的参数->_configure():

_configure: function(a) {
this.options && (a = f.extend({}, this.options, a));
for (var b = 0, c = n.length; b < c; b++) {
var d = n[b];
a[d] && (this[d] = a[d])
}
this.options = a
},

此方法是对实例化的时候对内部本身的一些值重新赋值。可以看到,里面循环了一个变量n,n指的就是一个包含可以重新定义值的数组,我们可以在之前关于n的定义中找到n所包含的一些值 ["model", "collection", "el", "id", "attributes", "className", "tagName"];如果你在实例化的时候传入的字面量中有n数组中包含的值的健名,那么它就会赋值到view这个对象里面通过this.xxxx或者this.options.xxxx调用到,而其他不在n数组中的属性则不会起任何作用。

第二步确认传入了el,也就是是说确保这个view对象有关联的html元素存在->_ensureElement():

_ensureElement: function() {
if (this.el) {
if (f.isString(this.el)) this.el = g(this.el).get(0)
} else {
var a = this.attributes || {};
if (this.id) a.id = this.id;
if (this.className) a["class"] = this.className;
this.el = this.make(this.tagName, a)
}
}

我们可以从里面看到,这个方法会判断我们在实例化时是有el这个属性是否存在,如果没有它会根据其他的属性配置自动创建一个元素出来,这里用的是自己的方法make()来制造对象的html元素。这里也许大家或有疑问,那就是我不知道需要创建那种类型的元素。bk默认创建的是div元素,因为它默认的就是这样的,在源码中我们可以看到this.tagName属性被赋予了默认的值,所有make出来的时div,当然前提是如果你没有覆盖它的话。

第三步,也是很关键的一步,绑定内部的事件->delegateEvents():

delegateEvents: function(a) {
if (a || (a = this.events)) for (var b in f.isFunction(a) && (a = a.call(this)), g(this.el).unbind(".delegateEvents" + this.cid), a) {
var c = this[a[b]];
if (!c) throw Error('Event "' + a[b] + '" does not exist');
var d = b.match(u),
e = d[1];
d = d[2];
c = f.bind(c, this);
e += ".delegateEvents" + this.cid;
d === "" ? g(this.el).bind(e, c) : g(this.el).delegate(d, e, c)
}
}

查阅源码,我们可以看到此方法会寻找this.events这个属性,并且枚举它。在项目的代码中我们在extend中已经给了它的events属性配置了值:events:{'click .c' : 'click'}并且自定义了关联的click方法(传入了事件对象e)。因此,delegateEvents会把events中的属性进行枚举,逐个地利用zepto的bind方法将我们自己定义的方法(如click)绑定到html(.c)元素上去。值得说明的是,我们在events中绑定的都是el中的元素,也就是说是利用el作为委托的最高级父元素向下捕获事件的。加入A元素不在el这个元素中,那么这样的绑定时无效的。

最后它执行了初始化函数->initialize();而在扩展的过程当中,我们重写了initialize方法并且在里面调用了自定义render函数,因此在实例化的时候会自动渲染html。

就这样,一个完整的视图对象就出现了。View能够使我们更加专注于界面逻辑,每一个视图对象中紧密地包含着自己的内部界面逻辑。通过model关联到对应的模型对象,同时它也使得我们在编写代码的时候更加美观。

Backbone源码解析(四):View(视图)模块的更多相关文章

  1. Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

    Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的?   如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...

  2. Sentinel源码解析四(流控策略和流控效果)

    引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...

  3. 【原创】backbone1.1.0源码解析之View

    作为MVC框架,M(odel)  V(iew)  C(ontroler)之间的联系是必不可少的,今天要说的就是View(视图) 通常我们在写逻辑代码也好或者是在ui组件也好,都需要跟dom打交道,我们 ...

  4. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  5. 『Python』源码解析_从ctype模块理解对象

    1.对象的引用计数 从c代码分析可知,python所有对象的内存有着同样的起始结构:引用计数+类型信息,实际上这些信息在python本体重也是可以透过包来一窥一二的, from ctypes impo ...

  6. iOS即时通讯之CocoaAsyncSocket源码解析四

    原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操 ...

  7. React的React.createContext()源码解析(四)

    一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...

  8. Backbone源码解析(二):Model(模型)模块

    Model(模型)模块在bk框架中的作用主要是存储处理数据,它对外和对内都有很多操作数据的接口和方法.它与视图(Views)模块精密联系着,通过set函数改变数据结构从而改变视图界面的变化.下面我们来 ...

  9. Backbone源码解析(一):Event模块

    Backbone是一个当下比较流行的MVC框架.它主要分为以下几个模块: Events, View, Model, Collection, History, Router等几大模块.它强制依赖unde ...

随机推荐

  1. CMakeLists for tesseract

    在网上找了很多,直接用都不行,试了半天的到以下的结果. cmake_minimum_required(VERSION 2.8) project( test ) include_directories ...

  2. 不容错过!2016年度优秀UI/UX设计文章

    本文整理了一些2016年度最受欢迎的文章,例如有关UI / UX设计的理论知识,书籍和工具,如何做出更好的设计的方法和建议,以及新的设计趋势. 1. 2017年用户体验设计趋势 我们期待着2017年用 ...

  3. 使用ycsb测试cassandra

    参考 https://github.com/cloudius-systems/osv/wiki/Benchmarking-Cassandra-and-other-NoSQL-databases-wit ...

  4. Thinkphp去掉index.php

    1.httpd.conf配置文件中 #LoadModule rewrite_module modules/mod_rewrite.so 把前面的警号去掉2.AllowOverride None 将No ...

  5. DIV弹出和关闭新DIV

    代码用HTML+JS实现: 代码(用HTML+JS实现): <!doctype html> <html lang="UTF-8"> <head> ...

  6. freeCodeCamp:Seek and Destroy

    金克斯的迫击炮! 实现一个摧毁(destroyer)函数,第一个参数是待摧毁的数组,其余的参数是待摧毁的值. 当你完成不了挑战的时候,记得开大招'Read-Search-Ask'. 这是一些对你有帮助 ...

  7. linux系统编程之I/O内核数据结构

    文件在内核中是用三种数据结构进行表示的 (1)文件描述符表:文件描述符表是一个结构体数组,数组的下标就是open函数返回的文件描述符. 文件描述符表的每一个记录有两个字段   *文件描述符标志 * 文 ...

  8. 封装ios静态库碰到的一些问题(一)

    封装IOS动态库,碰到的第一个问题,就是资源文件的问题,如果将你的程序封装成为静态库,那么静态库中不会包含资源文件和xib文件,这个时候就需要自己封装bundle文件了,而笔者开发环境默认是xcode ...

  9. Qt ffmpeg环境搭建

    ffmpeg下载地址:https://ffmpeg.zeranoe.com/builds/ 版本选择第一个,然后多少位看自己的pc(我的是64),右边对应三个都要下载,Static,Shared,De ...

  10. Sass的学习

    第一章:Sass简介 一. 什么是CSS预处理器 定义:CSS预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为CSS增加一些编程的特性,将CSS作为目标生成文件,然后开发者就只要使用 ...