zoukankan      html  css  js  c++  java
  • Backbone源码解析(四):View(视图)模块

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

    首先我们编写基础的html:

    <div id="m" style="height:200px; 200px; background:red;">
    	<div class="c" style="height:100px; 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关联到对应的模型对象,同时它也使得我们在编写代码的时候更加美观。

  • 相关阅读:
    迭代模型
    螺旋模型
    瀑布模型
    V模型
    codeforces411div.2
    专题1:记忆化搜索/DAG问题/基础动态规划
    Python
    字符串的相关操作方法
    Python基本数据类型
    编码
  • 原文地址:https://www.cnblogs.com/constantince/p/4369045.html
Copyright © 2011-2022 走看看