9

我目前正在编写一个非常非常大的单页 web/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() 问题有?

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

视听/视听

克里斯

更新

我想我会发布一个更新,以及事情的进展和效果。经过大量研究后,我决定使用 Google 的 AngularJS Javascript 框架。它成倍地简化了考验,我肯定会建议所有正在考虑制作大型 SPA 的人来看看。

链接:

主站 http://angularjs.org/

Angular 上很棒的免费短视频: http ://www.egghead.io/

4

8 回答 8

12

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

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

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

  1. 您是否想将 MVC 用作某种“服务层”,它只返回 JSON 对象,允许您的前端创建标记/模板(想想handlebars.js),或者
  2. 您是否希望您的 MVC 项目返回部分视图 (HTML) 作为响应,利用 Razor 模板系统并简单地使用前端来显示从服务器返回的内容?

无论哪种方式,您都必须设计一种方法来处理前端事件和导航(当与 jQuery 结合时,backbone 提供了这两种功能)。更复杂的是您选择将另一个视图的活动通知一个视图的模式(有很多方法可以做到这一点)——例如发布/订阅模式。

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

于 2012-12-05T18:54:27.427 回答
7

你的方法有很多问题。我建议观看一些关于人们如何构建单页应用程序以及最常用的工具的演示。

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

你至少会想要

  • 某种模块系统(我推荐 AMD – <a href="http://requirejs.org">http://requirejs.org)
  • 一个 MV* 框架(Backbone、Ember.js 等)
  • DOM/AJAX 框架(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
于 2012-12-05T18:50:15.713 回答
4

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

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

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

此外,“PJAX 方式”更容易缓存Google SEO 友好,而且实际上是新 Basecamp 所做的。

于 2012-12-06T18:39:00.150 回答
3

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

单页应用程序需要考虑的要点:

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

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

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

结论:

我想说如果不使用某些客户端框架,管理大型单页应用程序将非常困难;无论是否开源,并且不使用诸如客户端 MVC 之类的架构模式。

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

于 2013-03-22T14:44:16.153 回答
0

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

于 2012-12-05T18:40:58.930 回答
0

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

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

<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 应用程序)。只是分享我的经验。希望能帮助到你。

于 2012-12-05T18:47:30.253 回答
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 中可用的大多数标签,例如 for、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 的小部件系统会为您挂钩,并且还会在您的小部件被销毁时清除挂钩。
  6. 如果您使用 Dojo 的构建系统,这是一个获取所有 JavaScript 代码并对其进行压缩的系统,Dojo 将用实际的 HTML 替换您的模板,这样当您在生产模式下加载小部件时,Dojo 就不必制作另一个 AJAX请求获取您的模板。

这些只是我在开发项目时每天使用的一些功能。希望它能为您提供一些见解,您可以在为您的项目选择合适的 JavaScript 框架时做出决定。作为旁注,我不提倡 Dojo 或任何东西,只是想分享对我有用的东西。

于 2012-12-06T06:18:22.903 回答
0

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

于 2017-05-24T22:01:20.937 回答