简单了解Backbone.js的Model模型以及View视图的源码


Posted in Javascript onFebruary 14, 2016

Backbone.Model

今天我们先来谈谈Backbone.js MVC 中的 M , Model是backbone的核心部分,包含着页面展示内容的数据,还有围绕着数据操作的各种 转换,校验,计算 ,权限控制,服务端交互等等操作,你可以通过 Backbone.Model.extend() 生成你的model , 当然生成的model也可以作为一个基类去向下扩展更多的model

var People = Backbone.Model.extend({
     
 });

 var Man = People.extend({

 });

Backbone.Model Api

Backbone.Model 提供了大量方法用于实现一个Model的基本操作,当然其中最基本的还是基于 Backbone.Events 的事件机制,在Model的attributes发生变化的时候,相应的 change:attr事件会被触发,下面是提供的API:

简单了解Backbone.js的Model模型以及View视图的源码

其中有对数据进行服务端操作的方法:

  • sync : 包装了Backbone.sync,xhr的基类
  • fetch : 用于从服务端获取数据
  • save : 向服务端持久化数据
  • destroy: 从服务端删除数据

model中数据操作的方法:

  • get : 从attributes中获取数据
  • set : 向attributes中设置数据
  • escape : 对数据进行编码 ,使用的是underscore的 _.escape
  • has : attributes中有无对应数据
  • unset : 从attributes中删除数据
  • clear : 清空attributes数据
  • changed : 与上个状态(执行过set,unset),相比变化的值
  • toJSON : 将 attributes 序列化成一个对象
  • parse : 当设置项parse为真的时候,初始化/set/unset/fetch等数据操作中会对目标数据进行一个解析返回解析后的对象,此方法为空方法,需要重写覆盖
  • hasChanged : 与上个状态(执行过set,unset),相比是否发生过变化
  • changeAttributes : 与上个状态(执行过set,unset),相比发生的所有值
  • previous : 前一状态 (执行过set,unset),该属性对应的值
  • previousAttributes : 与上个状态(执行过set,unset),发生过变化对象的前一个状态的所有值

model中数据校验的方法:

  • validate:用于对model中数据进行校验,需要重写覆盖默认方法
  • validationError : 返回最近一个invalid时返回的值
  • isValid : 调用_validate方法

下面会针对一些重点的api进行讲解:

构造函数

var Model = Backbone.Model = function(attributes, options) {
     var attrs = attributes || {};
     options || (options = {});
     this.cid = _.uniqueId('c');
     this.attributes = {};
     if (options.collection) this.collection = options.collection;
     if (options.parse) attrs = this.parse(attrs, options) || {};
     attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
     this.set(attrs, options);
     this.changed = {};
     this.initialize.apply(this, arguments);
   };

构造函数主要对初始化的数据和选项进行设置,然后会对生成一个唯一的cid用于标示model,如果options中的parse为true,那么会对初始化数值通过parse方法进行一个解析,调用set方法,所有的初始值会被存入attributes中,调用initialize初始化方法 。model的初始化就完成了。

set

model.set(attributes, [options])

set方法会将值设置进入attribute中,设置时如果设置了slient为true,会触发相应的 change:attr 的事件,最后统一触发change事件,set方法部分代码如下:

 

set: function(key, val, options) {
     //......

      // key值可以是键值对,也可以是一个字符串,将赋值传入attrs属性中  
    if (typeof key === 'object') {
     attrs = key;
     options = val;
    } else {
     (attrs = {})[key] = val;
    }

   // ....

    //对设置的值进行校验 
      if (!this._validate(attrs, options)) return false;
    unset = options.unset; // unset为true时会删除设置的值,unset方法就是通过 set(key,val,{unset:true})去实现的
   
    //当对象正在被被设置的时候,不给 previousAttributes 赋值
    if (!changing) {
     this._previousAttributes = _.clone(this.attributes);
     this.changed = {};
    }
 
    current = this.attributes, prev = this._previousAttributes;
 
    //如果对Id进行了设置,则对对象的id属性也进行改变
    if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
 

    //进行 设置或者是删除操作
    for (attr in attrs) {
     val = attrs[attr];
     if (!_.isEqual(current[attr], val)) changes.push(attr);
     if (!_.isEqual(prev[attr], val)) {
      this.changed[attr] = val;//为model的changed进行设置
     } else {
      delete this.changed[attr];
     }
     unset ? delete current[attr] : current[attr] = val;//如果unset被设置成true了,则进行删除操作
    }
 
    //在silent不为false 的情况下,进行change:attr事件发送
    if (!silent) {
     if (changes.length) this._pending = options;
     for (var i = 0, l = changes.length; i < l; i++) {
      this.trigger('change:' + changes[i], this, current[changes[i]], options);
     }
    }
 

    //触发change事件
    if (changing) return this;
    if (!silent) {
     while (this._pending) {
      options = this._pending;
      this._pending = false;
      this.trigger('change', this, options);
     }
    }
    this._pending = false;
    this._changing = false;
    return this;
   
  }

set的整个流程就是 对传入的数值进行处理,变成一个键值对,然后对数值进行校验,检查正确性,然后开始进行设置操作,设置时检查数值时候是发生改变的,如果有则加入一个 changeed的对象中,然后检查unset的值,进行相应的添加更新删除操作。然后依次触发change:attr和change事件。

save

model.save([attributes], [options])

save方法用于向客户端持久化数据,会根据数据的不同和配置的不同选择使用create,update或者是patch,并且触发 sync 事件,以下为部分代码:

save: function(key, val, options) {     
    // ......
 

 
    //当设置了wait属性true的时候 , save方法先不执行set方法(不触发change事件),只执行validate
    if (attrs && !options.wait) {
     if (!this.set(attrs, options)) return false;
    } else {
     if (!this._validate(attrs, options)) return false;
    }

    //如果wait为true,设置this.attributes
    if (attrs && options.wait) {
     this.attributes = _.extend({}, attributes, attrs);
    }
 

    // .....
   
    var model = this;
    var success = options.success;
    options.success = function(resp) {
     // Ensure attributes are restored during synchronous saves.
     model.attributes = attributes;
     var serverAttrs = model.parse(resp, options);
     //如果wait为true , 那么会在请求返回之后才进行set操作
     if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
     if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
      return false;
     }
     if (success) success(model, resp, options);
     //触发 sync 事件
     model.trigger('sync', model, resp, options);
    };

    //生成XHR onerror 回调函数
    wrapError(this, options);
    //选择方法
    method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
    if (method === 'patch') options.attrs = attrs;
    xhr = this.sync(method, this, options);
 
    // Restore attributes.
    if (attrs && options.wait) this.attributes = attributes;
    //返回xhr对象
    return xhr;
  }

 
save 中最需要注意的就是 wait 的设置,当wait为真的时候,save返回会在xhr返回之后再执行set操作,而不是在xhr之前就进行set操作,因此change事件的触发时机也就不同了。

之前说过整个Backbone都是通过事件串联起来的,所以对于事件触发时机的了解和把握是非常重要的,不然会在开发过程中导致一些奇怪的问题出现。

Backbone.View
前面已经对backbone中的Event、Model、Collection代码进行了分析,现在我们来看下MVC中的V部分,也就是Backbone.View,View在Backbone中主要用于沟通页面中的DOM和Backbone.Model/Collection,页面的逻辑操作,DOM事件的绑定等,View部分的代码非常简答,加上注释只有110左右。 View部分有一下API:

简单了解Backbone.js的Model模型以及View视图的源码

方法不多,下面对部分API进行介绍:

构造方法

var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
 var View = Backbone.View = function(options) {
   this.cid = _.uniqueId('view');
   options || (options = {});
   _.extend(this, _.pick(options, viewOptions));
   this._ensureElement();
   this.initialize.apply(this, arguments);
   this.delegateEvents();
 };

构造方法中为View生成了一个唯一的cid,以'view'开头,然后进行对目标属性viewOptions进行合并,接着调用_ensureElement判断el的情况,接着调用delegateEvents进行方法绑定,初始化完成 。

delegateEvents

view.setElement(element)

   setElement: function(element, delegate) {
    if (this.$el) this.undelegateEvents();//如果已经存在this.$el,进行事件解绑

    //对$el进行赋值,本质是一个jquery或者是 Lo-Dash and Zepto 对象
    this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
    //把dom element 赋值给el
    this.el = this.$el[0];

     //如果没有显式传值,则进行事件绑定
    if (delegate !== false) this.delegateEvents();
    return this;
   }

setElement方法用于设置View对应的element , 这个方法在new的时候会被调用, 如果想要在使用过程中改变View的dom元素指向,可调用这个方法进行重新设置

_ensureElement

_ensureElement: function() {
     //如果已经对el进行设置,直接调用setElement方法
    if (!this.el) {//如果没有设置,生成一个元素对象,再调用setElement方法
     var attrs = _.extend({}, _.result(this, 'attributes'));
     if (this.id) attrs.id = _.result(this, 'id');
     if (this.className) attrs['class'] = _.result(this, 'className');
     var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
     this.setElement($el, false);
    } else {
     this.setElement(_.result(this, 'el'), false);
    }
  }

_ensureElement这个方法是内部方法,在构造函数中使用,用于判断指定的el在页面中存不存在,如果存在则对$el进行赋值,如果不存在,则生成一个$el,但是要注意这个对象是没有落地到dom树中的 。

delegateEvents

delegateEvents([events])

    // *{"event selector": "callback"}*
   //
   //   {
   //    'mousedown .title': 'edit',
   //    'click .button':   'save',
   //    'click .open':    function(e) { ... }
   //   }

   delegateEvents: function(events) {
      //如果不存在events,则直接返回
      if (!(events || (events = _.result(this, 'events')))) return this;
   
      //先解除所有的事件绑定
      this.undelegateEvents();
   
      //处理每个事件
      for (var key in events) {
       var method = events[key];
       //解析回调函数
       if (!_.isFunction(method)) method = this[events[key]];
       if (!method) continue;
   
       //对选择器进行分析
       var match = key.match(delegateEventSplitter);
       var eventName = match[1], selector = match[2];
       method = _.bind(method, this);
   
       //绑定的事件名都是以 eventName + '.delegateEvents' + cid 组成,
       //这么做能够在undelegateEvents的时候选择到这个View的所有事件
       eventName += '.delegateEvents' + this.cid;
       if (selector === '') {
        this.$el.on(eventName, method);
       } else {
        this.$el.on(eventName, selector, method);
       }
      }
      return this;
   }

在View中你可以使用一个 key:value 集合指定对应的事件,在初始化的时候构造函数会调用delegateEvents进行绑定,需要注意的是所有在key中指定的元素的父元素都必须是$el,也就是说元素必须是$el的子节点,否则绑定失败。

View和其他backbone模块一个区别就是没有自己的内建自定义事件,当然他也组合了Events模块,但是所有的事件都需要自己进行建立。View主要是一个MVC模型的承载,其实真正的功能不多,其实从模型层面上看V在前端开发中是最接近业务逻辑的,所以在View中大部分的逻辑都是开发者自己去扩展的。

Javascript 相关文章推荐
用showModalDialog弹出页面后,提交表单总是弹出一个新窗口
Jul 18 Javascript
javascript 弹出层组件(升级版)
May 12 Javascript
JavaScript实现带标题的图片轮播特效
May 20 Javascript
JavaScript动态生成二维码图片
Apr 20 Javascript
jQuery插件jqGrid动态获取列和列字段的方法
Mar 03 Javascript
Vue.js tab实现选项卡切换
May 16 Javascript
JavaScript的setter与getter方法
Nov 29 Javascript
Angular5升级RxJS到5.5.3报错:EmptyError: no elements in sequence的解决方法
Apr 09 Javascript
Angular4 反向代理Details实践
May 30 Javascript
vue.js实现格式化时间并每秒更新显示功能示例
Jul 07 Javascript
基于Vue组件化的日期联动选择器功能的实现代码
Nov 30 Javascript
JavaScript实现预览本地上传图片功能完整示例
Mar 08 Javascript
Backbone.js框架中简单的View视图编写学习笔记
Feb 14 #Javascript
10个JavaScript中易犯小错误
Feb 14 #Javascript
讲解JavaScript的Backbone.js框架的MVC结构设计理念
Feb 14 #Javascript
深入解析JavaScript框架Backbone.js中的事件机制
Feb 14 #Javascript
Node.js 条形码识别程序构建思路详解
Feb 14 #Javascript
jQuery插件支持同一页面被多次调用
Feb 14 #Javascript
JavaScript中通过提示框跳转页面的方法
Feb 14 #Javascript
You might like
处理php自动反斜杠的函数代码
2010/01/05 PHP
PHP+MYSQL会员系统的登陆即权限判断实现代码
2011/09/23 PHP
PHP Hash算法:Times33算法代码实例
2015/05/13 PHP
yii2实现根据时间搜索的方法
2016/05/25 PHP
利用php实现一周之内自动登录存储机制(cookie、session、localStorage)
2016/10/31 PHP
Javascript 判断 object 的特定类转载
2007/02/01 Javascript
ExtJS 2.2.1的grid控件在ie6中的显示问题
2009/05/04 Javascript
基于jQuery的烟花效果(运动相关)点击屏幕出烟花
2012/06/14 Javascript
jquery实现按Enter键触发事件示例
2013/09/10 Javascript
jquery 页面滚动到指定DIV实现代码
2013/09/25 Javascript
javascript 数组排序函数sort和reverse使用介绍
2013/11/21 Javascript
使用纯javascript实现经典扫雷游戏
2015/04/23 Javascript
解决WordPress使用CDN后博文无法评论的错误
2015/12/15 Javascript
jquery zTree异步加载、模糊搜索简单实例分享
2016/03/24 Javascript
谈谈JS中常遇到的浏览器兼容问题和解决方法
2016/12/17 Javascript
js解决软键盘遮挡输入框的问题分享
2017/12/19 Javascript
vue 简单自动补全的输入框的示例
2018/03/12 Javascript
VUE v-for循环中每个item节点动态绑定不同函数的实例
2018/09/26 Javascript
Python中的choice()方法使用详解
2015/05/15 Python
举例讲解Python设计模式编程中对抽象工厂模式的运用
2016/03/02 Python
python正则分析nginx的访问日志
2017/01/17 Python
使用Flask集成bootstrap的方法
2018/07/24 Python
python 保存float类型的小数的位数方法
2018/10/17 Python
浅谈Python type的使用
2019/11/19 Python
使用python的turtle函数绘制一个滑稽表情
2020/02/28 Python
python求解汉诺塔游戏
2020/07/09 Python
教你使用Canvas处理图片的方法
2017/11/28 HTML / CSS
全球第二大家装零售商:Lowe’s
2018/01/13 全球购物
行政助理岗位职责范文
2013/12/03 职场文书
后勤部经理岗位职责
2014/02/23 职场文书
母亲节感恩活动记录
2014/03/16 职场文书
同居协议书范本
2014/04/23 职场文书
教师求职自荐信
2015/03/26 职场文书
2015年扫黄打非工作总结
2015/05/13 职场文书
钱学森电影观后感
2015/06/04 职场文书
《天使的翅膀》读后感3篇
2019/12/20 职场文书