Backbone.js模型验证 #

一、验证概述 #

1.1 什么是验证 #

验证是确保数据符合特定规则的过程,在数据保存前检查数据的有效性。

text
验证流程
├── 调用set/save方法
├── 触发validate方法
├── 验证通过 → 继续操作
└── 验证失败 → 触发invalid事件

1.2 验证时机 #

方法 默认验证 说明
set 设置属性时验证
save 保存模型时验证
unset 删除属性时不验证
clear 清空属性时不验证

二、validate方法 #

2.1 基本验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.name || attrs.name.trim() === '') {
            return '姓名不能为空';
        }
    }
});

var user = new User();

user.on('invalid', function(model, error) {
    console.log('验证失败:', error);
});

user.set({ name: '' }, { validate: true });

2.2 返回错误信息 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.name) {
            return '姓名不能为空';
        }
        if (!attrs.email) {
            return '邮箱不能为空';
        }
        if (attrs.age < 0) {
            return '年龄不能为负数';
        }
    }
});

2.3 返回错误对象 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        var errors = [];
        
        if (!attrs.name || attrs.name.trim() === '') {
            errors.push({ field: 'name', message: '姓名不能为空' });
        }
        
        if (!attrs.email || !attrs.email.match(/^[\w-]+@[\w-]+\.[a-z]+$/i)) {
            errors.push({ field: 'email', message: '邮箱格式不正确' });
        }
        
        if (attrs.age !== undefined && attrs.age < 0) {
            errors.push({ field: 'age', message: '年龄不能为负数' });
        }
        
        if (errors.length > 0) {
            return errors;
        }
    }
});

var user = new User();

user.on('invalid', function(model, errors) {
    errors.forEach(function(error) {
        console.log(error.field + ': ' + error.message);
    });
});

user.set({ name: '', email: 'invalid', age: -1 }, { validate: true });

三、验证规则 #

3.1 必填验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        var required = ['name', 'email'];
        
        required.forEach(function(field) {
            if (!attrs[field] || attrs[field].trim() === '') {
                return field + '是必填字段';
            }
        });
    }
});

3.2 格式验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        var patterns = {
            email: /^[\w-]+@[\w-]+\.[a-z]+$/i,
            phone: /^1[3-9]\d{9}$/,
            url: /^https?:\/\/.+/
        };
        
        if (attrs.email && !patterns.email.test(attrs.email)) {
            return '邮箱格式不正确';
        }
        
        if (attrs.phone && !patterns.phone.test(attrs.phone)) {
            return '手机号格式不正确';
        }
        
        if (attrs.website && !patterns.url.test(attrs.website)) {
            return '网址格式不正确';
        }
    }
});

3.3 范围验证 #

javascript
var Product = Backbone.Model.extend({
    validate: function(attrs) {
        if (attrs.price !== undefined) {
            if (attrs.price < 0) {
                return '价格不能为负数';
            }
            if (attrs.price > 1000000) {
                return '价格不能超过100万';
            }
        }
        
        if (attrs.quantity !== undefined) {
            if (attrs.quantity < 0) {
                return '数量不能为负数';
            }
            if (attrs.quantity > 10000) {
                return '数量不能超过10000';
            }
        }
        
        if (attrs.discount !== undefined) {
            if (attrs.discount < 0 || attrs.discount > 100) {
                return '折扣必须在0-100之间';
            }
        }
    }
});

3.4 长度验证 #

javascript
var Article = Backbone.Model.extend({
    validate: function(attrs) {
        if (attrs.title) {
            if (attrs.title.length < 5) {
                return '标题至少5个字符';
            }
            if (attrs.title.length > 100) {
                return '标题不能超过100个字符';
            }
        }
        
        if (attrs.content) {
            if (attrs.content.length < 100) {
                return '内容至少100个字符';
            }
        }
        
        if (attrs.tags && attrs.tags.length > 10) {
            return '标签数量不能超过10个';
        }
    }
});

3.5 自定义验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (attrs.password && attrs.confirmPassword) {
            if (attrs.password !== attrs.confirmPassword) {
                return '两次密码输入不一致';
            }
            if (attrs.password.length < 6) {
                return '密码至少6个字符';
            }
        }
        
        if (attrs.birthDate) {
            var birthDate = new Date(attrs.birthDate);
            var now = new Date();
            var age = now.getFullYear() - birthDate.getFullYear();
            
            if (age < 18) {
                return '年龄必须满18岁';
            }
            if (age > 120) {
                return '请输入正确的出生日期';
            }
        }
    }
});

四、验证触发 #

4.1 set时验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.name) {
            return '姓名不能为空';
        }
    }
});

var user = new User({ name: '张三' });

var result = user.set({ name: '' }, { validate: true });
console.log(result);
console.log(user.get('name'));

4.2 save时验证 #

javascript
var User = Backbone.Model.extend({
    urlRoot: '/api/users',
    
    validate: function(attrs) {
        if (!attrs.name) {
            return '姓名不能为空';
        }
    }
});

var user = new User();

user.save({ name: '' }, {
    success: function() {
        console.log('保存成功');
    },
    error: function(model, response) {
        console.log('保存失败');
    }
});

4.3 跳过验证 #

javascript
var user = new User({ name: '张三' });

user.set({ name: '' }, { validate: false });
console.log(user.get('name'));

user.set({ name: '' }, { silent: true });

4.4 手动验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.name) {
            return '姓名不能为空';
        }
    },
    
    isValid: function() {
        return this.validate(this.attributes) === undefined;
    }
});

var user = new User({ name: '' });
console.log(user.isValid());

五、错误处理 #

5.1 invalid事件 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.name) {
            return '姓名不能为空';
        }
    }
});

var user = new User();

user.on('invalid', function(model, error, options) {
    console.log('验证错误:', error);
    console.log('验证属性:', options);
});

user.set({ name: '' }, { validate: true });

5.2 validationError属性 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.name) {
            return '姓名不能为空';
        }
    }
});

var user = new User();

var result = user.set({ name: '' }, { validate: true });
if (!result) {
    console.log('验证错误:', user.validationError);
}

5.3 在视图中处理错误 #

javascript
var UserView = Backbone.View.extend({
    events: {
        'click .save': 'saveUser'
    },
    
    initialize: function() {
        this.listenTo(this.model, 'invalid', this.showError);
    },
    
    saveUser: function() {
        var data = {
            name: this.$('#name').val(),
            email: this.$('#email').val()
        };
        
        this.model.save(data);
    },
    
    showError: function(model, error) {
        this.$('.error').text(error).show();
    }
});

5.4 显示多个错误 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        var errors = {};
        
        if (!attrs.name) {
            errors.name = '姓名不能为空';
        }
        if (!attrs.email) {
            errors.email = '邮箱不能为空';
        }
        if (attrs.age < 0) {
            errors.age = '年龄不能为负数';
        }
        
        if (Object.keys(errors).length > 0) {
            return errors;
        }
    }
});

var UserView = Backbone.View.extend({
    initialize: function() {
        this.listenTo(this.model, 'invalid', this.showErrors);
    },
    
    showErrors: function(model, errors) {
        this.clearErrors();
        
        for (var field in errors) {
            this.$('#' + field).addClass('error');
            this.$('#' + field + '-error').text(errors[field]);
        }
    },
    
    clearErrors: function() {
        this.$('.error').removeClass('error');
        this.$('.error-message').text('');
    }
});

六、高级验证 #

6.1 条件验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (attrs.type === 'company') {
            if (!attrs.companyName) {
                return '公司名称不能为空';
            }
            if (!attrs.businessLicense) {
                return '营业执照号不能为空';
            }
        } else if (attrs.type === 'personal') {
            if (!attrs.idCard) {
                return '身份证号不能为空';
            }
        }
    }
});

6.2 异步验证 #

javascript
var User = Backbone.Model.extend({
    validate: function(attrs) {
        if (attrs.username) {
            var self = this;
            $.ajax({
                url: '/api/check-username',
                data: { username: attrs.username },
                async: false,
                success: function(response) {
                    if (response.exists) {
                        self.validationError = '用户名已存在';
                    }
                }
            });
            
            if (this.validationError) {
                return this.validationError;
            }
        }
    }
});

6.3 验证器模式 #

javascript
var Validators = {
    required: function(value, message) {
        if (!value || value.trim() === '') {
            return message || '此字段必填';
        }
    },
    
    email: function(value, message) {
        if (value && !value.match(/^[\w-]+@[\w-]+\.[a-z]+$/i)) {
            return message || '邮箱格式不正确';
        }
    },
    
    minLength: function(value, length, message) {
        if (value && value.length < length) {
            return message || '长度至少' + length + '个字符';
        }
    },
    
    maxLength: function(value, length, message) {
        if (value && value.length > length) {
            return message || '长度不能超过' + length + '个字符';
        }
    },
    
    range: function(value, min, max, message) {
        if (value !== undefined && (value < min || value > max)) {
            return message || '值必须在' + min + '到' + max + '之间';
        }
    }
};

var User = Backbone.Model.extend({
    validators: {
        name: [
            { validator: 'required', message: '姓名不能为空' },
            { validator: 'minLength', args: [2], message: '姓名至少2个字符' }
        ],
        email: [
            { validator: 'required', message: '邮箱不能为空' },
            { validator: 'email', message: '邮箱格式不正确' }
        ],
        age: [
            { validator: 'range', args: [0, 150], message: '年龄必须在0-150之间' }
        ]
    },
    
    validate: function(attrs) {
        var errors = {};
        
        for (var field in this.validators) {
            var rules = this.validators[field];
            var value = attrs[field];
            
            for (var i = 0; i < rules.length; i++) {
                var rule = rules[i];
                var validator = Validators[rule.validator];
                var args = [value].concat(rule.args || []);
                args.push(rule.message);
                
                var error = validator.apply(null, args);
                if (error) {
                    errors[field] = error;
                    break;
                }
            }
        }
        
        if (Object.keys(errors).length > 0) {
            return errors;
        }
    }
});

七、实用示例 #

7.1 注册表单验证 #

javascript
var Registration = Backbone.Model.extend({
    validate: function(attrs) {
        var errors = {};
        
        if (!attrs.username || attrs.username.length < 3) {
            errors.username = '用户名至少3个字符';
        }
        
        if (!attrs.email || !attrs.email.match(/^[\w-]+@[\w-]+\.[a-z]+$/i)) {
            errors.email = '请输入有效的邮箱地址';
        }
        
        if (!attrs.password || attrs.password.length < 6) {
            errors.password = '密码至少6个字符';
        }
        
        if (attrs.password !== attrs.confirmPassword) {
            errors.confirmPassword = '两次密码输入不一致';
        }
        
        if (!attrs.agree) {
            errors.agree = '请同意用户协议';
        }
        
        if (Object.keys(errors).length > 0) {
            return errors;
        }
    }
});

7.2 产品验证 #

javascript
var Product = Backbone.Model.extend({
    validate: function(attrs) {
        var errors = {};
        
        if (!attrs.name || attrs.name.trim() === '') {
            errors.name = '产品名称不能为空';
        }
        
        if (attrs.price === undefined || attrs.price < 0) {
            errors.price = '价格必须大于等于0';
        }
        
        if (attrs.stock !== undefined && attrs.stock < 0) {
            errors.stock = '库存不能为负数';
        }
        
        if (attrs.category && !this.isValidCategory(attrs.category)) {
            errors.category = '无效的产品分类';
        }
        
        if (Object.keys(errors).length > 0) {
            return errors;
        }
    },
    
    isValidCategory: function(category) {
        var validCategories = ['electronics', 'clothing', 'food', 'books'];
        return validCategories.indexOf(category) !== -1;
    }
});

八、总结 #

8.1 验证要点 #

要点 说明
validate方法 返回错误信息表示验证失败
验证时机 set和save时自动验证
invalid事件 验证失败时触发
validationError 存储最后一次验证错误

8.2 最佳实践 #

  1. 返回结构化的错误信息
  2. 在视图中统一处理验证错误
  3. 提供清晰的错误提示
  4. 验证规则可配置
  5. 支持多种验证类型
最后更新:2026-03-28