TodoMVC Backbone 实战指南:基于 MV\* 架构的待办应用实现与构建解析 📅 发布时间:2026/9/19 12:28:11 👁 浏览次数: TodoMVC Backbone 实战指南基于 MV* 架构的待办应用实现与构建解析【免费下载链接】todomvcHelping you select a JavaScript framework - Todo apps for React.js, Angular, Vue and many more项目地址: https://gitcode.com/gh_mirrors/to/todomvc导读本文以 TodoMVC 仓库中 examples/backbone 示例为对象完整剖析如何用 Backbone.js 实现一个功能完备的 Todo 待办应用。你将掌握 Backbone 的 Model / Collection / View / Router 分层组织方式、基于 Underscore 模板的渲染方案、无后端场景下的内存存储替代实现以及从安装依赖、本地预览到npm run build产出dist静态目录的完整操作流程可作为学习 Backbone MV* 架构或为自有项目搭建 Backbone 骨架的参考。一、项目概览Backbone 与 TodoMVC该示例应用使用 Backbone 实现 TodoMVC 规范中的全部交互新增待办、勾选完成、双击编辑、Esc 撤销、清除已完成项、按 All / Active / Completed 过滤等。原文档对 Backbone 的定位如下Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.即 Backbone 通过带键值绑定与自定义事件的 Model、带丰富可枚举 API 的 Collection、带声明式事件处理的 View并最终通过 RESTful JSON 接口与既有 API 打通为 Web 应用赋予结构。本示例对应的依赖声明位于 examples/backbone/package.jsonbackbone ^1.4.1、jquery ^3.6.4、underscore ^1.13.6、todomvc-common ^1.0.5、todomvc-app-css ^2.4.2。从源码结构看examples/backbone/src 目录整个应用被拆分为五个职责清晰的模块目录/文件职责models/todo.jsTodo 模型属性默认值与状态切换逻辑collections/todos.jsTodo 集合过滤、排序、序号生成views/app-view.js顶层应用视图统计、新增、批量操作views/todo-view.js单个待办项视图编辑、切换、删除routers/router.js路由URL 哈希与过滤状态联动二、架构解析Backbone 的 MV* 模式落地原文档明确指出Backbone.js 采用的是 MV(*) 模式其思想松散地基于 MVC 或 MVP。下面把 Backbone 的概念与经典 MVC 概念逐一对应Backbone Model ↔ MVC Model维护应用的数据与行为。本示例中 models/todo.js 定义了title、completed属性默认值并提供toggle()方法翻转完成状态后调用save()持久化app.Todo Backbone.Model.extend({ defaults: { title: , completed: false }, toggle: function () { this.save({ completed: !this.get(completed) }); }, });Backbone View ↔ MVC View负责把模型渲染到 UI。原文档特别说明html file in conjunction with underscores templating solution——视图既包含 HTML 骨架文件也依赖 Underscore 的模板方案见下文Notes。Backbone Controller 角色 ↔ MVC Controller作为 View 与 Model 之间的接口。在 Backbone 中这一职责由view 和 router 文件共同承担View 捕获 DOM 事件并调用 Model/Collection 的方法Router 则把 URL 变化翻译为集合事件。Notes两个关键的实现细节存储方案为内存数据对象本示例不依赖任何后端或 localStorage而是用自定义的Backbone.sync实现将数据保存在一个 JavaScript 对象中详见下文第四节。Underscore 的template工具函数它把 JavaScript 模板字符串编译为可执行、可求值渲染的函数。本示例在 views/app-view.js 和 views/todo-view.js 中均通过_.template($(#...).html())缓存模板函数模板本体定义在 examples/backbone/index.html 的script typetext/template块内。三、数据层Model 与 Collection 的协作Todo 模型如上面代码所示模型只有两个默认属性与一个toggle()方法。所有属性读写都经由get()/set()/save()这正是 Backbone 键值绑定与自定义事件的体现——属性变化会自动触发change事件驱动视图重渲染。Todos 集合collections/todos.js 展示了 Collection 的核心用法const Todos Backbone.Collection.extend({ model: app.Todo, completed: function () { return this.where({ completed: true }); }, remaining: function () { return this.where({ completed: false }); }, nextOrder: function () { return this.length ? this.last().get(order) 1 : 1; }, comparator: order, }); app.todos new Todos();要点说明model: app.Todo声明集合元素类型向集合add/create原始对象时会自动实例化为 Todo 模型completed()/remaining()借助 Backbone 集合内置的where()过滤器返回完成/未完成子集nextOrder()依据最后一项的order加 1生成新待办序号空集合从 1 开始对应注释中提到的尽管底层数据库以无序 GUID 保存我们仍让 Todos 保持顺序comparator: order声明集合按order属性排序保证渲染顺序即创建顺序。四、存储层用 Backbone.sync 实现内存同步原文档 Notes 中强调存储方案使用内存数据对象包含在 sync 文件里。这一实现位于 sync/backbone.sync.js。它重写了Backbone.syncBackbone 默认基于$.ajax的 REST 持久化入口用闭包内的cache对象模拟数据库size变量模拟自增主键并覆盖四种方法method行为create为模型分配id size并存入cachesize 1update若模型存在于cache则整体覆盖否则回调error(Model not found)read若存在则把cache[model.id].toJSON()交给success回调delete从cache中移除该模型success收到空对象{}值得注意的实现细节是它返回$.Deferred().promise()而不是直接的$.ajax请求。源码注释解释了原因标准 sync 返回的$.ajax内部就是 deferred这里显式构造 deferred 是为了让调用方仍能沿用标准的.then()/.fail()链式语法而不是只能依赖 success/error 回调。此外examples/backbone/index.html 末尾还有一行window.Backbone.sync () {};用于在页面加载完成后覆盖 sync避免后续无意义请求。五、视图层AppView 与 TodoViewAppView顶层应用视图views/app-view.js 将视图直接绑定到 HTML 中已有的.todoapp骨架元素el: .todoapp而非动态生成元素。其声明式事件委托如下events: { keypress .new-todo: createOnEnter, click .clear-completed: clearCompleted, click .toggle-all: toggleAllComplete, }初始化时通过listenTo订阅集合事件驱动 UI 更新add→addOne为每个新模型创建 TodoView 并追加到ul classtodo-listreset→addAll批量渲染initialize中执行app.todos.fetch({ reset: true })并通过{reset: true}抑制逐条add事件、只在 fetch 结束的reset事件时渲染一次源码注释明确说明了这一点change:completed→filterOne、filter→filterAll联动过滤状态all→_.debounce(this.render, 0)集合任意事件变化时防抖重渲染统计区。render()只负责刷新底部统计completed / remaining 数量、过滤链接高亮、主区域显隐、全选复选框状态体现了视图只重渲染变化部分的 Backbone 惯用法。新增逻辑由createOnEnter在检测到ENTER_KEY13且输入非空时调用app.todos.create(this.newAttributes())完成newAttributes会依次取title、nextOrder()与completed: false。TodoView单个待办项视图views/todo-view.js 使用tagName: li声明每个待办项渲染为li模板缓存在_.template($(#item-template).html())中。其事件映射覆盖了完整的单条交互events: { click .toggle: toggleCompleted, dblclick label: edit, click .destroy: clear, keypress .edit: updateOnEnter, keydown .edit: revertOnEscape, blur .edit: close, }实现细节亮点render()中有一处针对模型创建后id变化导致视图重复渲染的防御逻辑源码注释引用了 todomvc issue #469当this.model.changed.id ! undefined时直接返回过滤掉第二次冗余渲染编辑流程双击label进入editing态并聚焦输入框回车ENTER_KEY提交保存、blur关闭编辑ESC_KEY27则移除editing类并把输入框重置为原title若保存时标题被清空则直接删除该条this.clear()显隐控制toggleVisible()/isHidden()依据当前app.TodoFilteractive/completed与模型完成状态决定是否添加hidden类。六、路由层URL 与过滤状态联动routers/router.js 是整个应用最短却最关键的一环const TodoRouter Backbone.Router.extend({ routes: { *filter: setFilter }, setFilter: function (param) { app.TodoFilter param || ; app.todos.trigger(filter); }, }); app.TodoRouter new TodoRouter(); Backbone.history.start();它注册了一条通配路由*filter访问#/active、#/completed、#/时分别把全局过滤状态app.TodoFilter置为active、completed或空串随后触发集合的filter事件。AppView 监听到该事件后经由filterAll→filterOne通知每个 TodoView 按需隐藏或显示同时render()中也会用$(.filters li a).filter([href#/ (app.TodoFilter || ) ]).addClass(selected)高亮当前激活的过滤链接。最终通过Backbone.history.start()启用基于 hash 的历史管理。七、页面骨架与模板examples/backbone/index.html 承担三方面职责静态骨架.todoapp内包含标题输入框.new-todo、.toggle-all全选框、空列表ul classtodo-list和空 footer——footer 的内容完全由 AppView 渲染填充Underscore 模板#item-template单条待办的复选、标题、删除按钮与编辑框和#stats-template剩余数量、All / Active / Completed 过滤链接、条件渲染的 Clear completed 按钮。模板中completed ? checked : 控制复选框状态%- title %采用转义插值防止 XSS脚本加载顺序先加载todomvc-common/base.js、jQuery、Underscore、Backbone随后按依赖次序加载 sync → model → collection → 两个 view → router → app.js。入口 src/app.js 在 DOM ready 后创建window.appView new app.AppView()并追加一个idappIsReady的占位节点用于通知外部基准测试工具如 Speedometer应用已初始化完成。八、构建与运行从源码到 dist 静态站点环境要求原文档列出的最低版本要求同样写入了 examples/backbone/package.json 的engines字段Node最低 18.13.0NPM最低 8.19.3唯一的前置依赖就是安装 Node以便安装依赖并启动本地服务器。本地预览在原文档给出的终端命令基础上完整步骤如下npm install npm run devnpm run dev实际执行的是 package.json 中的http-server ./ -p 7001 -c-1 --cors即在项目根目录起一个端口为7001的静态服务器-c-1禁用缓存--cors允许跨域。随后浏览器访问http://localhost:7001/即可直接运行应用——它不需要任何编译或转译步骤直接以原始 HTML、CSS、JS 文件对外服务。构建npm run buildnpm run build调用 scripts/build.js其流程如下删除已存在的dist目录并重建递归复制整个src目录到dist复制index.html按 build.js 中filesToMove数组复制所需的静态资源与库文件todomvc-common的 base.css/base.js、todomvc-app-css的 index.css、jQuery / Underscore / Backbone 的压缩版及其 sourcemap全部平铺到dist根目录改写dist/index.html中的引用路径把node_modules/...前缀替换为文件名、把src/前缀去除使产物成为完全自包含、可直接部署的静态目录。构建完成后可通过 package.json 中现成的npm run servehttp-server ./dist -p 7002 -c-1 --cors在7002端口预览构建产物。九、小结一条清晰的 Backbone 学习路径纵观整个示例一条完整的 Backbone 数据流可以概括为Router 把 URL 变为集合事件 → AppView 监听事件并驱动 TodoView → TodoView 捕获 DOM 交互调用 Model 的save/destroy→ 自定义Backbone.sync完成内存读写 → 模型change事件再次触发视图重渲染。其中 Model 的键值绑定与事件、Collection 的过滤排序 API、View 的声明式事件委托、Router 的 hash 路由以及可替换的Backbone.sync持久化层恰好覆盖了原文档描述的 Backbone 全部核心特性。对希望快速上手 Backbone 或为现有项目引入 MV* 结构的开发者而言这份示例的 readme.md、package.json 与 src 源码是很好的配套阅读材料。【免费下载链接】todomvcHelping you select a JavaScript framework - Todo apps for React.js, Angular, Vue and many more项目地址: https://gitcode.com/gh_mirrors/to/todomvc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考