18

只是为了让事情变得有趣并结束我的最后一个未解决的问题,以良好组织方式和体面的架构实现以下功能的解决方案获得了丰厚的回报。完整的代码在 jsfiddle 上,有任何问题都可以问:)

您通常如何组织在客户端非常丰富的复杂 Web 应用程序。我创建了一个人为的示例来说明如果大型应用程序管理不善,很容易陷入混乱。随意修改/扩展此示例 - http://jsfiddle.net/NHyLC/1/

该示例基本上反映了在 SO 上发布的部分评论,并遵循以下规则:

  1. 在将多个空格修剪为一个之后,必须至少包含 15 个字符。
  2. 如果Add Comment单击 ,但删除多个空格后大小小于 15,则显示带有错误的弹出窗口。
  3. 指示剩余的字符数量并用颜色编码进行总结。灰色表示小评论,棕色表示中等评论,橙色表示大评论,红色表示评论溢出。
  4. 每 15 秒只能提交一条评论。如果过早提交评论,则显示带有适当错误消息的弹出窗口。

我在这个例子中注意到了几个问题。

  • 理想情况下,这应该是一个小部件或某种打包的功能。
  • 每 15 秒的评论和最少 15 个字符的评论等内容属于某些应用程序范围的策略,而不是嵌入到每个小部件中。
  • 硬编码值太多。
  • 没有代码组织。模型、视图、控制器都捆绑在一起。并不是说 MVC 是组织富客户端 Web 应用程序的唯一方法,但本示例中没有。

你会怎么清理这个?沿途应用一点 MVC/MVP?

下面是一些相关的函数,但如果你在 jsfiddle 上看到了整个代码,它会更有意义:

/**
 * Handle comment change.
 * Update character count. 
 * Indicate progress
 */
function handleCommentUpdate(comment) {
    var status = $('.comment-status');

    status.text(getStatusText(comment));
    status.removeClass('mild spicy hot sizzling');
    status.addClass(getStatusClass(comment));
}

/**
 * Is the comment valid for submission
 * But first, check if it's all good.
 */
function commentSubmittable(comment) {
    var notTooSoon = !isTooSoon();
    var notEmpty = !isEmpty(comment);
    var hasEnoughCharacters = !isTooShort(comment);

    return notTooSoon && notEmpty && hasEnoughCharacters;
}

/**
 * Submit comment.
 * But first, check if it's all good!
 */
$('.add-comment').click(function() {
    var comment = $('.comment-box').val();

    // submit comment, fake ajax call
    if(commentSubmittable(comment)) {
        .. 
    }

    // show a popup if comment is mostly spaces
    if(isTooShort(comment)) {
        if(comment.length < 15) {
            // blink status message
        }
        else {
           popup("Comment must be at least 15 characters in length.");
        }
    }
    // show a popup is comment submitted too soon
    else if(isTooSoon()) {
        popup("Only 1 comment allowed per 15 seconds.");
    }

});

编辑1:

@matpol 感谢您对包装器对象和插件的建议。这将是对现有混乱局面的重大改进。但是,该插件不是独立的,正如我所提到的,它将是一个更大的复杂应用程序的一部分。客户端/服务器端的应用程序范围的策略将规定评论的最小/最大长度、用户评论的频率等。当然,插件可以将此信息作为参数提供。

此外,对于富客户端应用程序,数据必须与其 html 表示分离,因为可以节省许多服务器往返行程,因为应用程序是数据感知的,并且可以将内容存储在本地,并在服务器上定期更新,或者应用程序本身的有趣事件(例如当窗口关闭时)。这就是我不太喜欢插件方法的原因。它可以像提供打包的表示一样工作,但它仍然以 DOM 为中心,当应用程序中有 20 个这样的插件时,这将是一个问题,这无论如何都不是一个荒谬的数字。

4

4 回答 4

23

我这样做的方式是 3 倍。

  1. 将 javascript 封装在命名空间内定义良好的小型类中
  2. Javascript 类应该有 HTML,它们需要“注入”作为依赖项,允许浏览器外的单元测试
  3. 将尽可能多的客户端功能移至服务器,并使用称为 AHAH 的概念

Javascript 命名空间

这可以很容易地实现,并且已经在其他帖子中进行了介绍,例如在 JavaScript 中是否有一种“简洁”的方式来进行命名空间?

小型封装类

Javascript 代码,就像服务器端代码一样,应该用小的内聚类和方法很好地封装。每个类都存在于一个单独的文件中,与它所在的命名空间一起命名,例如:MyCompany.SomePackage.MyClass.js。可以通过在构建时组合和缩小这些类文件来保存对每个文件的过多 HTTP 请求。

Javascript中的依赖倒置

如此有效,而不是选择您需要在类中使用的元素,如下所示:

var MyNamespace.MyClass = function() {
  var elementINeed = $('#IdOfElementINeed');
}

你会这样注入它:

var foo = new MyNamspace.MyClass($('#IdOfElementINeed'));

var MyNamespace.MyClass = function(incomingDependency) {
  var elementINeed = incomingDependency;
}

这种技术非常适合可测试的 javscript 和通过代码的MVC 样式分层来分离关注点。

AHAH 和客户端简化

AHAH是一种相当古老的技术,在 web 开发中已经存在了相当长的一段时间,尽管它因其纯粹的简单性而在 web 爱好者中重新流行。但是,必须在架构技术层面以外的地方购买该理念,并且必须将其用作所有客户端 javascript 的替代品,例如:验证、显示/隐藏动态内容、计算等

您可能曾经在其中附加了具有客户端复杂性的 onClick 事件:

$('#someElement').click(function(){
  // insert complex client-side functionality here, such as validating input
  // eg var isValid = $(this).val() < minimumCommentLength;
  // update page based on result of javascript code
  // eg $('#commentTooLong').show();
})

现在您只需触发一个 ajax 请求返回到服务器以获取新的 HTML,并简单地替换您感兴趣的所有或部分元素:

$('#addCommentButton').click(function(){
  $.ajax({ 
    url: "/comment/add", 
    context: document.body, success: 
    function(responseHTML){
        $('body').html(reponseHTML);
      }});
})

显然这是一个微不足道的例子,但如果有效使用,页面上的任何 javascript 事件都会简单地触发相同的 ajax 请求和 HTML 替换,从而大大减少所需的客户端代码量。将其移至可以对其进行有效测试的服务器。

啊哈反对者会争辩说,这不是一种运行网站的高效方式,但是我已经在具有 56k 调制解调器访问权限的站点以及大规模扩展的公共网站上使用并看到了这种技术。结果当然更慢,但你仍然可以产生低于 100 毫秒的往返行程,这对人类来说几乎是即时的。

于 2010-06-13T10:04:45.830 回答
4

Matpol 对提供的具体信息给出了字面解决方案。您的编辑意味着您正在寻找一个更具假设性的问题的答案。换句话说,您正在寻找“方法”。

以这种方式回答问题;您给出的单个示例有点像红鲱鱼。它只是整个应用程序中的一个项目,并阻止我们“从树上看森林”。那么什么是森林?这样问的问题并没有定义它。但是当我说从事一个没有定义的项目是一场噩梦时,我想所有的程序员都会同意。所以,我将重新表述你的问题和答案,“定义项目的过程是什么?”

事实上,我在问一系列问题:

  • 这个应用程序的核心目的是什么?
  • 你希望它什么时候推出?如果我们必须在那个时间的 1/4 内推出它,你会保留哪些功能?
  • 您绝对确定以后需要哪些功能?

很多时候,为了弄清这些问题,我需要问其他业务问题:

  • 你的听众是谁?
  • 他们为什么关心这个网站?是什么让他们回来?
  • 你将如何产生收入?
  • 你的行动号召是什么?如果您可以将所有用户集中到一条路径上,您会选择哪条路径?

希望这些问题会产生一组您可以考虑核心的基础代码。正如您所怀疑的那样,您的核心可能不适合模块化方法。正如您所确定的,您将希望将该核心分解为模型/视图/控制器布局。

但不可避免的是,您需要添加设计绒毛。这让我们回到您的代码示例 - 它的绒毛。Fluff 属于一个插件,与核心分离。这并不是说您的所有插件都应该在单独的 js 文件中交付给用户......但是出于您的开发目的,它们应该被视为模块化,并且独立于核心代码库。

于 2010-06-07T06:27:20.197 回答
3

我要么把它变成一个 jQuery 插件或一个静态对象。

静态对象只是充当一种类型或包装器。我也会把它分解成更小的函数,例如

init()
checkLength()
checkTime()

所以你最终可能会得到类似的东西:

Widget = {

init:function(){//setup events etc},
checkLength:function(){},
checkTime:function(){},
doMessage:function(){}


}
于 2010-03-18T08:46:55.437 回答
1

当前代码是一个良好的开端,可以“扩展”成大型应用程序,避免硬编码,并且只需进行一些更改即可具有清晰的 MVC 分离。

  • 理想情况下,这应该是一个小部件或某种打包的功能

小部件将使重用评论功能更容易,并提供在不同页面/应用程序中的重用。不仅将关注点的封装和分离扩展到表示,还扩展到小部件模型。当您想到评论字段时,将组件的状态视为评论文本是很直观的,但影响其行为的所有参数都可以成为其模型的一部分,包括验证参数。所以,除了评论文本,我会让模型包括:

  • 字符数到大小类别的映射(太小、小、中、大、溢出)。
  • 最大评论提交频率(15 秒)

小部件模型会随着文本的更改而更新尺寸类别。视图监听尺寸类别的变化,尺寸类别值用于更新文本类以产生不同评论长度的 CSS 样式。单击“添加评论”时也会检查尺寸类别。如果它“太小”或“溢出”,则可以显示一个弹出窗口。请注意,添加注释处理程序不检查字符数 - 这在模型中是孤立的 - 它检查大小类别。如有必要,“添加评论”控制器可以使用相同的字符计数映射到模型用于为用户生成有用消息的大小类别。例如,“评论必须至少为 15 个字符”,其中 15 个是从大小类别映射中获取的。

该模型还提供了“剩余字符数”属性,其更改事件用于更新小部件的 UI。从字符到大小类别映射中获取最大字符数。

  • 每 15 秒的评论和最少 15 个字符的评论等内容属于某些应用程序范围的策略,而不是嵌入到每个小部件中。
  • 硬编码值太多。
  • 没有代码组织。模型、视图、控制器都捆绑在一起。并不是说 MVC 是组织富客户端 Web 应用程序的唯一方法,但本示例中没有。

可能有许多类型的评论(例如被评论的不同项目)和不同类型的评论小部件。如果它们都应该具有相同的最小/最大范围,那么它们都应该使用控制评论验证的相同模型值进行参数化。在为评论模型构建数据时,最好在服务器端完成。为该特定评论提取评论文本,并从页面或应用程序配置默认值中提取评论验证值,例如大小类别映射。通过拥有用于生成组件验证模型的中央逻辑,添加新规则变得更加简单——例如“版主可以发布高达 1K 的评论”,成为对一段代码的更改。让组件模型在服务器端计算的另一点是模型也应该在服务器端进行验证 - 客户端验证更多的是用户方便(有些人可能会认为不方便!) - 而不是硬执行。可以禁用 JavaScript,并且可以独立于验证客户端构造 HTTP 请求。

总而言之,这在很大程度上可以看作是组织小部件模型服务器端的生产。通过在服务器端执行此操作,服务器可以强制执行验证规则,并使小部件免受规则和应用程序范围配置的复杂性的影响。

我没有提到 jQuery 或任何 UI 技术,因为无论 UI 技术如何,这种模式都适用于应用程序。您如何应用该模式将在某种程度上是特定于 UI 的,例如如何将验证模型提供给小部件,或者如何将侦听器连接到模型,但该模式的组织级别与 UI 是正交的。主要关注于模型——将其扩展为包括验证方面并在服务器端计算它。一旦到位,组织问题就基本解决了。

于 2010-06-12T22:51:29.983 回答