说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

【前端框架】Backbone.js在大型单页面应用中的应用实践

HTML文档下载 WORD文档下载 PDF文档下载
本文选自《程序员》2013年3月刊,全面介绍了轻量JavaScript MVC框架Backbone.js,并分享了豌豆荚PC客户端借助Backbone.js在开发大型单页面应用上做出的大量实践。

Backbone.js是什么?

Backbone.js是一个JavaScript MVC框架,提供了良好的代码组织能力,可以方便地将应用程序解耦成可以复用的部分,为建立大型的单页面应用提供框架支持,目前的版本是0.9.10(注:现在已到1.2.1版本)。通过将应用程序分解成MVC模式中不同职责的模块,带来了以下几点好处。

  1. 降低维护成本。数据、控制器、视图的更新都是独立进行的,互相之间都是松散耦合的。
  2. 解耦数据和视图,便于直接对业务逻辑进行单元测试。
  3. 便于团队合作,负责UI开发和业务逻辑开发的工程师可以分工并行工作。对于Web前端开发来讲,负责HTML模板和CSS的界面工程师和负责业务逻辑的JavaScript工程师可以协同工作。

Backbone.js算是比较轻量的MVC框架,所谓轻量,是说它只关注一个框架应该关注的最基本的事情——如何给应用分层、如何组织各种功能的代码。至于在实际开发中需要用到的Utils或UI组件,Backbone.js则没有提供任何支持。但Backbone.js所依赖的Underscore.js是一个功能比较全面的非侵入式工具函数类库,算是在Utils方面的一个补充。

轻量并不意味着功能薄弱。首先,Backbone.js的精髓是它定义前端MVC的方式和编码哲学,并依据这些规定了如何去给代码分层,因此Backbone.js能够让前端工程在可维护性和扩展性上都得到质的提升;同时,由于其良好且易于理解的结构,各个模块之间都是松散耦合的,虽然目前官方并没有提供根据实际需求build文件的功能,但如果你愿意,完全可以自己手工删掉源码中的Bakcbone.View只使用Model和Collection;最后,Backbone.js的任何一个部分都是非常容易扩展的。因此,Backbone.js的功能实际上非常强大的。下面将介绍Backbone.js的主要组件(架构如图1所示)。


图1 架构图

Backbone.Events和Backbone.Sync

Backbone.Events和Backbone.Sync两个组件是Backbone.js异步通信、事件驱动的编程模型的基础。

Backbone.js中所有的组件都通过_.extend()的方法“继承”了Backbone.Events所提供的功能,可以维护一套自己的事件订阅和回调列表。通过Events.on(event,[callback],[context])和Events.off([event], [callback], [context])两个方法来实现对事件的订阅和取消订阅。


早期版本中的事件订阅和取消订阅是通过Events.bind()和Events.unbind()两个方法实现的,目前的版本中还保留了这两个别名方法,但不推荐使用。

Backbone.js的所有组件都有一些内置的事件,可以查阅官方文档。除了预置事件外,通过Events.trigger(event,[*args])方法也可以方便地触发自定义事件。


从0.9版开始,Backbone.Events提供了Events.listenTo(other,event,callback)和Events.stopListening([other],[event],[callback])两个新方法来通过另外一种形式实现事件的订阅和取消订阅。

与on()和of f()不同,这种方式将监听的主动权转换了。举个例子来说:有对象A和对象B,B.on('someThingHappened',A.doSomeThing)是当对象BsomeThingHappened时候知对象A去doSomeThing;而A.listenTo(B,'someThingHappend',A.doSomeThing)是对象A主动去盯着对象B,当它someThingHappend的时候去doSomeThing。

第二种方式最大的意义是变被动为主动,从而实现了IoC(Inversionofcontrol,控制反转)。监听者和被监听者之间没有了耦合,只要被监听的对象能够抛出指定的事件,就可以和监听者组合在一起,甚至不需要去关心被监听对象的类型,这对代码的复用和行为抽象有很大的帮助。在测试层面,可以轻易地把被监听对象换成mock的测试代码来模拟真实情况。

Backbone.Sync则将同服务器的通信封装了起来,当Collection和Model需要和服务器通信交换数据时,会去调用Backbone.Sync中对应的方法并发送请求,如果服务器端支持RESTfulAPI就可以将整个通信过程描述得非常优雅并易于扩展。Sync的实现可以是jQuery.ajax()的封装,也可以是其他的类库提供的异步通信工具的封装。

Backbone.Model和Backbone.Collection

Backbone.js中的Model和Collection共同构成了MVC中的M层。Model的本质就是一组以keyvalue形式保存的数据,可以通过Backbone.Model.extend()来定义自己的Model。


上面的示例代码中defaults属性定义了一组默认值,当Model初始化时,如果没有指定defualts中所定义的属性的值,就会用默认值来填充Model;initialize()方法会在Model被实例化时调用,用来进行一些初始化的操作;validate()方法会在Model的save()或set()方法被调用时执行,可以根据具体需求进行扩展。

通过Model的getters和setters可以实现对Model中属性的读写,并且当set()方法被调用时,Model会将属性变化的事件广播给所有订阅者(通常是视图),驱动视图重新渲染或其他关联的Model的数据更新。

Collection是一组Model的集合,通过Collection可以将一组数据结构相同的Model有序地组织在一起,进行批量操作和管理等。同时,Collection代理了Undersore.js中众多用来操作Collection的工具方法,例如find、filter、map等。

Model和Collection都可以通过RESTfulAPI同服务器进行数据交换。

Backbone.View

Backbone.View是基于Backbone.js开发的Web App中的核心部分,负责用户交互事件的捕捉和处理、把用户输入导向Model或Collection、渲染视图、操作DOM等。Backbone.js的内部实现依赖$变量,因此DOM操作的库可以在jQuery、Zepto或Ender等中选择。从目前的情况来看,在桌面浏览器中,Sizzle.js(jQuer y所使用的SelectorEngine)的性能还是甩开Zepto几条街的,因此面向桌面浏览器的开发还是推荐使用jQuery,移动端考虑到文件体积等因素推荐使用Zepto。

对于一个Backbone.View来讲,最重要的就是$el属性,$el是一个jQuery对象(取决于所采用的DOM操作类库),是一个视图的最外层容器。容器所采用的HTML标签可以通过tagName属性来指定,可以是ul也可以是header或其他任何标签,默认情况下是div。

容器内部的DOM渲染可以通过模板引擎来完成。Underscore.js本身提供了一个_.template()的方法,因此Backbone.js不需要额外的模板引擎支持。当然,如果有特殊的需求,例如和后端共用模板文件,也可以选用Mustache等其他的模板引擎。


这样一个View就被渲染到界面上了。上面的代码中还监听了Model的change事件,当数据发生变化时,驱动视图重新渲染。当Model的数据比较丰富时,只有一个属性变化就重新渲染整个视图显然会带来性能上的隐患,因此这里的最佳实践就是把render的过程break-down成粒度更细的片段。


值得注意的是,当一个View不再被需要时,一定要记得销毁,除了销毁DOM对象外,也要销毁所有的事件监听器。在只有Events.on()和Events.off()的年代,由于销毁View时需要逐一取消订阅所有的消息,经常由于忘记解除某个绑定导致产生被称为GhostView的垃圾对象,既无法被释放也无法被回收。这也是Events.listentTo()方法带来的另外一个好处——只要调用Events.stopListening()方法即可将此对象的所有事件监听器销毁。

所有的DOM事件也是通过$el来代理的,在Backbone.View中可以通过以下方法来方便地管理DOM事件。


Backbone.js的内部实现里,在View的构造方法中调用View. initialize( )后将继续调用View.delegateEvents()方法,这个方法将解析events属性所定义的事件和回调列表,并将全部事件代理到$el对象上。由于使用的是事件代理,某些不支持冒泡的DOM事件则必须另外监听,如滚动条事件。


Backbone.Router和Backbone.History

Router是用来在URLHash和特定的动作或视图之间做映射的。


最后一句History.start()是告诉Backbone.js开始对URLHash的变化进行监视,也可以随时通过History.stop()来停止监视。同时,如果目标平台是支持HTML5Histor yAPI的,那么在start时传入{pushstate:true}的参数,就可以去掉URL中的#字符,对SEO有一定帮助。

Backbone.js的适用场景

经常能在各种场合听到前端工程师们讨论“你们的XXX是用什么做的啊?”“为什么不用XXX啊?”这样的问题。前端的类库和框架林林总总,算在一起数量没有一千也有五百,因此在面对一个新项目时难免会产生选择恐惧症。

但说到底,技术方案都是由需求决定的,任何一个类库或框架都有其适用范围和最佳的使用场景,Backbone.js也不例外。Backbone.js的最佳使用场景是大型的单页面应用:通过RESTfulAPI同服务器通信,然后根据数据的变化来驱动视图重新渲染,整个程序建立在异步通信和事件驱动的编程模型之上。

单页面应用给用户带来的使用体验是沉浸式的、相对重型的,对于普通的Web Page和数据相对稳定、视图不需要频繁重新渲染的场景来讲,Backbone.js显然就没有用武之地了。

Backbone.js和MV*不得不说的那点事儿

四十年前,Trygve Reenskaug基于Smartalk语言设计出了MVC模式,经过几十年的发展,MVC出现了众多的衍生。而我们今日说的MVC在不加特殊说明的情况下,通常指的是在服务器端Web应用开发中大量使用的WebMVC。

对于典型MVC模式来讲,View是无法直接获得用户输入的,而Controller则是用户输入和View之间的桥梁。但在浏览器中,View层的载体是HTML,用户输入和交互行为都是基于HTML的,对事件的捕捉、输入都由浏览器代劳了,并且输入会首先进入View层,因此对于前端开发来讲,严格意义的MVC是无法实现的。

因此,包括Backbone.js在内的JavaScript MVC框架的实现并没有严格遵循MVC的定义,Controller的部分职责被转移到了View层。Backbone.js对于前端MVC的定义非常易于理解,但对于没接触过MVC模式的同学来说在初期会有一些迷惑,原因是Backbone.js核心组件的命名。Backbone.js的核心组件包括Backbone.Collection、Backbone.Model、Backbone.View和Backbone.Router,而在早期的版本中,Backbone.Router组件的名称是Backbone.Controller,这很容易让人直接将其和MVC三层中的C层联系起来。但事实上,Backbone.Controller的作用是根据URLHash来在对应的行为和事件中做路由的,其功能同MVC中的C相比要简单很多,因此在0.5前后Controller改名叫做Router了。从实体代码的角度看,View层其实是模板代码和Backbone.View中部分代码的综合体,而Backbone.View中剩余的部分才是MVC模式中Controller的概念,负责操作View以及数据在View和Model层中的流转。

对于前端开发来说,用户直接面对的一层并不是Controller而是View,用户输入也会首先进入View,因而用MVP和MVVM模式来描述架构更加合理。相比AngularJS等框架,Backbone.js的模式显然更易于理解,学习曲线比较平缓,因为它并没有引入过多的需要重新认知和理解的新概念而是尽量在靠近传统的MVC模式,对于以前接触过MVC模式开发的同学来说非常容易上手,而AngularJS中的Directive等概念还是需要一定认知成本的。

但如果从架构的角度讨论,AngularJS其实是更为纯粹并更接近严格意义上的MVC模式。为了把View的功能提升到一个应有的高度,引入了Directive的概念,通过扩展HTML标签和自定义属性来描述View,在Directive中来解析这些扩展出来的内容,理解成本和代码的复杂程度都有所提高,但View层功能则得到了质的提升。

反观Backbone.js,并没有在前端开发中真正的View的载体HTML上做太多文章,即便采用模板引擎也仅仅是把数据和HTML组合起来。但得益于其强大的扩展性,可以很容易将Knockout等Data-binding框架集成进来,从而实现MVVM的架构和分层。例如前文提到将render的过程Breakdown就完全可以用Data-binding来取代,省掉了手工更新DOM的烦琐。

Backbone.js在豌豆荚PC客户端2.0中的实践

豌豆荚PC客户端2.0的UI是完全建立在Web前端基础上的。借助Backbone.js,豌豆荚PC客户端在开发大型单页面应用中做了大量的实践。通过在客户端中捆绑一个Webkit引擎并对其进行扩展,使得跑在Webview中的前端代码跳出沙箱的限制,可以操作文件系统并调用系统API,以此来进行桌面应用的开发。这样做的好处有以下几点。

  1. 极大提高开发效率:桌面应用的开发中,UI开发效率一直很低,但借助HTML5和CSS3的新功能,Web前端可以轻易地做出精细程度和交互体验都不输桌面应用的UI,但开发效率和维护成本都极大降低。
  2. 易于跨平台:UI依赖于Webkit引擎,而Webkit引擎本身是跨平台的,因此可以很容易地移植到Web上或其他桌面平台。
  3. 快速迭代和改进:由于维护和扩展成本的下降,可以快速的将原型设计产品化并进行验证,提高产品迭代和改进的速度。

但与此同时,用Web前端技术开发桌面应用也要面临巨大的挑战。首先就是内存消耗。用户在使用浏览器和使用桌面应用时的心理预期是不一样的,即使一段有内存泄漏问题的前端代码跑在浏览器里,当出现运行缓慢时大不了一下刷新页面,但对桌面应用来讲,大多数情况下没有刷新Webview的机会,因此必须对内存实现更精细的控制。由于Webview本身依赖于GC,前端无法主动管理和回收内存,因此必须借助ChromeDeveloper tools中的Profiles等工具查找出现内存泄漏的地方从而进行改善,这也依赖于大量的经验积累。

其次是运行速度和界面响应速度。由于Webview是单线程的,但单页面应用面临的是数百倍于Web Page的业务逻辑复杂度,同时业务逻辑的执行和UI共用一个线程,如何优化执行速度也是一个很大的挑战。虽然目前WebUI的界面流畅程度无法完全达到桌面应用的水平,但依然是很有竞争力且有提高空间的。(责编:陈秋歌)

作者简介

赵望野:豌豆荚前端团队负责人。

本文选自《程序员》2013年3月刊。2000年创刊至今所有文章目录请查看程序员封面秀。欢迎订阅程序员电子版(含iPad版、Android版、PDF版)。

欢迎加入CSDN前端交流群:218126086,进行前端技术交流。  

DRM你又赢了:其API纳入HTML5标准 豌豆荚王俊煜:洗白白其实是一个计划外的产物 抢Google等巨头生意,纽约大学小伙挖掘并出售自己数据 noBackend:前端优先的开发模式 C、CPP const 详解 三星5G技术获得突破性进展 未来传输速度可达10Gbps 5月14日:1984年美国社交网站Facebook创办人Mark Zuckerberg出生 Chartkick:一行代码即可绘制出漂亮的图表 用友UAP将打造开放平台 建应用商店销售企业级应用 共享软件营销:如何将你的产品推向海外 谷歌使用Debian Linux作为GCE的默认操作系统 大数据?别唬人了!我们真的需要盲目烧钱追求大数据吗? 日本社交手游公司GREE巨星陨落 中国区全员被裁 战歌:85后手游开发团队背后的故事 分秒钟做款App:细数国内外在线DIY应用网站 指点传媒CEO专访:紧握二三线市场用户命脉 专访Waygo创始人:白切鸡怎么翻译? 探究共享软件海外营销新策略(西安站) 讲师秀8:车音网沈康麒和他的智能驾车服务 数据可视化独领风骚:看这6家初创公司如何玩转大数据? 最新研究显示:IE10防恶意软件能力比Chrome等浏览器都要强 Windows Blue正式定名Windows 8.1 对Windows 8及RT用户免费 Testin云测试破1000万次 自动化测试成趋势 接手Android后首次接受媒体采访 Sundar Pichai谈Android未来 C语言实现二分法查找 让开发者高效编程的10个新框架 Google Analytics中显示访客停留时间为“0”的秘密 大数据之惑 麻省理工三位物理学家自建数据库服务Cloudant 融资1200万美元 网页浏览器进入再造新时代 AMD揭露HSA运算架构新技术hUMA细节 怎样释放工具条? fanqinshan@china.com 那位大哥帮忙改一下,显示 DLL 可导出的函数? 请问,在vc的api CReateFile中如何设置。可使读硬盘的速度达到最高? 自己的贴子可否删除?怎么删? MUGEN 人物预览利器 菜鸟请教:1delphi有什么好处——与别的语言相比 关于组播问题,急急急! 求:IE5.50使用代理服务器/不使用代理服务器 之间进行切换的代码 送分20!!! 周五不好干什么,没女朋友,编程水平又菜,学校又不好,文凭又差自修室又不开放??? 各位同志,我有个问题搞不清楚.请不吝赐教. 如何在局域网中实现共享只读文件(没有共享密码)的写操作 杭州的朋友,请问........ 如何在explore显示的网页的文本框中设置正文??? 谁能告诉我学软件工程到底有多少用? 这里有20分,谁要? 大家来看,这就是别人对中国程序员的看法! 寻找图书~? 获取NT管理员权限创意 ?寻找图书~? 为什么运行时正常的程序,编译成可执行文件后运行过程中出错... 怎样填充按钮客户区? 寻找图书~?? 请问如果数据量很大,比如大型企业的即时采集数据,应该用什么数据库系统处理呢?oracle,sysbase,db2,sqlserver还是cobol?? 这么简单的问题,难道就没有人能帮我了吗?!太遗憾了! 敬一杯“情人醉”给MM_mimi(人淡如菊),别人不要进来呀! 谁有过同样的遭遇??? 长整数除法的算法?? 关于Java Plug-in和Applet的一个问题 请求推荐有关SDK的好书!!!!!!! 我受不了了 个人主页链接如何让鼠标变成手形?谢谢! 老话题:三层结构中blob字段的存取出问题了? run-time类是怎么回事? 当多个程序对同一个文件进行读写操作,如何防止同时写入造成混乱? 求银行家算法的c程序! 请问W2K下面鼠标阴影的效果是用哪个API实现的? 我提议有编程经验的朋友们,一起来研究一种真正符合我们实际开发工作的模板 我用上kylix了!!!!!!! 请教,关于NT域用户的问题!NT账号,怎么设定唯一性呀?比如有aa的用户,我用aa登陆后,别人不能用aa同时登陆了?应该在那儿设置呀? Mfm1992?是个什么东西呀! 如何在informix/esql c中使用大事物? 如何保存收下來的email呢???!!!!! 关于一个网站建设的问题,望高手指点。 求救;如何建立一个登陆页面? 谁能给出一个远程访问互联网ACCESS数据库的经典代码?有28分送上! 怎样将-个十六进制数转换为十进制? 如何将java程序编译成dll文件 cannot focus a disabled or invisible window 一表我想通过文本框来增加记录,为何不能发送到表中,不要告诉我是没有post的原因。 苏教版六年级上册数学评价手册答案(是期中测试) 人教版六年级下册数学同步解析与测评35页4题答案一种农药用药液和水按照1:1200配制而成.1.如果现在有1.5kg的药液,能配制这种农药多少kg?2.要配制这种农药480.4kg,需要药液与水各多少kg?要准 人教版数学六年级上册配套练习册71页答案. 苏教版六年级上册数学评价手册期末测试答案(第一大题计算题除外)求苏教版六年级上册数学评价手册期末测试的答案,今天就必须写完, 小学六年级数学下册配套练习与检测第38页思维拓展第9题解法在长度为1米的线段上任取10个点,至少有两个点,他们之间的距离不大于1/9米.为什么? 五年级下册人教版数学练习册第四页第五大题的第二小题怎么写 苏教版六年级上册数学评价手册第6,7单元测试答案问题是:钢笔的单价是铅笔的5倍,买2支钢笔和5支铅笔共用15元.一支钢笔和一支铅笔个多少元? 我不要方程解的,我要的是普通的算式加答案! 小学六年级数学练习册第25页第6题怎么做? 五年级下册数学练习册点中典12页 五大题1小题 苏教版六年级下册数学评价手册期中测试答案 急! 小学六年级上人教版数学练习册24页第一题2X( )=8分之9X( )=( )x16分之13分之2+( )=( )—8分之7=( )x100 五年级下册数学练习册56页五大题 六年级下数学练习册习题p41~42答案六年级下数学练习册习题6.10答案谢谢 六年级上数学练习册4.3习题的答案帮帮忙啊~ ~ 五年级下册数学练习册答案55-58 六年级上册数学《补充习题》第88页第四题的答案!(苏教版)冬冬倒了一杯纯牛奶,先喝了50%,加满水后,又喝了50%,再加满水喝完.冬冬喝的牛奶多还是水多?( 我在网上查过了,有些人算出来是一 六年级下册数学练习与测试期中测试答案P41~44非常急 急 急 急 急 知道的赶紧给答案 感激他 十八辈祖宗 六年级数学练习册答案66页第五题的第二小题 苏教版六年级上册数学补充习题上的一道题,在线等苏教版六年级上册数学补充习题上第47页最后一题,要算式,谢谢,另外还有( )的期待和( )搏击,请解答有一个两位数,个位上的数是十 六年级上数学练习册p41~42页的9~14题急~!答案一定要正确!九年制义务教育数学练习部分六年级第一学期(试用本)上海的! 苏教版六年级下册数学练习册48-50页的答案 人教版小学六年级上册数学补充习题89、90页答案 苏教版六年级上册数学期中测试题 六年级上册数学练习册人教版51、52的答案 六年级上册数学练习与测试第16业第3题怎么写?一个长方体的水槽,从里面量长24dm,宽5dm深8dm。如果将360升水倒入水槽,水槽中深多少分米?(用方程解) 苏教版六年级上册数学期中试卷和苏教版六年级上册语文期中试卷 52谢谢你的关注,其实已经不需要了 六年级上册数学练习与测试的第8页的答案问题是:有两块布,一块148米一块100米.两块布料减去同样长的米数,第一块的米数是第二块的米数的3倍,两块布料各长多少米?大哥大姐千万不可马虎小 苏教版六年级数学上册期中试卷 数学练习册5年级下册人教版52 苏教版六年级上册数学练习与测试第20页第4题怎么写找一个有盖的长方形纸盒先量出它的长宽高各是多少厘米,再算出它的表面积把盒盖打开,观察每两个面和接头处,想一想,纸盒的表面积和纸 2011六年级上册数学人教期中测试题和答案漳州市的啊!下午就要考了, 人教版小学英语六年级下册课堂作业本50、51、52、53页答案没有答案的就不要来,不要空说,我要答案,高悬赏 有两块布料,第一块148米,第二块100米.两块布料各剪去同样长的一段后,第一块剩下的长度是第二块的3倍.两块布料剩下多少米? 苏教版六年级下册数学小练习册第八页答案 第25页 急 给我发来可不 小学数学练习与测试六年级下答案66-67页 苏教版六年级下册数学配套练习册16,17页答案啊啊啊跪求了! 10 嘉兴市小学六年级数学(上册)期末检测卷(2013.1) 六年级数学苏教版练习册下册105至116页答案,后天老师让我讲课,怕讲错啊,小生必定大恩不言谢啊! 苏教版六年级上册数学练习册50到57页答案 2013年小学六年级数学上册期末综合检测答案 苏教版六年级数学上册练习册第58页答案 六年级上册数学练习册【66页】折扣——能力扩展体育服装专卖店根据市场需要,决定调整某款运动套装的售价.调整的方案如下:运动衣打八折,运动裤提价20%.这样每件运动衣和每条运动裤 小学数学六年级上册期末检测卷(2010.1)书店有一套科普丛书,原价96元,先按七折出售,买一套可以便宜多少元?如果买6套,360元钱够吗? 苏教版六年级数学练习册63—64页答案苏教版!急、急、急……六(2)班的男生人数是女生的8/9(九分之八),转进1名女生后,男生人数是女生的6/7(七分之六).六(2)班原来男、女生各有多 1、一根长方体木料的横截面是边长为5厘米的正方形。已知该木料长120厘米,这跟木料的体积是多少立方厘米?2、一个长5米宽4、5米高2米的无盖木箱,容积是多少(木板厚度忽律不计)3、 黄冈小状元六年级语文上册期末测试(1)和(2)答案汉语拼音 和那个汉字 快,有急用 果园里桃树和杏树共180棵,桃树的棵树是杏树的3倍,桃树和杏树各有多少课?甲、乙、丙三个数的和是490,甲是丙的4倍,乙是丙的2倍.甲、乙、丙各是多少?三个连续自然数的和是246,求这3个数甲、 黄冈小状元达标卷(R)六年级数学(上)期末检测(一)期末检测(二)答案 (全部)...急.快 人教版六年级数学练习册63.64. 数学练习册五年级上册76页答案 求人教版数学六年级上册黄冈小状元期末检测(二)答案!在一个直径为3米的圆形花坛四周,修一条宽1米的灌木丛带,这条灌木丛的面积为多少?乐乐从甲地步行去异地,第一小时行了全程的四分 人教版 同步解析与测评下册 (检查孩子作业) 五年级上册数学练习册60页 黄冈小状元达标卷六年级上册数学答案期末检测二第五题 小学六年级下册人教版数学检测第60页准确一点的答案! 一个工地用汽车运土,每辆车运X吨.一天上午运了6车,下午运了5车.这一天共运土( )吨,上午比下午多运( )吨.商场上午卖出电视机10台,下午卖出去7台,每台电视机a元.全天买电视机一共收入
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn