zoukankan      html  css  js  c++  java
  • 前端MVC框架Backbone 1.1.0源码分析(二)

    模型是什么?

    Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control. You extend Backbone.Model with your domain-specific methods, and Model provides a basic set of functionality for managing changes.

    模型 是所有 Javascript 应用程序的核心,包括交互数据及相关的大量逻辑: 转换、验证、计算属性和访问控制。

    你可以用特定的方法扩展 Backbone.Model模型 也提供了一组基本的管理变化的功能,这个东西就像是后端开发中的数据库映射那个model一样,也是数据对象的模型,并且应该是和后端的model有相同的属性(仅是需要通过前端来操作的属性)。

    简而言之,就是围绕着数据处理,如创建、校验、销毁和保存到服务端等等...


    如何设计模型

    之前说了,模型可以围绕数据处理类似curd的操作,所以backbone就为我们提供了这样的一个基础模板,Backbone中的模型类是Backbone.Model,它包含了数据存储,数据验证,以及数据发生变动时触发相关动作,我们只要继承就能使用这些特性了

    用别人的框架,就需要了解别人的规则,这种学习成本是跑不掉的 - -

    官方的demo

    下面是一个示例,它演示了定义一个模型使用一个自定义的方法,设置一个属性,触发一个事件的特定属性的变化

    var Sidebar = Backbone.Model.extend({
        promptColor: function() {
            var cssColor = prompt("Please enter a CSS color:");
            this.set({color: cssColor});
        }
    });
    
    window.sidebar = new Sidebar;
    
    sidebar.on('change:color', function(model, color) {
        console.log('修改颜色',color)
    });
    
    sidebar.set({color: 'white'});
    
    sidebar.promptColor();

    当models中值被改变时自动触发一个"change"事件、所有用于展示models数据的views都会侦听到这个事件,然后进行重新渲染。

    Backbone.Model 是Backbone提供模板类,通过继承extend构造自己Sidebar模型类

    所以具有了on ,set 等等这种基础的属性与方法


    Backbone.Model

    模型构造器

    那么我看看backbone模型类模板能为我们提供什么基础功能

    1. 既然是模型首先就是围绕数据操作,上帝set,上帝get不能少,这样也是为了体现出对象封装性
    2. 与此同时数据的清理与改变也是不能少的
    3. 监听对象中属性变化
    4. 为对象添加验证规则,以及错误提示
    5. 对象的获取和保存,需要服务器端支持才能测试
    6. 等等一些围绕的数据的处理了

    继承extend

    就是把模板类的方法继承给子类,所以我们子类都具有相同的特性了

    Backbone.Model.extend

    Backbone.Collection.extend

    Backbone.Router.extend

    Backbone.View.extend

    扩充的静态方法

    Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;

    关于这个继承很好理解,js的继承常用的就是这个了

    代码简单分析下

    创建子类的载体,换句话就是我们构造出来的那个新的类的一个新的构造器

    if (protoProps && _.has(protoProps, 'constructor')) {
        child = protoProps.constructor;
    } else {
        child = function () {
            return parent.apply(this, arguments);
        };
    }

    如果用户自定义了constructor函数,就用这个,否则就内部自行构建

    之后就是复制静态属性到新的child

    然后把父类的原型链的引用给指向child

    这个请参考http://www.cnblogs.com/aaronjs/archive/2012/08/26/2657103.html

    image

    扩展了属性,在constructor中扩展了__super__ 指向父类,继承了模板的原型链上的方法


    上帝get/上帝set

    可以想像下模型实例用来存储数据表中的一行数据(row)

    Backbone利用model的attributes与数据库的字段一一对应

    使用set和get方法来设置或获取模型的属性。

    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);
    };

    不能把属性直接写到 Backbone.Model.extend的扩展中,原因也很简单,一个是封装性,最重要的原型上是共享的,如果是引用类型就糟糕了

    所以属性在模型实例上有一个专门的属性来存储:this.attributes,set/get都围绕this.attributes操作。


    监听对象中属性的变化(change)

      Models 用来创建数据,校验数据,存储数据到服务器端.Models 还可以绑定事件。比如用户动作变化触发 models 的 change 事件,所有展示此model 数据的 views 都会接收到 这个 change 事件,进行重绘。

    如果任何属性的改变模型的状态,“改变”事件将触发模式

    只是实现了一个自定义事件功能

    监听属性color的改变

    sidebar.on('change:color', function(model, color) {
        console.log('修改颜色',color)
    });

    设置改变

    sidebar.set({color: 'white'});

    源码实现

    Backbone.Model继承了自定义事件Events

    _.extend(Model.prototype, Events, {});

    sidebar实例继承了Backbone.Model.

    var Sidebar = Backbone.Model.extend

    所以Sidebar也具有自定义事件的功能,只是在set方法里面按照规则触发

    使用 set() 方法创建或者设置属性值可以触发自定义事件,

    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);
        }
    }

    PS:

    我们知道虽然属性是存储this.attributes中,但是如果是直接

    实例.attributes.name = "属性名";

    这样很明显就丢失了自定义事件了,所以使用 set() 是改变模型状态并触发其变更事件的唯一方法

    Model 这一概念来对事件进行控制,但是这样很好的使我们将结构分离开,容易控制整体以及之后的变更都会变得异常简单。


    为对象添加验证规则,以及错误提示

    验证模型数据规范

    var Chapter = Backbone.Model.extend({
      validate: function(attrs, options) {
        if (attrs.end < attrs.start) {
          return "can't end before it starts";
        }
      }
    });

    源码部分

    _validate: function (attrs, options) {
                if (!options.validate || !this.validate) return true;
                attrs = _.extend({}, this.attributes, attrs);
                var error = this.validationError = this.validate(attrs, options) || null;
                if (!error) return true;
                this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
                return false;
            }

    执行了this.validate(attrs, options) 自定义验证函数,可见如果返回了true

    就会执行this.trigger('invalid', this, error, _.extend(options, {validationError: error})); 错误通知了

    余下的fetch,save,sync,url等等放在合集中在讲吧

  • 相关阅读:
    C# 网络斗地主源码开源
    windows 10
    JAVA Eclipse Incorrect line ending found carriage return 怎么办
    JAVA Eclipse中如何简易的实现消息机制
    JAVA Eclipse中的Android程序如何使用线程
    JAVA Eclipse如何重新设置工作空间workspace
    JAVA Eclipse如何重命名包
    JAVA Eclipse如何修改Android程序名称
    JAVA Eclipse如何设置编程环境字体
    JAVA Eclipse如何设置点击按钮切换图片
  • 原文地址:https://www.cnblogs.com/aaronjs/p/3504571.html
Copyright © 2011-2022 走看看