《BackboneJS框架的技巧及模式》(3)

简介: 《BackboneJS框架的技巧及模式》(3)本文紧接第二部分:《BackboneJS框架的技巧及模式(2)》作者:chszs,转载需注明。

《BackboneJS框架的技巧及模式》(3)

本文紧接第二部分:《BackboneJS框架的技巧及模式(2)》

作者:chszs,转载需注明。博客主页:http://blog.csdn.net/chszs

四、页面部分刷新

当第一次使用Backbone.js开发应用时,典型的视图结构是像这样的:

var View = Backbone.View.extend({
    initialize: function(options) {
        this.model.on('change', this.render, this);
    },

    template: _.template($(‘#template’).html()),

    render: function() {
        this.$el.html(template(this.model.toJSON());
        $(‘#a’, this.$el).html(this.model.get(‘a’));
        $(‘#b’, this.$el).html(this.model.get(‘b’));
    }
});

在这里,任何对模型的改变都会触发整个视图的完全刷新。我第一次用Backbone.js开发时,我是此模式的实践者。但随着视图代码的增长,我很快意识到,这种方式不利于维护或优化,因为模型的任一属性发生改变时,都会触发视图的完全刷新。

当我遇到这个问题,我快速用Google搜索了一下,看别人是怎么做的。结果找到了Ian Storm Taylor的博客,“分解Backbone.js渲染方法”,他在其中描述了在模型中监听单个的属性变化,然后仅仅重新渲染相对于视图中属性变化的那部分。Taylor也描述了返回对象的引用,以便单个渲染函数可以很容易的链接在一起。上面的例子现在就可以修改的易于维护、性能也更优。因为我们只需做视图的部分刷新。

var View = Backbone.View.extend({
    initialize: function(options) {
        this.model.on('change:a', this.renderA, this);
        this.model.on('change:b', this.renderB, this);
    },

    renderA: function() {
        $(‘#a’, this.$el).html(this.model.get(‘a’));
        return this;
    },

    renderB: function() {
        $(‘#b’, this.$el).html(this.model.get(‘b’));
        return this;
    },

    render: function() {
        this
            .renderA()
            .renderB();
    }
});

我要提醒一下:有不少插件,比如Backbone.StickIt和Backbone.ModelBinder,提供了模型属性与视图元素的键值绑定,这会让你省去编写很多样板代码的时间,所以,如果你需要实现比较复杂的表单字段,那么可以看看这些插件。

五、保持模型与视图无关

正如 Jeremy Ashkenas 在 Backbone.js的GitHub问题中所指出的,Backbone.js并没有实施数据模型与视图层之间的真正分隔,除非模型不是引用视图创建的。因为Backbone.js并没有实施任何关注点分隔,所以你应该将其分离吗?我和许多Backbone.js开发人员,如Oz Katz和Dayal,都相信答案毫无疑问是yes:模型与集合,也就是数据层,应该彻底的与绑定它们的视图无关,保持一个清晰的关注点分离。如果你没有遵循关注点分离,你的代码库会很快变成意大利面条式的代码,而实际上是没有人喜欢意大利面条式的代码。


保持模型与视图无关将会帮助你预防意大利面条式的代码,而没有人喜欢意大利面条式的代码!

保持数据层完全与视图层无关,这会使你创建出更具模块化、可重用和可维护的代码库。你可以很容易地在应用程序各个地方重用和扩展模型与集合,而无需考虑它们所绑定的视图。遵循此模式使对项目不熟悉的新手也能迅速深入到代码库,因为他们会确切地知道哪里发生了渲染,业务逻辑存在于哪里。

这个模式也实现了单一职责原则——它规定了每个类应该具有单个职责,而且它的职责应该封装在类中,因为模型与集合负责处理数据,而视图则负责处理渲染。

六、路由的参数映射

此模式的最佳演示是理解整个例子。比如说对搜索页的结果进行排序,搜索页允许用户添加两个不同的过滤类型,foo和bar,每个类型代表不同的过滤规则。那么,你的URL结构应该是这样:

'search/:foo'
'search/:bar'
'search/:foo/:bar'

现在,所有路由都使用相同的视图和模型,这样大多数人都喜欢用同一个函数search()来实现。然而,你要是检查过Backbone.js代码的话,你会发现它里面没有任何参数映射的排序;这些参数只是从左至右依次传入函数。这样,为了能统一使用同一函数,你要停止创建不同的函数并正确地把参数映射到search()。

routes: {
    'search/:foo': 'searchFoo',
    'search/:bar': 'searchBar',
    'search/:foo/:bar': 'search'
},

search: function(foo, bar) {    
},

// I know this function will actually still map correctly, but for explanatory purposes, it's left in.
searchFoo: function(foo) {
    this.search(foo, undefined);
},

searchBar: function(bar) {
    this.search(undefined, bar);
},

如你所想,此模式可以使路由功能快速膨胀。当我第一次遇到这个问题时,我试图创建一些正则表达式解析实际的函数定义来实现参数的映射,当然这是可以工作的——但也是受约束的。因此,我放弃了我的这个想法(或许我仍然可以通过BackboneJS的插件来解决)。我进入GitHub的Issues页面,其中Ashkenas建议应该让所有的参数都映射到search函数。 

上面的代码现在可修改为如下的维护性更强的代码:

routes: {
    'base/:foo': 'search',
    'base/:bar': 'search',
    'base/:foo/:bar': 'search'
},

search: function() {
    var foo, bar, i;

    for(i = arguments.length - 1; i >= 0; i--) {

        if(arguments[i] === 'something to determine foo') {
            foo = arguments[i];
            continue;
        }
        else if(arguments[i] === 'something to determine bar') {
            bar = arguments[i];
            continue;
        }
    }
},

此模式可以显著减少路由的过度膨胀。但是,需注意到如果它不能识别参数,则它不会工作。比如,如果你有两个带ID的参数,如模式XXXX-XXXX,那么你不能区分哪个ID是对哪个参数的响应。

七、model.fetch() 不会清除你的模型

通常它会对那些Backbone.js新手造成困扰:model.fetch()不会清除你的模型,而是继承模型的属性。因此,假如模型具有属性x、y和z,你取回y和z,那么属性x仍然是模型中的那个x,而属性y和z会被更新。下面的例子说明了这一点:

var Model = Backbone.Model.extend({
    defaults: {
        x: 1,
        y: 1,
        z: 1
    }
});
var model = new Model();
/* model.attributes yields
{
    x: 1,
    y: 1,
    z: 1
} */
model.fetch();
/* let’s assume that the endpoint returns this
{
    y: 2,
    z: 2,
} */
/* model.attributes now yields
{
    x: 1,
    y: 2,
    z: 2
} */


目录
相关文章
|
存储 物联网 测试技术
改变LoRA的初始化方式,北大新方法PiSSA显著提升微调效果
【4月更文挑战第23天】北京大学团队提出的新方法PiSSA,基于SVD进行参数高效微调,降低计算成本。PiSSA通过聚焦低秩矩阵训练,实现与全参数微调相当甚至更好的性能,快于LoRA收敛且在五个基准测试中胜出。PiSSA继承LoRA的参数效率,初始化仅需几秒,适合快速适应不同下游任务。尽管有潜力,但其在更大模型和任务上的效果,以及与LoRA结合的可能优化,仍是未来研究课题。[链接](https://arxiv.org/pdf/2404.02948.pdf)
499 7
|
人工智能 数据中心 芯片
液冷是大模型对算力需求的必然选择?|英伟达 GTC 2024六大亮点
在这个以高性能计算和大模型推动未来通用人工智能时代,算力已成为科技发展的隐形支柱。本文将重点探讨算力的演进,深入分析在不同领域中算力如何成为推动进步的基石;着眼于液冷如何突破算力瓶颈成为引领未来的先锋,对液冷散热的三种方式(冷板式、浸没式和喷淋式)做了详细的对比分析、成本测算和市场空间预测。并为您提供一份“实用教程”,指导如何将普通服务器改装为液冷服务器,以应对越来越复杂的计算需求。
|
11月前
|
人工智能 Devops 数据挖掘
IT项目经理的职责、能力和工具:让项目管理更高效
3分钟了解IT项目经理的主要职责和需要具备哪些专业技能。
513 5
IT项目经理的职责、能力和工具:让项目管理更高效
|
10月前
|
Java 编译器 Spring
JAVA中切面的使用
AOP(面向切面编程)通过切面、通知、切入点和连接点实现模块化关注点分离。Spring AOP基于代理模式,使用JDK动态代理或CGLIB代理;AspectJ采用字节码增强,在编译或类加载时织入切面逻辑,性能更高。示例代码展示了如何在方法调用前后插入日志记录等操作。
|
11月前
|
关系型数据库 分布式数据库 数据库
人脸对比
【7月更文挑战第31天】人脸对比
658 2
|
机器学习/深度学习 供应链 监控
ERP系统中的供应链风险识别与应对策略解析
【7月更文挑战第25天】 ERP系统中的供应链风险识别与应对策略解析
1038 1
|
机器学习/深度学习 开发框架 量子技术
未来编程:量子计算与量子编程语言的兴起
【5月更文挑战第27天】 在探索计算前沿的旅程中,量子计算以其对传统计算概念的颠覆性挑战而脱颖而出。本文旨在探讨量子计算的基础原理以及正在发展的量子编程语言,这些语言预计将成为未来软件开发的重要工具。我们将简要回顾量子位(qubit)和量子门的逻辑,并分析几个量子编程语言的案例,如Q#、Qiskit和Quipper,以展示它们如何使程序员能够编写和测试量子算法。通过比较传统编程与量子编程的不同之处,本文揭示了量子计算对于未来技术革新的潜力以及它所面临的挑战。
|
数据采集 机器学习/深度学习 数据可视化
Jupyter Notebooks和IPython是交互式数据分析利器,提升效率。Jupyter是开源Web应用
【7月更文挑战第5天】Jupyter Notebooks和IPython是交互式数据分析利器,提升效率。Jupyter是开源Web应用,支持多语言,结合IPython的交互式解释器,便于编程和科学计算。两者提供即时反馈、丰富库支持、跨语言功能及协作共享。基本流程包括:数据导入(使用Pandas)、预处理、分析(借助Pandas、NumPy、Matplotlib)、模型训练(如随机森林)和评估。
238 0
|
机器学习/深度学习 人工智能 自然语言处理
人工智能生成内容(AIGC)
人工智能生成内容(AIGC)
392 0