十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

cujoJS TodoMVC 架构深度解析:wire 声明式组合、cola 数据绑定与 OOCSS 实战

cujoJS TodoMVC 架构深度解析:wire 声明式组合、cola 数据绑定与 OOCSS 实战 cujoJS TodoMVC 架构深度解析wire 声明式组合、cola 数据绑定与 OOCSS 实战【免费下载链接】todomvcHelping you select a JavaScript framework - Todo apps for React.js, Angular, Vue and many more项目地址: https://gitcode.com/gh_mirrors/to/todomvccujoJS 是一套面向下一代 JavaScript 应用的架构工具包主张高度模块化开发、声明式应用装配并充分拥抱 JavaScript 的异步特性与面向对象/函数式融合的编程风格。本文以仓库中的 examples/cujo 实现为骨架完整剖析 cujoJS TodoMVC 示例的组件结构、wire 组合规格、cola 数据绑定与控制器设计让读者掌握组合与逻辑分离环境垫片而非抽象等 cujo 核心实践并能在自己的项目中直接复用这套声明式装配模式。cujoJS 与 TodoMVC示例定位TodoMVC 项目通过同一款待办应用在不同框架下的实现来帮助开发者挑选 JavaScript 框架cujoJS 正是其中架构工具包路线的代表。与 React、Vue 等完整框架不同cujoJS 本身不提供 UI 层而是由多个可独立使用的库协同工作wire声明式应用装配IoC 容器负责组件创建、连接与生命周期cola数据与 DOM 的双向链接数据绑定curlAMD 模块加载器引导应用whenPromise 库异步流程meld面向切面编程AOPpolyES5 环境垫片shim。上述依赖在 examples/cujo/package.json 中明确列出curl ~0.7.4、cola latest、wire ~0.10.0、meld ~1.3.0、when ~2.1.1、poly ~0.5.1并在 examples/cujo/app/run.js 的packages配置里完成路径映射。核心架构理念应用组合与应用逻辑分离原文档反复强调的第一条设计原则是Application composition is separate from application logic应用组合独立于应用逻辑。在 cujoJS 中这由 wire 的组合规格composition specification即 wire spec承担。整份规格就是一个 AMD 模块通过声明式描述创建哪些组件、如何渲染、如何连接而真正的行为逻辑则放在独立的 controller 模块中。以 examples/cujo/app/main.js 为例这是一份完整的 wire spec全部内容不包含任何业务函数实现只描述装配define({ root: { $ref: dom!todoapp }, // 视图挂载根节点 createView: { render: { template: { module: text!app/create/template.html }, replace: { module: i18n!app/create/strings } }, insert: { first: root } }, createForm: { element: { $ref: dom.first!form, at: createView }, connect: { todos.onAdd: reset } }, // ... listView / controlsView / footerView / todoStore / todos / todoController plugins: [wire/dom, wire/dom/render, wire/on, wire/aop, wire/connect, cola] });可以看到每个组件都通过render、insert、bind、connect、on等声明式键描述职责业务逻辑则以module: app/controller之类的方式被注入进来。这种组合与逻辑的分离意味着换一个模板、换一套事件绑定、甚至换一个数据源都只需要修改这份 spec 而无需触碰业务代码。wire spec 中的关键概念声明键作用示例$ref引用其他组件或 DOM 节点{ $ref: dom!todoapp }、{ $ref: todos }module指定要创建的 AMD 模块{ module: cola/adapter/LocalStorage, args: todos-cujo }render.template加载 HTML 模板text! 插件text!app/create/template.htmlrender.replace用 i18n 字典替换模板占位符i18n!app/create/stringsinsert决定视图插入位置{ first: root }、{ after: createView }bindcola 数据绑定声明{ to: { $ref: todos }, comparator: dateCreated, bindings: {...} }onDOM 事件声明式监听click:.destroy: removeTodoconnect组件方法与事件/信号的连接{ todos.onAdd: reset }before数据操作前拦截AOP 语义{ add: cleanTodo \| generateMetadata }从源码结构看wire 通过plugins列表见 main.js 末尾加载wire/domDOM 引用、wire/dom/render渲染、wire/on事件、wire/aop切面、wire/connect连接与cola数据绑定等能力共同构成一份 spec 的方言。引导流程从 curl 到 wire 的装配管线应用不是从 DOM 事件开始而是由一个加载器拉起整棵组件树。入口在 examples/cujo/index.htmlsection idtodoapp/section script>define([curl], function (curl) { curl({ main: wire!app/main, // 用 wire! 插件加载组合规格 packages: [ { name: curl, location: bower_components/curl/src/curl }, { name: wire, location: bower_components/wire, main: wire }, { name: when, location: bower_components/when, main: when }, { name: meld, location: bower_components/meld, main: meld }, { name: cola, location: bower_components/cola, main: cola }, { name: poly, location: bower_components/poly, main: poly } ], preloads: [poly/es5], locale: false // 关闭 i18n 语言环境自动嗅探 }); });关键点main: wire!app/main使用 wire 的 AMD 插件形式加载并执行组合规格整个应用的生命周期由 wire 接管preloads: [poly/es5]在应用逻辑运行前垫片化 ES5 环境这正是环境被垫片而非抽象的具体落地locale: false关闭 i18n 语言嗅探——若需测试特定 locale 或启用自动语言检测可修改或删除该行源码注释对此有明确说明。组件结构一个组件 六类文件原文档指出cujo 示例的代码高度模块化每个组件由一个或多个文件组成wire 组合规格、JavaScript 控制器模块、辅助模块、本地化字典strings.js、HTML 模板template.html以及通常不带主题的 CSSstructure.css。以create新建组件为例examples/cujo/app/create 目录下恰好包含了全部类型文件职责template.html纯 HTML 结构仅含#header、表单与#new-todo输入框strings.js本地化字典title: todos、todo.placeholder: What needs to be done?cleanTodo.js数据清洗辅助模块generateMetadata.js生成 GUID 与创建时间的辅助模块validateTodo.js数据校验辅助模块其他组件list、controls、footer遵循同样的组织方式其中list还多出一个setCompletedClass.js自定义数据链接处理器与structure.css。这种文件布局让组件成为一个自包含、可独立理解与替换的单元。HTML 模板干净到凡人可编辑原文档强调模板clean and simple, editable by mere mortals。看 examples/cujo/app/list/template.htmlsection idmain input idtoggle-all typecheckbox label fortoggle-all${markAll}/label ul idtodo-list li div classview input classtoggle typecheckbox label/label button classdestroy/button /div forminput classedit nametext/form /li /ul /section模板中唯一的动态成分是${markAll}这类 i18n 占位符列表项的结构由 cola 的模板绑定在运行时生成数据驱动的 DOM 创建模板自身不掺杂ng-*、v-*之类的框架指令也没有任何内联事件处理器。全量国际化strings.js 与 i18n! 插件All strings are easily internationalized在示例中体现为每组件一个 strings.js 字典并通过render.replacei18n!插件注入模板。例如 examples/cujo/app/controls/strings.jsdefine({ itemsLeft: { zero: strong/strong items left, one: strong/strong item left, many: strong/strong items left }, filter: { all: All, active: Active, completed: Completed }, clearCompleted: Clear completed });filters 复数规则支持 zero/one/many 三种数量形态配合 controls/structure.css 中基于.remaining-zero/.remaining-one状态类的显示切换实现英文复数语义的完整国际化。页脚组件 footer/strings.js 也把edit、createdBy、partOf等全部文案抽离成字典。OOCSS 实践结构与主题分离、零直接样式操作原文档专门列出 OOCSS面向对象 CSS原则在本示例中的体现结构 CSS 与主题skin分离、视觉状态通过 CSS 类变化实现、零直接样式操作。结构样式独立成文件每个组件携带structure.css如 list/structure.css、controls/structure.css内容只关心显示/隐藏这类结构状态不带配色、字体等主题内容状态由类驱动例如 list/structure.css 用.todos-one #toggle-all, .todos-many #toggle-all { display: block; }控制全选框在恰好 0 条待办时隐藏controls/structure.css 用.remaining-zero .remaining-count-zero { display: inline; }切换剩余数量文案状态类由 wire 自动维护spec 中的setTodosTotalState/setTodosRemainingState/setTodosCompletedState均通过wire/dom/transform/cardinality创建向根节点追加todos-*、remaining-*、completed-*前缀类见 main.js控制器只更新数值从不直接操作styleCSS 文件通过css!插件声明式加载如css: { module: css!app/list/structure.css }在 spec 中与模板、字典并列描述。数据层cola 双向链接与 localStorage 适配器数据管理同样走声明式路线。spec 中创建了一个基于cola/adapter/LocalStorage的存储适配器以todos-cujo作为 localStorage 键并与todos集合双向绑定todoStore: { create: { module: cola/adapter/LocalStorage, args: todos-cujo }, bind: { to: { $ref: todos } } }, todos: { create: { module: cola/Collection, args: { strategyOptions: { validator: { module: app/create/validateTodo } } } }, before: { add: cleanTodo | generateMetadata, update: cleanTodo } }值得注意的源码细节校验策略注入cola/Collection通过strategyOptions.validator注入 validateTodo.js该模块返回{ valid, errors }结构未通过校验的待办缺少非空text会附带errors: [{ property: text, message: missing }]AOP 式操作前拦截before.add/before.update声明了数据操作前的转换管线——新增时先执行cleanTodo | generateMetadata清洗再补元数据更新时执行cleanTodo。cleanTodo.js 负责text.trim()与complete布尔化generateMetadata.js 为无主键的 localStorage 数据生成 GUID 风格id与dateCreated时间戳排序依据视图绑定listView.bind将列表绑定到todoscomparator: dateCreated指定排序字段bindings映射数据字段到 DOM——text双向绑定label与.edit输入框complete绑定.toggle复选框并挂载自定义处理器setCompletedClass。自定义数据链接处理器list/setCompletedClass.js 演示了 cola 支持自定义 DOM 链接处理器return function (node, data, info) { node.className data[info.prop] ? completed : ; };它按绑定属性的布尔值直接设置节点className把已完成视觉状态交给类而非行内样式——这正是visual state changes done via CSS视觉状态变化经由 CSS原则的数据层印证。源码注释也明确指出其意图是展示默认处理器之外可以做任何事的扩展点。控制器更偏向中介者的单一模块spec 注释指出典型 cujo 应用会有多个甚至许多个小型视图控制器而本应用相对简单因此采用单一控制器 中介者mediator风格——它不直接操作 DOM而是响应多个视图的事件并协调数据。见 examples/cujo/app/controller.js控制器通过属性注入获得todos集合与createTodo/removeTodo/updateTodo组合操作compose形式beginEditTodo/endEditTodo处理双击编辑空文本的待办按应用规范被移除endEditTodo中/\S/.test(todo.text)判断removeCompleted遍历移除complete为真的待办toggleAll依据主复选框状态批量更新updateCount汇总总数、已完成数并同步主复选框与三类计数节点。spec 中通过on与connect把事件接到这些方法main.json: { createView: { submit:form: createTodo }, listView: { click:.destroy: removeTodo, change:.toggle: updateTodo, click:#toggle-all: toggleAll, dblclick:label: todos.edit, change,focusout:.edit: todos.submit, submit:form: todos.submit }, controlsView: { click:#clear-completed: removeCompleted } }事件委托基于事件目标选择器如click:.destroy由wire/on插件在 wire 组合阶段完成挂接因此应用代码中没有任何addEventListener/querySelector绑定逻辑。环境垫片而非抽象poly 与零隐式依赖原文档最后两条要点是关于运行环境的JavaScript 环境被垫片shimmed而非抽象abstracted应用代码直接按现代标准书写poly/es5在preloads中先行加载为老旧浏览器补齐 ES5 能力而不是把代码包进自定义的抽象 API。run.js注释明确写了curl is not a global, only define即整个应用不依赖任何全局变量应用代码无显式依赖DOMReady应用生命周期甚至 DOMReady 本身由 wire 透明管理——能在 DOMReady 前做的事提前做不能做的延后做业务代码里看不到window.onload或$(document).readyDOM 查询引擎DOM 引用全部通过dom!/dom.first!/dom.all!这类 wire 引用表达式声明式取得如{ $ref: dom.first!.count, at: controlsView }代码中不出现document.getElementByIdDOM 事件库事件监听由wire/on声明式装配无 jQuery 式事件绑定。controller.js 中的updateRemainingCount甚至展示了自优化函数的写法首次调用时嗅探textContent是否可用随后永久切换为最优实现这同样是面向现代标准、以垫片兜底哲学的微观体现。运行与扩展方式依赖安装项目使用 npm 管理依赖package.jsontodomvc-common、curl、cola、poly、when、wire、meld均在dependencies中需注意wire、cola等包经由 AMDpackages路径映射引用且run.js中的 location 指向bower_components目录实际运行时需保证对应目录或加载路径存在本地运行通过任意静态文件服务器托管examples/cujo/目录在浏览器打开index.html即可看到应用从空#todoapp容器被 wire 逐步渲染出完整界面扩展思路新增组件只需造一个组件目录模板 字典 辅助模块→ 在 main.js 中声明 render/insert/bind → 在 controller 中补充方法即可被 wire 自动装配符合 cujo组合与逻辑分离的既定模式。总结通过这份 TodoMVC 示例可以看到 cujoJS 不同于常规框架的架构取向wire 把装配从逻辑中剥离一份 spec 即可纵览整棵组件树cola 以双向数据链接消灭了手工 DOM 同步OOCSS 让视觉状态完全由状态类驱动poly 垫片让代码可以面向现代标准书写而模板、字典、样式、控制器的物理分文件保证了每个组件既是可读的、也是可替换的。对希望理解声明式 IoC 装配、数据绑定与组件化边界设计的开发者而言examples/cujo 是一份极具参考价值的完整范例。【免费下载链接】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),仅供参考
返回列表