Backbone.js视图基础 #

一、什么是View #

View(视图)是Backbone.js中负责渲染UI和处理用户交互的组件。

1.1 View的作用 #

text
View职责
├── 渲染UI:将模型数据渲染为HTML
├── 事件处理:处理用户交互事件
├── 模型绑定:监听模型变化并更新UI
├── DOM操作:操作DOM元素
└── 子视图管理:管理子视图的生命周期

1.2 View的双重角色 #

Backbone的View同时承担视图和控制器的职责:

text
View = View(视图) + Controller(控制器)

视图职责:
├── 渲染HTML
├── 更新UI
└── 显示数据

控制器职责:
├── 处理用户输入
├── 更新模型
└── 协调视图和模型

二、创建View #

2.1 基本创建方式 #

javascript
var UserView = Backbone.View.extend({
    render: function() {
        this.$el.html('<h1>Hello Backbone!</h1>');
        return this;
    }
});

var view = new UserView();
view.render();

$('#app').html(view.el);

2.2 initialize方法 #

javascript
var UserView = Backbone.View.extend({
    initialize: function(options) {
        console.log('视图已创建');
        console.log('选项:', options);
        
        this.listenTo(this.model, 'change', this.render);
    }
});

var user = new Backbone.Model({ name: '张三' });
var view = new UserView({ model: user });

2.3 传递选项 #

javascript
var UserView = Backbone.View.extend({
    initialize: function(options) {
        this.customOption = options.customOption;
    }
});

var view = new UserView({
    model: user,
    collection: users,
    customOption: '自定义选项'
});

三、el属性 #

3.1 什么是el #

el 是视图的根DOM元素,所有视图操作都基于这个元素。

javascript
var view = new Backbone.View({
    el: '#app'
});

console.log(view.el);
console.log(view.$el);

3.2 el vs $el #

属性 类型 说明
el DOM Element 原生DOM元素
$el jQuery Object jQuery包装对象

3.3 使用现有元素 #

javascript
var UserView = Backbone.View.extend({
    el: '#app',
    
    render: function() {
        this.$el.html('<h1>Hello</h1>');
        return this;
    }
});

var view = new UserView();
view.render();

3.4 创建新元素 #

javascript
var UserView = Backbone.View.extend({
    tagName: 'div',
    className: 'user-item',
    id: 'user-1',
    attributes: {
        'data-role': 'user'
    },
    
    render: function() {
        this.$el.html('<span>用户信息</span>');
        return this;
    }
});

var view = new UserView();
console.log(view.el.outerHTML);

3.5 tagName、className、id #

javascript
var UserView = Backbone.View.extend({
    tagName: 'li',
    className: 'user-item active',
    id: 'user-item-1'
});

var view = new UserView();
console.log(view.el.outerHTML);

3.6 动态属性 #

javascript
var UserView = Backbone.View.extend({
    tagName: 'div',
    
    attributes: function() {
        return {
            'class': 'user user-' + this.model.id,
            'data-user-id': this.model.id
        };
    }
});

var user = new Backbone.Model({ id: 1, name: '张三' });
var view = new UserView({ model: user });
console.log(view.el.outerHTML);

四、render方法 #

4.1 基本渲染 #

javascript
var UserView = Backbone.View.extend({
    render: function() {
        this.$el.html('<h1>' + this.model.get('name') + '</h1>');
        return this;
    }
});

var user = new Backbone.Model({ name: '张三' });
var view = new UserView({ model: user });

$('#app').html(view.render().el);

4.2 返回this #

javascript
var UserView = Backbone.View.extend({
    render: function() {
        this.$el.html('<h1>Hello</h1>');
        return this;
    }
});

var view = new UserView();
view.render().$el.appendTo('#app');

4.3 使用模板 #

javascript
var UserView = Backbone.View.extend({
    template: _.template(
        '<div class="user">' +
        '  <h2><%= name %></h2>' +
        '  <p><%= email %></p>' +
        '</div>'
    ),
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

var user = new Backbone.Model({
    name: '张三',
    email: 'zhangsan@example.com'
});

var view = new UserView({ model: user });
view.render();

4.4 渲染集合 #

javascript
var UserListView = Backbone.View.extend({
    tagName: 'ul',
    
    render: function() {
        this.$el.empty();
        
        this.collection.each(function(user) {
            var userView = new UserView({ model: user });
            this.$el.append(userView.render().el);
        }, this);
        
        return this;
    }
});

五、$方法 #

5.1 查找元素 #

javascript
var UserView = Backbone.View.extend({
    template: _.template(
        '<div class="user">' +
        '  <h2 class="name"><%= name %></h2>' +
        '  <p class="email"><%= email %></p>' +
        '</div>'
    ),
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },
    
    updateName: function(name) {
        this.$('.name').text(name);
    },
    
    updateEmail: function(email) {
        this.$('.email').text(email);
    }
});

5.2 $ vs jQuery #

javascript
var UserView = Backbone.View.extend({
    el: '#app',
    
    render: function() {
        this.$el.html('<div class="item">Item</div>');
        
        var $item1 = this.$('.item');
        var $item2 = $('#app .item');
        
        console.log($item1.length);
        console.log($item2.length);
    }
});

六、视图选项 #

6.1 标准选项 #

javascript
var view = new Backbone.View({
    model: user,
    collection: users,
    el: '#app',
    id: 'my-view',
    className: 'my-class',
    tagName: 'div',
    attributes: { 'data-test': 'value' }
});

6.2 自定义选项 #

javascript
var UserView = Backbone.View.extend({
    initialize: function(options) {
        this.title = options.title || '默认标题';
        this.editable = options.editable !== false;
    }
});

var view = new UserView({
    model: user,
    title: '用户详情',
    editable: true
});

七、视图生命周期 #

7.1 创建阶段 #

javascript
var UserView = Backbone.View.extend({
    initialize: function() {
        console.log('1. 视图初始化');
        
        this.listenTo(this.model, 'change', this.render);
    },
    
    render: function() {
        console.log('2. 视图渲染');
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

7.2 销毁视图 #

javascript
var UserView = Backbone.View.extend({
    remove: function() {
        this.stopListening();
        this.undelegateEvents();
        this.$el.remove();
        return this;
    }
});

7.3 清理资源 #

javascript
var UserView = Backbone.View.extend({
    initialize: function() {
        this.childViews = [];
        this.listenTo(this.model, 'change', this.render);
    },
    
    remove: function() {
        this.childViews.forEach(function(child) {
            child.remove();
        });
        
        this.stopListening();
        this.undelegateEvents();
        Backbone.View.prototype.remove.call(this);
    }
});

八、实用示例 #

8.1 用户卡片视图 #

javascript
var UserCardView = Backbone.View.extend({
    tagName: 'div',
    className: 'user-card',
    
    template: _.template(
        '<div class="card">' +
        '  <img src="<%= avatar %>" class="avatar">' +
        '  <h3 class="name"><%= name %></h3>' +
        '  <p class="email"><%= email %></p>' +
        '  <button class="btn-edit">编辑</button>' +
        '</div>'
    ),
    
    events: {
        'click .btn-edit': 'edit'
    },
    
    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
    },
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },
    
    edit: function() {
        console.log('编辑用户:', this.model.get('name'));
    }
});

8.2 列表项视图 #

javascript
var TodoItemView = Backbone.View.extend({
    tagName: 'li',
    className: 'todo-item',
    
    template: _.template(
        '<input type="checkbox" <%= completed ? "checked" : "" %>>' +
        '<span class="title"><%= title %></span>' +
        '<button class="delete">×</button>'
    ),
    
    events: {
        'click input[type="checkbox"]': 'toggle',
        'click .delete': 'clear'
    },
    
    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'destroy', this.remove);
    },
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        this.$el.toggleClass('completed', this.model.get('completed'));
        return this;
    },
    
    toggle: function() {
        this.model.toggle();
    },
    
    clear: function() {
        this.model.destroy();
    }
});

8.3 表单视图 #

javascript
var UserFormView = Backbone.View.extend({
    tagName: 'form',
    className: 'user-form',
    
    template: _.template(
        '<div class="form-group">' +
        '  <label>姓名</label>' +
        '  <input type="text" name="name" value="<%= name %>">' +
        '</div>' +
        '<div class="form-group">' +
        '  <label>邮箱</label>' +
        '  <input type="email" name="email" value="<%= email %>">' +
        '</div>' +
        '<button type="submit">保存</button>'
    ),
    
    events: {
        'submit': 'submit',
        'input input': 'updateModel'
    },
    
    initialize: function() {
        this.listenTo(this.model, 'change', this.render);
    },
    
    render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },
    
    updateModel: function(e) {
        var $input = $(e.target);
        this.model.set($input.attr('name'), $input.val());
    },
    
    submit: function(e) {
        e.preventDefault();
        this.model.save();
    }
});

九、总结 #

9.1 View核心属性 #

属性 说明
el 根DOM元素
$el jQuery包装的根元素
model 关联的模型
collection 关联的集合
events 事件映射
template 模板函数

9.2 View核心方法 #

方法 说明
initialize 初始化方法
render 渲染方法
remove 移除视图
$(selector) 在视图内查找元素
delegateEvents 绑定事件
undelegateEvents 解绑事件

9.3 最佳实践 #

  1. 始终在 render 方法中返回 this
  2. 使用 listenTo 绑定模型事件
  3. remove 中清理资源
  4. 使用模板引擎分离HTML
  5. 保持视图职责单一
最后更新:2026-03-28