你将如何组织大型复杂的Web应用程序(参见基本示例)? [英] How would you organize a large complex web application (see basic example)?

查看:158
本文介绍了你将如何组织大型复杂的Web应用程序(参见基本示例)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只是为了让事情变得有趣,并关闭我最后的开放性问题,以一种体面的架构,以良好的组织方式实现以下功能的解决方案获得了很好的收益。完整的代码在jsfiddle上,并随时提出任何问题:)



通常如何组织客户端非常丰富的复杂Web应用程序。我创造了一个有创意的例子,以表明如果对大型应用程序的管理不好,容易陷入的混乱。随意修改/扩展这个例子 - http://jsfiddle.net/NHyLC/1/



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


  1. 最少必须有15个字符,
    在多个空格修剪
    之后才能一个。

  2. 如果添加评论被点击,但是在删除
    多个空格后,
    的大小小于15,然后显示一个弹出式
    并显示错误。

  3. 指示剩余的字符数量,
    用颜色编码汇总。灰色表示
    小评论,棕色表示
    媒体评论,橙色大
    评论和红色评论溢出。

  4. 一个评论只能每15秒提交
    。如果评论是
    提交太早,显示弹出
    与适当的错误消息。

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




  • 这应该是一个小部件或某种打包功能。

  • 像15秒的注释,最小15个字符的注释属于一些应用范围广泛的策略,而不是嵌入到每个widget中。

  • 硬编码值太多了。 >
  • 没有代码组织。模型,视图,控制器都捆绑在一起。不是MVC是组织丰富的客户端Web应用程序的唯一方法,但在这个例子中没有。



你会怎么样关于清理这个?应用一点MVC / MVP的方式?



这是一些相关的功能,但如果您看到jsfiddle上的整个代码,将会更有意义:

  / ** 
*处理注释更改。
*更新字符数。
*指示进度
* /
函数handleCommentUpdate(comment){
var status = $('。comment-status');

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

/ **
*注释是否有效提交
*但首先,检查是否都很好。
* /
函数commentSubmittable(comment){
var notTooSoon =!isTooSoon();
var notEmpty =!isEmpty(comment);
var hasEnoughCharacters =!isTooShort(comment);

return notTooSoon&& notEmpty&& hasEnoughCharacters;
}

/ **
*提交评论。
*但首先,检查一切都好!
* /
$('。add-comment')。click(function(){
var comment = $('。comment-box')。val();

//提交评论,假ajax调用
if(commentSubmittable(comment)){
..
}

//显示一个弹出if评论主要是空格
if(isTooShort(comment)){
if(comment.length< 15){
//闪烁状态消息
}
else {
弹出窗口(注释长度必须至少为15个字符);
}
}
//显示弹出窗口是否提交的评论
else if (isTooSoon()){
弹出窗口(每15秒只允许一个注释);
}

});

编辑1:



@matpol感谢您提供了一个包装器对象和插件。这对现有的混乱实在是一个很大的改善。但是,插件不是独立的,正如我所说,它将成为一个更大的复杂应用程序的一部分。客户端/服务器端的应用范围广泛的策略将决定注释的最小/最大长度,用户评论的频率等等。当然,插件可以将这些信息作为参数提供。



此外,对于富客户端应用程序,数据必须与其HTML表示形式分离,因为应用程序是数据可以保存多个服务器往返 - 可以将东西存储在本地,并在服务器上定期更新,或者在应用程序本身的有趣事件(例如窗口关闭时)中定期更新。这就是为什么我不太喜欢插件方法。它可以像提供一个打包的表示一样工作,但它仍然以DOM为中心,当您在应用程序中有20个这样的插件时,这将是有问题的,这不是一个荒谬的数字。

解决方案

我会这样做的方式是3倍。


  1. 在命名空间内的小型定义良好的类中封装javascript

  2. Javascript类应该有HTML,它们需要注入作为依赖,允许 out-of-browser单元测试

  3. 移动一样多客户端功能尽可能地服务于服务器,并使用称为AHAH的概念

Javascript name-spacing / p>

这可以轻松实现,已经在其他帖子中被覆盖,例如这个是否有简明在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 是一种非常古老的技术,在网络开发方面已有相当长的一段时间,尽管在纯粹的简单性中,网络爱好者正在复苏。但是,哲学必须在建筑技术层面上进行购买,并且必须被用作所有客户端的替代品,例如:验证,显示/隐藏动态内容,计算等。



您可能曾经附加一个客户端复杂性的onClick事件:

  $('# someElement')。click(function(){
//这里插入复杂的客户端功能,例如验证输入
// eg var isValid = $(this).val()< minimumCommentLength;
//根据javascript代码的结果更新页面
// eg $('#commentTooLong')。show();
})

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

  $('#addCommentButton')点击(function(){
$ .ajax({
url:/ comment / add,
上下文:文档。身体,成功:
函数(responseHTML){
$('body')。html(reponseHTML);
}});
})

显然这是一个简单的例子,但是当有效地使用时,任何JavaScript事件在页面上,简单地触发相同的ajax请求和HTML替换,大大减少了所需的客户端代码的数量。将其移动到可以有效测试的服务器。



AHAH不说话者会认为这不是运行网站的一种表现方式,但是我在56k调制解调器访问的网站以及大规模的公共网站上使用和看到了这种技术。结果当然要慢一点,但是你仍然可以生产100毫秒的小时,这对人类来说几乎是瞬间的。


Just to keep things interesting and close my final open question, the solution that implements the below functionality in a nicely organized manner with a decent architecture gets a good bounty. The full code is on jsfiddle, and feel free to ask any questions :)

How do you usually organize complex web applications that are extremely rich on the client side. I have created a contrived example to indicate the kind of mess it's easy to get into if things are not managed well for big apps. Feel free to modify/extend this example as you wish - http://jsfiddle.net/NHyLC/1/

The example basically mirrors part of the comment posting on SO, and follows the following rules:

  1. Must have 15 characters minimum, after multiple spaces are trimmed out to one.
  2. If Add Comment is clicked, but the size is less than 15 after removing multiple spaces, then show a popup with the error.
  3. Indicate amount of characters remaining and summarize with color coding. Gray indicates a small comment, brown indicates a medium comment, orange a large comment, and red a comment overflow.
  4. One comment can only be submitted every 15 seconds. If comment is submitted too soon, show a popup with appropriate error message.

A couple of issues I noticed with this example.

  • This should ideally be a widget or some sort of packaged functionality.
  • Things like a comment per 15 seconds, and minimum 15 character comment belong to some application wide policies rather than being embedded inside each widget.
  • Too many hard-coded values.
  • No code organization. Model, Views, Controllers are all bundled together. Not that MVC is the only approach for organizing rich client side web applications, but there is none in this example.

How would you go about cleaning this up? Applying a little MVC/MVP along the way?

Here's some of the relevant functions, but it will make more sense if you saw the entire code on 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.");
    }

});

Edit 1:

@matpol Thanks for the suggestion for a wrapper object and plugin. That will really be a big improvement over the existing mess. However, the plugin is not independent and as I mentioned, it would be part of a larger complex application. Application wide policies on client/server side would dictate things like minimum/maximum length of a comment, how often can a user comment, etc. Surely the plugin can be fed this information as parameters.

Also, for a rich client side application, the data would have to be separated from its html representation, as many server round-trips can be saved since the application is data-aware and things could be stored locally, and periodically updated on the server, or upon interesting events within the application itself (such as when the window is closed). Here's why I don't really like a plugin approach. It would work as in provide a packaged representation, but it would still be centered around the DOM, which is going to be problematic when you have 20 such plugins in the application, which is not an absurd number by any means.

解决方案

The way in which I would do this is 3 fold.

  1. Encapsulate javascript in small well-defined classes within namespaces
  2. Javascript classes should have HTML they require "injected" into them as dependency allowing out-of-browser unit testing
  3. Move as much client-side functionality to server as possible and use a concept known as AHAH

Javascript name-spacing

This can be achieved easily and has been covered in other posts such as this Is there a "concise" way to do namespacing in JavaScript?

Small encapsulated classes

Javascript code, just like server-side code should be well encapsulated with small cohesive classes and methods. Each class lives in a separate file, named along with the namespace it is in, eg: MyCompany.SomePackage.MyClass.js. Excessive HTTP requests to each file can be saved via combining and minifying these class files at build time.

Dependency Inversion in Javascript

So effectively instead of selecting the elements you require to work with inside your class, like this:

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

You would inject it as such:

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

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

This technique lends itself well to testable javscript and seperation of concerns through MVC style layering of your code.

AHAH and Client-side simplification

AHAH is quite an old technique that has been around for quite some time in web-development, although is making a resurgence amongst web aficionados for its pure simplicity. However, the philosophy must be bought into at more than the architectural technique level and it must be used as a replacement for all your client side javascript eg: validation, showing/hiding dynamic content, calculations etc

Where you may used to have attached an onClick event with client-side complexity:

$('#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();
})

Now you would simply trigger an ajax request back to the server to get the new HTML and simply replace all or some of the elements you are interested in as such:

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

Obviously this is a trivial example, but when used effectively, ANY javascript event on the page, simply fires off the identical ajax request and HTML replacement, greatly reducing the amount of client-side code required. Moving it to the server where it can be effectively tested.

AHAH nay-sayers, will argue that this is not a performant way to run a web-site, however I have used and seen this technique on sites with 56k modem access and also massively scaled public web-sites. The result is of course slower, but you can still produce sub 100 millisecond round trips, which is practically instant to humans.

这篇关于你将如何组织大型复杂的Web应用程序(参见基本示例)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆