22

我正在构建一个多页 javascript 应用程序。我已经阅读了很多设计模式,并使用松散耦合(发布/订阅事件)的核心/外观/模块方法创建应用程序。

我有一个非常好的系统,可以在部署时将我的所有模块文件和相关依赖项缩小并合并到一个外部 javascript 文件中。为我的应用程序最小化额外的 HTTP 请求是一个设计目标——因此我对 AMD(异步模块定义)不太感兴趣。

我正在使用 Nicholas Zakas 的演示文稿 Scalable JavaScript Application Architecture中界定的指南

http://www.youtube.com/watch?v=vXjVFPosQHw

&&

Addy Osmani 的大型 JavaScript 应用程序架构模式 http://addyosmani.com/largescalejavascript/

&&

这个高级教程,来自 Nettuts 的 Andrew Burgess,编写模块化 JavaScript http://marketplace.tutsplus.com/item/writing-modular-javascript/128103/?ref=addyosmani&ref=addyosmani&clickthrough_id=90060087&redirect_back=true

我的问题是关于如何管理此应用程序的不同页面及其相关模块的建议。我还使用 Backbonejs 的路由器类 w/ballupton 的History.js库来操作 HTML5 历史/状态 API 并在不刷新的情况下动态加载页面,同时保持对不支持 HTML 状态 API 的旧浏览器的向后兼容性。我的所有页面都共享一个通用代码库(单个缩小和压缩的 js 文件)。

这是我正在考虑在我的应用程序中使用的结构的概述: 在此处输入图像描述

它本质上是一种混合方法。上半部分由 Core/Facade/Module 模式和离散的模块组成,这些模块不直接相互交互并通过外观发布/订阅通知。

下半部分由我提出的应用程序结构组成,当状态/url改变时通知“主控制器”,主控制器执行任何全局操作(例如,如果尚未初始化,则初始化我的 UI 的标题和侧边栏菜单)并指示相关子控件运行它的 init() 方法(以及调用 destroy(); 在先前加载的任何控制器上)。每个子控制器(与 ex:主页、日历页面、预订页面等相关)从可用模块池中挑选模块并初始化它们。

这是一个好方法还是我走错了路?我可以看到模块仍然相互独立,这有利于可扩展性。

我还考虑过将路由器和控制器视为离散模块并让它们发布/订阅核心,并且每个控制器以某种方式初始化其页面所需的必要模块。

4

2 回答 2

6

我们为保持历史平稳运行所做的一件事是首先更改 URL。在更改 URL 时会触发事件,路由器将解析 url 然后找出要做什么。此事件也会在页面加载时自动触发。如果您单击一个链接,它只会更改 URL,这相当简单,并且将链接/按钮与应用程序逻辑完全分离。这似乎对我们的应用程序很有效。我们使用了 JQM,但我们放弃了他们的大部分路由器,因为我们从一些 XML 文件中读取了大部分指令,并且没有一堆 HTML 页面可以加载到主视口区域。

我经常看到主干应用程序使用路由器作为核心/中介。这是一个好主意。您可以简单地监听 URL 的更改事件,然后适当地更改页面。这个 Mediator 应该是一个单例,尽管单例更难进行单元测试。

我不一定同意 Backbone 的观点是它对“视图”的定义。视图有点像控制器中的一个动作(从某些角度来看)。那时我们在我们的应用程序中增加了一层分离。我们的视图向模板文件发出 ajax 请求,这些模板文件填充了一些 JSON 和 handlebars.js。我会说你的标题/侧边栏应该只是模板。如果你需要刷新它们,那么看看你可以如何做非常简单,否则你正在考虑创建 4 个新模块:列表的集合、每个项目的模型、集合视图和模型视图。我会将模板与一些更高级别的视图更紧密地结合起来,直到它们需要进一步分解(例如,一些“应用程序/主视图”)。

有了这个模板层,您也可以在不重新编译的情况下进行表面更改,这很好。任何时候您可以将内容放入“元”中,这都是一种胜利(除非它要求您阅读 XML (ha))。作为奖励,您还可以单独缓存模板(或单独缓存它)。

不过,您的架构看起来确实不错,并且是解决问题的有效方法。我要给出的一个提示是不要预先设计。迭代是最好的。您将需要重构。无法提前 3-6 个月预见什么会使您的申请流程更加顺畅。

2013 年 12 月 18 日更新

现在有一天,我们正在使用木偶和更多的 osmani 技巧。在上述项目之上,我们使用 AMD 的替代格式:

define(function(require) {
    var myTemplate = require('hb!mytemplate.handlebars'),
        view = require('myview');
    ...
});

我们还将 marionette 应用程序类与提供请求/响应层的 wreqr 结合使用。这允许我们干净地设置应用程序范围的对象。它还允许我们在不明确说明类名的情况下定义类。这是沙盒的一个很好的方法。例如:

this.app.setHandler('CanvasClass', function() {
    return RaphaelCanvasView;
});

// elsewhere

this.app.request('CanvasClass').text('123', {x:1, y:2});

这一切似乎都很顺利。

您还应该检查 aura js 和 Web 组件。我们的目录结构有点模仿/预测这些概念,但还没有投资它们。

于 2012-11-02T20:02:52.723 回答
2

我认为这是一个很好的方法。我一直在 2 个巨大的商业网络应用程序中开发类似的东西(减去主干,并使用自定义历史管理器)并且效果很好。我也没有使用 AMD,所有交互都由 pub/sub 处理。我最好的灵感之一(我相信你已经知道)是:https ://github.com/aurajs/aura

于 2012-11-09T15:35:58.323 回答