Backbone.js 实战详解:MVC 架构、零框架替代方案与 Model/View/Router 核心用法(基于 jstutorial 仓库)

📅 发布时间:2026/10/13 8:34:32
Backbone.js 实战详解:MVC 架构、零框架替代方案与 Model/View/Router 核心用法(基于 jstutorial 仓库)
教程文档【免费下载链接】jstutorialJavascript tutorial book项目地址https://gitcode.com/gh_mirrors/js/jstutorial点击查看免费下载本文是 jstutorial 仓库「语法专题」系列中《MVC框架与Backbone.js》一文的完整展开版。全文以该文为骨架从 JavaScript 代码组织困境出发依次讲解 MVC 概念、ES6 时代的零框架模块化方案、Backbone 的加载方式与整体用法并逐项拆解Backbone.View、Backbone.Events、Backbone.Router、Backbone.history、Backbone.Model、Backbone.Collection六大核心对象的 API 与实战代码。读完本文你将掌握用 Backbone 组织一个可维护的浏览器端应用的基本方法并理解它与 RequireJS、Browserify、Underscore 等生态工具如何协同工作。MVC 框架复杂 JavaScript 应用的代码组织之道随着 JavaScript 程序变得越来越复杂往往需要一个团队协作开发这时代码的模块化和组织规范就变得异常重要。MVC 模式就是代码组织的经典模式。MVC 模式把应用程序拆分为三层各司其职1ModelModel 表示数据层也就是程序需要的数据源通常使用 JSON 格式表示。它封装了业务数据以及对这些数据的读写逻辑是应用中事实的唯一来源。2ViewView 表示表现层也就是用户界面。对于网页来说就是用户看到的网页 HTML 代码。View 负责把 Model 中的数据呈现出来并收集用户的交互动作。3ControllerController 表示控制层用来对原始数据Model进行加工传送到 View。它承担翻译官的角色监听用户操作、调用 Model 更新数据、再驱动 View 重新渲染。由于网页编程不同于客户端编程在 MVC 的基础上JavaScript 社区产生了各种变体框架 MVPModel-View-Presenter、MVVMModel-View-ViewModel等等有人就把所有这一类框架的各种模式统称为 MV*。框架的优点在于合理组织代码、便于团队合作和未来的维护缺点在于有一定的学习成本且限制你只能采取它的写法。从历史背景看Backbone 正是这一波浪潮的标志性产物jstutorial 的 JavaScript 发展史 记载2010 年 NPM、BackboneJS 和 RequireJS 三个重要项目诞生标志着 JavaScript 进入模块化开发的时代2012 年单页面应用程序框架single-page app framework开始崛起AngularJS 和 Ember 项目相继发布 1.0 版本。理解这段背景有助于把握 Backbone 小而自由的设计取向。零框架解决方案ES6 时代的模块化组合MVC 框架尤其是大型框架有一个严重的缺点就是会产生用户的重度依赖。一旦框架本身出现问题或者停止更新用户的处境就会很困难维护和更新成本极高。ES6 的到来使得 JavaScript 语言有了原生的模块解决方案相关语法特性可参见仓库中的 ECMAScript 6 介绍。于是开发者有了另一种选择就是不使用 MVC 框架只使用各种单一用途的模块库组合完成一个项目。下面是可供选择的各种用途的模块清单以下库名与用途说明整理自原文档。辅助功能库Helper Librariesmoment.js日期和时间的标准化underscore.js / Lo-Dash一系列函数式编程的功能函数本仓库的 underscore 教程 有专门章节路由库Routingrouter.jsEmber.js 使用的路由库route-recognizer功能全面的路由库page.js类似 Express 路由的库director同时支持服务器和浏览器的路由库Promise 库异步方案的系统讲解可参考仓库 Promise 对象 一章RSVP.jsES6 兼容的 Promise 库ES6-PromiseRSVP.js 的子集但是全面兼容 ES6q最常用的 Promise 库之一AngularJS 用了它的精简版native-promise-only严格符合 ES6 的 Promise 标准同时兼容老式浏览器客户端与服务器的通信库fetch实现 window.fetch 功能qwest支持 XHR2 和 Promise 的 Ajax 库jQueryjQuery 2.0 支持按模块打包因此可以创建一个纯 Ajax 功能库动画库Animationcssanimevent兼容老式浏览器的 CSS3 动画库Velocity.js性能优秀的动画库辅助开发库Development AssistanceLogJS轻量级的 logging 功能库UserTiming.js支持老式浏览器的高精度时间戳库流程控制和架构Flow Control/Architectureondomready类似 jQuery 的 ready() 方法符合 AMD 规范script.js异步的脚本加载和依赖关系管理库async浏览器和 node.js 的异步管理工具库Virtual DOMreact.js 的一个替代方案核心思路是借助 Virtual DOM 与 diffing 算法减少真实 DOM 操作数据绑定Data-bindingObject.observe()Chrome 已经支持该方法可以轻易实现双向数据绑定模板库TemplatingMustache大概是目前使用最广的不含逻辑的模板系统微框架Micro-Framework某些情况下可以使用微型框架作为项目开发的起点。bottlejs提供惰性加载、中间件钩子、装饰器等功能Stapes.js微型 MVC 框架soma.js提供一个松耦合、易测试的架构knockout最流行的微框架之一主要关注 UIBackbone 的加载三种主流方式Backbone 依赖 jQuery或 Zepto与 Underscore 两个库加载时必须保证依赖顺序正确。方式一传统script标签顺序加载这是原文档给出的经典加载方式依赖先于 Backbone 声明业务脚本router、init放在最后script src/javascripts/lib/jquery.js/script script src/javascripts/lib/underscore.js/script script src/javascripts/lib/backbone.js/script script src/javascripts/jst.js/script script src/javascripts/router.js/script script src/javascripts/init.js/script方式二RequireJS 的 shim 声明Backbone 与 Underscore 本身都不是 AMD 规范的库使用 RequireJS 时需要借助shim声明依赖关系与导出符号。仓库 RequireJS 教程 中给出了标准配置require.config({ paths: { backbone: vendor/backbone, underscore: vendor/underscore }, shim: { backbone: { deps: [ underscore ], exports: Backbone }, underscore: { exports: _ } } });上面的配置中shim指定了 backbone 依赖于 underscore以及两个库的输出符号backbone 为Backboneunderscore 为_。模块化加载后就可以用 AMD 的define形式引入 Backbone 并定义视图define([ backbone, text!templates.html ], function( Backbone, template ){ // ... });其中text!前缀的插件会把模板文本作为字符串传入回调函数。方式三Browserify 的 CommonJS 打包在 Node.js 生态中可以用 npm 安装 Backbone 及其依赖再通过 Browserify 打包成浏览器可直接加载的单个文件。仓库 Browserify 教程 给出的完整流程如下npm install backbone jquery新建main.js// main.js var Backbone require(backbone); var $ Backbone.$ require(jquery/dist/jquery)(window); var AppView Backbone.View.extend({ render: function(){ $(main).append(h1Browserify is a great tool./h1); } }); var appView new AppView(); appView.render();然后打包browserify main.js -o app.jsapp.js可以直接插入 HTML 网页且只需引入这一个文件完全不需要再加载 backbone.js 和 jQuery 了。Backbone 的整体用法从应用骨架到启动Backbone 是最早的 JavaScript MVC 框架也是最简化的一个框架。它的设计思想是只提供最基本的功能给用户提供最大的自由。这意味着好的一面是它没有一整套规则强制你接受坏的一面是很多功能你必须自己实现。Backbone 的体积相当小最小化后只有 30 多 KB。首先定义一个对象表示 Web 应用它由四部分组成Model、Collection、Controller 和 View。var AppName { Models :{}, Views :{}, Collections :{}, Controllers :{} };定义 Model表示数据的一个基本单位AppName.Models.Person Backbone.Model.extend({ urlRoot: /persons });定义 Collection表示 Model 的集合。Collection 对象必须有model属性指明由哪一个 model 构成AppName.Collections.Library Backbone.Collection.extend({ model: AppName.Models.Book });定义 View。View 对象必须有el属性指明当前 View 绑定的 DOM 节点events属性指明事件和对应的方法AppName.Views.Modals.AcceptDecline Backbone.View.Extend({ el: .modal-accept, events: { ajax:success .link-accept :acceptSuccess, ajax:error .link-accept :acceptError }, acceptSuccess :function(evt, response) { this.$el.modal(hide); alert(Cool! Thanks); }, acceptError :function(evt, response) { var $modalContent this.$el.find(.panel-modal); $modalContent.append(Something was wrong!); } });定义 Controller负责业务逻辑的编排例如按 id 取回数据、更新数据、再渲染对应视图AppName.Controllers.Person {}; AppName.Controllers.Person.show function(id) { var aMa new AppName.Models.Person({id: id}); aMa.updateAge(25); aMa.fetch().done(function(){ var view new AppName.Views.Show({model: aMa}); }); };最后定义路由启动应用程序。routes对象的键是 URL 路径规则值是对应的处理函数这里直接指向 Controller 中的方法var Workspace Backbone.Router.extend({ routes: { * :wholeApp, users/:id :usersShow, users/:id/orders/ :ordersIndex }, wholeApp :AppName.Controller.Application.default, usersShow :AppName.Controller.Users.show, ordersIndex :AppName.Controller.Orders.index }); new Workspace(); Backbone.history.start({pushState: true});Backbone.View视图层核心 API基本用法Backbone.View方法用于定义视图类。通过extend定义视图类内部用render方法把视图放置到网页上var AppView Backbone.View.extend({ render: function(){ $(main).append(h1一级标题/h1); } });使用的时候需要先新建视图类的实例再通过实例调用render方法var appView new AppView(); appView.render();新建视图实例时通常需要指定 Model作为视图的数据来源var document new Document({ model: doc });initialize 方法视图还可以定义initialize方法生成实例的时候会自动调用该方法对实例初始化从而省去生成实例后手动调用render()的步骤var AppView Backbone.View.extend({ initialize: function(){ this.render(); }, render: function(){ $(main).append(h1一级标题/h1); } }); var appView new AppView();el 属性与 $el 属性除了直接在render方法中指定视图所绑定的网页元素还可以用视图的el属性指定网页元素var AppView Backbone.View.extend({ el: $(main), render: function(){ this.$el.append(h1一级标题/h1); } });上面的代码与在 render 方法中直接绑定网页元素效果完全一样。注意这里除了el属性还有$el属性前者代表指定的 DOM 元素后者表示该 DOM 元素对应的 jQuery 对象。tagName 属性与 className 属性如果不指定el属性也可以通过tagName属性和className属性让 Backbone 自动生成所需的 DOM 元素var Document Backbone.View.extend({ tagName: li, className: document, render: function() { // ... } });template 方法配合 Underscore 模板引擎视图的template属性用来指定网页模板。最常用的做法是借助 Underscore 的_.template函数编译模板字符串var AppView Backbone.View.extend({ template: _.template(h3Hello % who %h3), });_.template接受一个模板字符串作为参数返回对应的模板函数。只要提供具体的值就能生成网页代码。完整用法如下var AppView Backbone.View.extend({ el: $(#container), template: _.template(h3Hello % who %h3), initialize: function(){ this.render(); }, render: function(){ this.$el.html(this.template({who: world!})); } });这里render调用了template方法把数据对象{who: world!}代入模板生成具体的网页代码后写入容器。关于_.template的细节仓库 underscore 教程 有更完整的说明这里补充几个要点模板字符串就是普通 HTML变量使用% … %的形式插入data对象负责提供变量的值_.template(txt, {word : Hello World})如果变量的值包含五个特殊字符 /需要用%- ... %转义例如_.template(h2%- word %/h2, {word : H W})会输出h2H amp; W/h2JavaScript 命令可以采用% … %的形式插入支持判断语句和循环语句配合_.each如果_.template只传第一个参数templateString则会返回一个函数之后可以向这个函数输入数据模板内部变量默认是obj对象的属性可以通过第三个参数settings中的variable改名例如_.template(..., null, { variable: data })还能避免with语句带来的性能开销。实际应用中一般将模板放在script标签中为了防止浏览器按照 JavaScript 代码解析type属性设为text/templatescript typetext/template>window.templates {}; var $sources $(script[typetext/template]); $sources.each(function(index, el) { var $el $(el); templates[$el.data(name)] _.template($el.html()); });events 属性events属性用于指定视图的事件及其对应的处理函数。键的格式是事件名 选择器值是处理函数名var Document Backbone.View.extend({ events: { click .icon: open, click .button.edit: openEditDialog, click .button.delete: destroy } });上面代码指定了三个 CSS 选择器的单击事件及其对应的三个处理函数。listenTo 方法listenTo方法用于为特定事件指定回调函数典型场景是让视图监听自己的 Model 变化并自动重绘var Document Backbone.View.extend({ initialize: function() { this.listenTo(this.model, change, this.render); } });上面代码为 model 的change事件指定了回调函数render。与直接使用model.on(...)相比listenTo由视图统一管理监听关系视图销毁时监听也会被自动清理。remove 方法remove方法用于移除一个视图典型用法是先移除旧视图再渲染新视图更新视图场景updateView: function() { view.remove(); view.render(); };子视图subview在父视图中可以调用子视图实现视图的嵌套组合。下面是一种常见写法父视图先渲染自己的模板再创建子视图实例并填充到指定占位容器render : function (){ this.$el.html(this.template()); this.child new Child(); this.child.appendTo($.(.container-placeholder).render(); }Backbone.Events发布/订阅事件机制Backbone.Events是一个事件对象。任何继承了这个对象的对象都具备了Backbone.Events的事件接口可以调用on和trigger方法发布和订阅消息。var EventChannel _.extend({}, Backbone.Events);下面是几个例子。事件通道可以承载任意类型的数据作为消息载荷var channel $.extend( {}, Backbone.Events ); channel.on(remove-node, function(msg) { // code to remove the node }); channel.trigger( remove-node, msg ); // msg can be everything: String, number, object and so forth // also we can pass more than one message like the example below channel.on(add-node, function(node, callback) { // code to add a new node callback(); } ); channel.trigger(add-node, { label: I am a new node, color: black }, function() { console.log( I am a callback ); });可以看到trigger可以携带多个参数数据对象 回调函数这些参数会原样传给on注册的处理函数。这也体现了 Backbone 事件机制的灵活性事件名是字符串、载荷可以是任意 JavaScript 值。Backbone.Router 与 routes 属性Router 是 Backbone 提供的路由对象用来将用户请求的网址与后端的处理函数一一对应。首先新定义一个 Router 类Router Backbone.Router.extend({ routes: { } });routes 属性Backbone.Router对象中最重要的就是routes属性它用来设置路径的处理方法。routes属性是一个对象每个成员代表一个路径处理规则键名为路径规则键值为处理方法。根路径如果键名为空字符串就代表根路径。routes: { : phonesIndex, }, phonesIndex: function () { new PhonesIndexView({ el: section#main }); }任意路径捕获星号代表任意路径可以设置路径参数捕获具体的路径值。var AppRouter Backbone.Router.extend({ routes: { *actions: defaultRoute } }); var app_router new AppRouter; app_router.on(route:defaultRoute, function(actions) { console.log(actions); })上面代码中根路径后面的参数都会被捕获传入回调函数。参数占位符的写法:param匹配路径中的一个片段*splat匹配任意多个路径片段也支持用-连接多个参数var myrouter Backbone.Router.extend({ routes: { help: help, search/:query: search }, help: function() { ... }, search: function(query) { ... } }); routes: { help/:page: help, download/*path: download, folder/:name: openFolder, folder/:name-:mode: openFolder } router.on(route:help, function(page) { ... });上面的:query、:page、:name、:mode都是具名参数会按顺序作为参数传给对应的路由处理函数*path是通配参数捕获download/之后的全部路径。路由触发时会发出形如route:help的事件也可以在 Router 实例上监听这些事件。Backbone.history启动路由与应用设置了 Router 以后就可以启动应用程序。Backbone.history对象用来监控 URL 的变化是路由真正开始工作前的最后一步。App new Router(); $(document).ready(function () { Backbone.history.start({ pushState: true }); });打开pushState选项后路由将使用 HTML5 History API 管理 URL。如果应用程序不在根目录就需要通过root参数指定根目录Backbone.history.start({pushState: true, root: /public/search/})Backbone.Model数据实体核心 APIModel 代表单个的对象实体。使用extend方法生成一个 Model 类相当于模板再用new命令生成实例。defaults属性用来设置默认属性var User Backbone.Model.extend({ defaults: { name: , email: } }); var user new User();上面代码中user 对象默认有name和email两个属性值都等于空字符串。生成实例时也可以直接提供各个属性的具体值var user new User ({ id: 1, name: name, email: nameemail.com });idAttribute 属性Model 实例必须有一个属性作为区分其他实例的主键。这个属性的名称由idAttribute属性设定一般设为idvar Music Backbone.Model.extend({ idAttribute: id });get 方法get方法用于返回 Model 实例的某个属性的值var user new User({ name: name, age: 24}); var age user.get(age); // 24 var name user.get(name); // nameset 方法set方法用于设置 Model 实例的某个属性的值var User Backbone.Model.extend({ buy: function(newCarsName){ this.set({car: newCarsName }); } }); var user new User({name: BMW,model:i8,type:car}); user.buy(Porsche); var car user.get(car); // Porscheon 方法on方法用于监听对象的变化。事件名change:name表示 name 属性发生变化var user new User({name: BMW,model:i8}); user.on(change:name, function(model){ var name model.get(name); // Porsche console.log(Changed my cars name to name); }); user.set({name: Porsche}); // Changed my cars name to PorscheurlRoot 属性urlRoot属性用于指定服务器端对 Model 进行操作的路径。一旦设定该 Model 的所有 REST 请求都会以此为基准var User Backbone.Model.extend({ urlRoot: /user });fetch 方法GETfetch用于从服务器取出 Model。实例含有id属性值为 1时fetch方法使用 HTTP 动词 GET向网址/user/1发出请求从服务器取出该实例var user new User ({id: 1}); user.fetch({ success: function (user){ console.log(user.toJSON()); } })save 方法POST / PUTsave方法用于通知服务器新建或更新 Model。如果 Model 实例不含id属性save将使用 POST 方法新建该实例。此时它有两个参数第一个是实例对象的具体属性第二个是回调函数对象设定success保存成功事件的回调函数var User Backbone.Model.extend({ urlRoot: /user }); var user new User (); var userDetails { name: name, email: nameemail.com }; user.save(userDetails, { success: function (user) { console.log(user.toJSON()); } })具体来说save方法会向/user发出一个 POST 请求并将{name: name, email: nameemail.com}作为数据提供。如果 Model 实例含有id属性save将使用 PUT 方法更新该实例var user new User ({ id: 1, name: 张三, email: nameemail.com }); user.save({name: 李四}, { success: function (model) { console.log(user.toJSON()); } });对象实例含有id属性值为 1save将使用 PUT 方法向网址/user/1发出请求从而更新该实例。destroy 方法DELETEdestroy方法用于在服务器上删除该实例。它将使用 HTTP 动词 DELETE向网址/user/1发出请求删除对应的 Model 实例var user new User ({ id: 1, name: name, email: nameemail.com }); user.destroy({ success: function () { console.log(Destroyed); } });综上Backbone.Model 与服务器通信时遵循 REST 风格fetch对应 GETsave根据有无id分别对应 POST 与 PUTdestroy对应 DELETE。Backbone.CollectionModel 的集合Collection 是同一类 Model 的集合。打个比方Model 是动物Collection 就是动物园Model 是单个的人Collection 就是一家公司。定义 Collection 时model属性指明该集合由哪一种 Model 构成var Song Backbone.Model.extend({}); var Album Backbone.Collection.extend({ model: Song });上面代码中Song 是 ModelAlbum 是 CollectionAlbum 的model属性等于 Song表明 Album 是 Song 的集合。add 方法、remove 方法Model 的实例可以直接放入 Collection 的实例也可以用add方法添加var song1 new Song({ id: 1 ,name: 歌名1, artist: 张三 }); var song2 new Music ({id: 2,name: 歌名2, artist: 李四 }); var myAlbum new Album([song1, song2]); var song3 new Music({ id: 3, name: 歌名3,artist:赵五 }); myAlbum.add(song3);remove方法用于从 Collection 实例中移除一个 Model 实例参数是 model 实例的id属性myAlbum.remove(1);get 方法get方法用于从 Collection 中获取指定 id 的 Model 实例myAlbum.get(2)fetch 方法fetch方法用于从服务器取出整个 Collection 的数据。需要先给 Collection 指定url属性var songs new Backbone.Collection; songs.url /songs; songs.fetch();延伸阅读继续深入 Backbone 的模板协作对象 Underscore可阅读仓库的 underscore 教程重点看template、each、extend等函数的完整说明用 AMD 规范组织 Backbone 模块可参考仓库的 RequireJS 教程 中shim配置与text!插件的用法用 CommonJS 与 Browserify 打包 Backbone 应用可参考仓库的 Browserify 教程Backbone 诞生的历史背景与模块化开发时代的来龙去脉可参考仓库的 JavaScript 发展史本文零框架解决方案中提到的 Promise 类库其异步编程思想可结合仓库的 Promise 对象 一章理解。赞分享教程文档【免费下载链接】jstutorialJavascript tutorial book项目地址https://gitcode.com/gh_mirrors/js/jstutorial点击查看免费下载相关推荐ASP.NET Core MVC 架构模式解析Model、View、Controller 与实战落地ASP.NET Core MVC 架构模式解析Model、View、Controller 与实战落地 导读 MVCModel View Controlle文档教程知识库终极 DLSS Swapper 使用指南不开游戏目录替换 DLSS 版本帧率画质一步到位终极 DLSS Swapper 使用指南不开游戏目录替换 DLSS 版本帧率画质一步到位 你的游戏自带的 DLSS 版本太旧帧率上不去又不想手动翻游戏目桌面应用Android Debug Database MVC架构详解Model-View-Controller在Web界面中的实现Android Debug Database MVC架构详解Model View Controller在Web界面中的实现 Android Debug Dat开发工具数据库客户端上一篇英雄联盟玩家必看如何用智能工具在选人阶段就赢在起跑线下一篇百度网盘直链解析方案突破限速瓶颈的技术实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考