非常大的单页面应用程序设计问题

时间:2012-12-05 18:18:01

标签: javascript jquery asp.net-mvc-4 angularjs singlepage

我目前正在撰写一篇非常非常大的单页网页/ javascript应用程序。

我使用的技术是ASP.NET MVC4,jquery,knockout.js和amplify.js。

我遇到的问题是,大多数(如果不是全部)单页应用程序示例适用于较小的应用程序,其中包含所有脚本模板(无论是jquery,handlbars等等)都在同一个文件中与其余的HTML代码。这适用于较小的应用程序,但我正在构建的应用程序是一个包含许多很多屏幕的整个维护物流应用程序。

我到目前为止采用的方法是我有一个外壳(我的主index.cshtml文件),并且我使用jquery的load()方法来加载或者更确切地注入用户进行特定选择时所需的特定文件。

示例:

function handleLoginClick(){

    App.mainContentContainer.delegate('#btnLogin', 'click', function() {

        App.loadModule('Home/ProductionControlMenu', App.MainMenuView.render());

    });

}

这是App.loadModule函数的代码:

App.loadModule = function(path, callback){

    App.mainContentContainer.load(App.siteRoot + path, callback);

};

一切顺利,直到我需要实际开始与新加载的屏幕上的各种表单元素进行交互。 jquery似乎无法直接处理它们。我不能使用.live()或.delegate()因为它们不是事件,它们是文本框和按钮,有时我需要更改它们的css类。

他们发现的唯一方法是从外壳获取一个元素的句柄(没有通过.load()加载的东西)并使用jquery的.find()方法,如下所示:

  App.mainContentContainer.find('#btnLogin').addClass('disabled');

很明显,每次我需要与表单元素进行交互甚至检索值时,我都不想做这样的事情。

有没有人想知道如何创建一个可维护的非常大的单页面应用程序,可能有数百个.html文件,而不必将所有html代码放在一个文件中,仍然可以解决这个问题.load()我有问题吗?

任何想法都将不胜感激。 : - )

V / R

克里斯

更新

我以为我会发布一个更新,关于事情的进展和有效的方法。经过大量研究后,我决定采用Google的AngularJS Javascript框架。它以指数方式简化了考验,我绝对会建议所有正在考虑制作大型SPA的人来看看。

链接:

主要网站 http://angularjs.org/

关于Angular的精彩免费短视频: http://www.egghead.io/

8 个答案:

答案 0 :(得分:12)

这实际上是一个非常复杂的问题,因为它真正归结为您的架构设计。

对于大型单页面应用程序,最好使用某种前端MV *样式框架,例如backbone.js,它与jQuery非常有用。您还应该考虑使用某种依赖关系管理框架(如require.js)来异步加载脚本和依赖项,甚至更好 - 在应用程序设计中使用AMD模式,使您的架构模块化,更容易管理。

至于这与你的MVC4项目有什么关系,你有一些选择:

  1. 您是否希望将MVC用作各种“服务层”,只返回JSON对象,允许您的前端进行标记/模板创建(想想handlebars.js)或
  2. 您是否希望MVC项目将部分视图(HTML)作为响应返回,您可以利用Razor模板系统并使用前端显示从服务器返回的内容?
  3. 无论哪种方式,您都必须设计一种方法来处理前端事件和导航(骨干在与jQuery结合时提供这两种功能)。更复杂的是您选择通知另一个视图的活动的一个视图的模式(有很多方法可以做到这一点) - 例如发布/订阅模式。

    我希望我有所帮助,我知道我没有完全回答这个问题,但答案可能会很长!

答案 1 :(得分:7)

你的方法有很多问题。我建议的是观看一些关于人们如何构建单页应用程序以及主要使用什么工具的演示文稿。

这似乎是合理的:http://singlepageappbook.com/

你至少想要

  • 某种模块系统(我推荐AMD - http://requirejs.org
  • MV *框架(Backbone,Ember.js等)
  • DOM / AJAX Framework(jQuery,Mootools等)。一些框架提供了这个和所有上述(Dojo,YUI,Sencha)
  • 构建解决方案(在开发/生产中具有不同的环境)

一些好的链接:

  1. http://nerds.airbnb.com/slides-and-video-from-spike-brehms-tech-talk
  2. http://video.copenhagenjs.dk/video/3413395/simon-hjberg-swipely-building
  3. http://backstage.soundcloud.com/2012/06/building-the-next-soundcloud/
  4. http://www.youtube.com/watch?v=vXjVFPosQHw

答案 2 :(得分:4)

如果您不需要复杂的真正SOFEA单页应用,那么我建议您转到PJAX路线。

然后,您只需将应用程序编写为普通的Web 1.0应用程序,并具有单页面加载的性能优势。我建议您考虑这个选项,因为它允许您完成大部分验证工作服务器端。

对于发送整个页面的每个响应减去页眉和页脚(包含javascript和css包含),这个想法非常简单。这些天DOM渲染时间快得令人难以置信...什么不是整页重新加载,所以不要担心返回的HTML的大小。

此外,“PJAX方式”更容易缓存 Google SEO友好,实际上是new Basecamp does

答案 3 :(得分:3)

注意:希望这是一个评论而不是答案,但没有足够的exp来发表评论;(
[欢迎社区成员进行任何更正!]

单页应用需要考虑的重点:

  • Lazy loading非常重要,因为您不希望在用户首次加载页面时立即加载数百个js文件(加载时间非常慢)。
  • 良好的文件组织, - 有助于简化更改,降低复杂性并促进可重用组件。使组件测试更容易。
  • 测试, - 由于单页应用程序底层有很多javascript,因此您需要一个自动测试组件的测试框架。此测试是在您将用于确认是否呈现某些用户控件等的测试之上,因为您不希望无视组件在不应该等时对服务器进行ajax调用。

+1 gryzzly关于使用框架的观点。

Sencha为他们的ExtJs产品提供了一个很好的MVC框架。它们具有数据存储,ajax,延迟加载,类层次结构以及捆绑到包中的更多内容。他们还有一个good api page来查找对象属性和方法(因为javascript似乎没有任何intellisense:/)。他们的api页面是;据我所知,单页应用程序的一个例子。我知道很多东西ExtJs你可以找到一个开源替代品,但我喜欢它只是一个库,我不必下载几个不同的框架来做各种操作。 [注意:我与Sencha没有任何关系,除非他们是他们的客户并喜欢他们的东西。]

结论:

我会说在不使用某些客户端框架的情况下管理大型单页面应用程序会非常困难;无论是否开源,并且不使用像客户端MVC那样的架构模式。

我认为单页应用程序更复杂,因此您的团队需要非常方便地了解单页应用程序的概念以及如何实现它。如果你把它拉下来,那么在用户体验方面,网站将是惊人的。

答案 4 :(得分:0)

我建议使用Sammy.js并将各种视图模型在knockout中拆分为单独的url。如果您使用的是asp.net mvc 4,请使用部分视图(用户控件),以便将所有代码放在一个文件中。并命名all并以有意义的方式拆分所有js-code,js中的文件名和命名空间。从长远来看,这将有助于提高可维护性和自身的理智。 并使用常识

答案 5 :(得分:0)

我这样做的方法是包含与模板相关的JavaScript代码以及模板本身。然后使用ajax加载整个模板+脚本。如果您想尝试这一点,请注意大多数浏览器不会执行注入页面的<script>标记。特别是如果使用innerHTML完成。因此,您应该自己eval脚本标记(或者您可以使用document.createElement注入脚本,但与eval相比,这不会提供任何额外的优势,因为浏览器无论如何都会盲目地执行脚本)。

就我而言,为了更容易获取模板的html和脚本部分,我将模板存储在XML文件中而不是普通的旧HTML中。这样我就可以简单地使用ajax请求的.responseXML来解析模板。我的模板具有以下基本结构:

<template options...>
    <title>Optional</title>
    <html><![CDATA[

        Template body

    ]]></html>
    <init><![CDATA[

        // code that only needs to execute once

    ]]></init>
    <script><![CDATA[

        // code that needs to run every time
        // the template loads

    ]]></script>
</template>

您还需要记住将服务器配置为使用正确的模板内容类型进行回复。否则resultXML将无效。当然,这不是实现该系统的唯一方法。您可以简单地将模板保存为HTML,然后解析该HTML以提取要执行的脚本。

代码的主要部分,函数,构造函数,对象等可以包含在js文件中。模板脚本只需要调用这些函数来告诉页面的其余部分如何使用模板。

如果您进一步将数据与模板分开并仅使用页面上的数据填充模板或对JSON数据单独发出ajax请求,则可以配置服务器以使浏览器缓存模板。这对于经常使用的模板(例如对话框的模板)特别有用。这允许浏览器仅下载模板一次,并在下次调用模板时使用缓存版本。

无论如何,这就是我上次的表现。它的扩展性足以为Facebook用户提供服务(该网络应用程序是一个Facebook应用程序)。只是分享我的经验。希望它有所帮助。

答案 6 :(得分:0)

我使用Dojo Toolkit编写了一些巨大的单页应用程序。我很确定你选择的JavaScript框架可能对你有用。我使用Dojo,因为它为我提供了使单页面应用程序开发更容易管理的功能。

您可以使用Dojo的窗口小部件系统来帮助您将所有屏幕和窗体定义为窗口小部件,然后在需要时可以实例化并将它们插入到您需要的任何位置。当你想要摆脱它时,你只需在该特定小部件上调用destroydestroyRecursive即可。 Dojo的小部件系统还可以帮助您将HTML与JavaScript分开,但仍然将它们保持在一起,以便它们不会遍布整个地方。

我为登录表单添加了一个简单的小部件定义。

这是HTML模板。

/* mine/forms/Login.html */
<div>
    ${form_name}
    <label>Username</label>
    <input data-dojo-type="dijit.form.TextBox"
           data-dojo-attach-point="_usrfld" />
    <br />
    <label>Password</label>
    <input data-dojo-type="dijit.form.TextBox"
           data-dojo-props="type: 'password'"
           data-dojo-attach-point="_pwdfld" />
    <br />
    <input data-dojo-type="dijit.form.Button"
           data-dojo-props="label: 'Login'"
           data-dojo-attach-event="onClick:_handleLogin" />
</div>

这是小部件的JavaScript部分。

/* mine/forms/Login.js */
define([
    "dojo/_base/declare",
    "dijit/_Widget",
    "dijit/_Templated",
    "dijit/_WidgetsInTemplateMixin",
    "dijit/form/Button",
    "dijit/form/TextBox",
    "dojo/text!./Login.html"
], function(
    declare,
    _Widget,
    _Templated,
    _WidgetsInTemplateMixin,
    Button,
    TextBox,
    template
) {

return declare("mine.forms.Login", [_Widget, _Templated, _WidgetsInTemplateMixin], {
    // assign the template
    templateString: template,

    // signal that we will have widgets within our template and the parser should 
    // locate them and instantiate them
    widgetsInTemplate: true,

    form_name: "My Login Form",

    // place holders that will be referencing the corresponding widgets
    // that I have placed a data-dojo-attach-point on
    _usrfld: null,
    _pwdfld: null,
    _lgbtn: null,

    // a call back function that will be trigger when the Login button is clicked
    _handleLogin: function() {
        var usr = this._usrfld.get('value');
        var pwd = this._pwdfld.get('value');

        // now you have the username & password
        // you can use it to login
    }
});

});

小部件系统提供了一些好处:

  1. 将在需要时为您加载HTML模板
  2. 您可以在小部件中使用小部件。 Dojo负责实例化甚至为你破坏这些小部件
  3. 您可以使用dojo的简单模板语言来帮助插入字符串。上面的示例使用${form_name}。如果你想使用更复杂的设施,dojo也支持Django类似的语法模板语言。这将允许您使用Django提供的大多数标签,如if-then-else等。
  4. data-dojo-attach-point非常有帮助。如果使用它,则永远不需要将id分配给DOM元素。您不需要在HTML DOM树中找到该元素。您在data-dojo-attach-point中命名的变量将自动分配,以引用您在模板中定义的小部件或DOM元素。上面的示例使用_usrfld_pwdfld_lgbtn
  5. data-dojo-attach-event也非常有帮助。如果你使用它,你就不必手动向按钮添加一个事件挂钩,Dojo的widget系统将为你挂钩,并且当你的widget被销毁时也会清除钩子。
  6. 如果你使用Dojo的构建系统,它是一个获取所有JavaScript代码并压​​缩它们的系统,Dojo将用实际的HTML替换你的模板,这样当你在生产模式下加载你的小部件时,Dojo就不必制作另一个AJAX请求以获取模板。
  7. 这些只是我在开发项目时每天使用的一些功能。希望它能为您提供一些洞察力,您可以在为项目选择合适的JavaScript框架时做出决定。作为旁注,我不是在提倡Dojo或其他任何东西,只是想分享对我有用的东西。

答案 7 :(得分:0)

要处理大尺寸单页面应用程序,建议将单个模块分解为不同的应用程序。
示例:搜索页面应用和结果页面应用等应用。