javascript 非常大的单页应用程序设计问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13729788/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Very large single page application design problems
提问by cpeele00
I am currently writing whats going to be a very, very large single page web/javascript application.
我目前正在编写一个非常非常大的单页 web/javascript 应用程序。
Technologies I am using are ASP.NET MVC4, jquery, knockout.js and amplify.js.
我使用的技术是 ASP.NET MVC4、jquery、knockout.js 和 amplify.js。
The problem I am having is that most if not all of the single page application examples are for smaller applications where having all of the script templates (be it jquery, handlbars, etc...) are all in the same file along with the rest of the html code. This is fine for smaller apps but the application I am building is an entire maintenance logistics application with many, many, many screens.
我遇到的问题是,大多数(如果不是全部)单页应用程序示例都是针对较小的应用程序,其中所有脚本模板(无论是 jquery、handlbars 等)都与其他脚本模板一起位于同一个文件中的 html 代码。这对于较小的应用程序来说很好,但我正在构建的应用程序是一个完整的维护物流应用程序,有很多很多很多屏幕。
The approach I took so far is I have an outer shell (my main index.cshtml file) and I am using jquery's load() method to load or rather inject the particular file I need when a user makes a certain selection.
到目前为止,我采用的方法是我有一个外壳(我的主要 index.cshtml 文件),并且我使用 jquery 的 load() 方法来加载或者更确切地说是在用户进行特定选择时注入我需要的特定文件。
example:
例子:
function handleLoginClick(){
App.mainContentContainer.delegate('#btnLogin', 'click', function() {
App.loadModule('Home/ProductionControlMenu', App.MainMenuView.render());
});
}
here's the code for the App.loadModule function:
这是 App.loadModule 函数的代码:
App.loadModule = function(path, callback){
App.mainContentContainer.load(App.siteRoot + path, callback);
};
All was going well until I needed to actually start interacting with the various form elements on the newly loaded screen. jquery can't seem to get a handle on them directly. I can't use .live() or .delegate() because they aren't events, they are textboxes and buttons and sometimes I need to change their css classes.
一切都很顺利,直到我需要真正开始与新加载的屏幕上的各种表单元素进行交互。jquery 似乎无法直接处理它们。我不能使用 .live() 或 .delegate() 因为它们不是事件,它们是文本框和按钮,有时我需要更改它们的 css 类。
They only way I found is to get a handle on an element from the outer shell (something that wasn't loaded via .load() ) and use jquery's .find() method like so:
我发现他们唯一的方法是从外壳(不是通过 .load() 加载的东西)获取一个元素的句柄,并使用 jquery 的 .find() 方法,如下所示:
App.mainContentContainer.find('#btnLogin').addClass('disabled');
clearly I don't want to have to do something like this everytime I need to interact with or even retrieve values from a form element.
显然,每次我需要与表单元素交互甚至从表单元素检索值时,我都不想做这样的事情。
Does anybody have any ideas as to how I can create a maintainable very large single page application with potentially hundreds of .html files without having to have all that html code located in a single file and still get around this .load() issue I am having?
有没有人知道我如何创建一个可维护的非常大的单页应用程序,其中可能有数百个 .html 文件,而不必将所有 html 代码都放在一个文件中,并且仍然解决这个 .load() 问题我是有吗?
Any thoughts would be greatly appreciated. :-)
任何想法将不胜感激。:-)
V/R
V/R
Chris
克里斯
UPDATE
更新
I thought I'd post an update and as to how things went and what worked. After much research I decided to go with Google's AngularJS Javascript framework. It simplified the ordeal exponentially and I would definitely, definitely advise all who are looking into making a large SPA to give it a look.
我想我会发布更新以及事情的进展和工作方式。经过大量研究,我决定使用 Google 的 AngularJS Javascript 框架。它以指数方式简化了磨难,我绝对会建议所有正在考虑制作大型 SPA 的人看一看。
Links:
链接:
Main Site http://angularjs.org/
Awesome free short videos on Angular: http://www.egghead.io/
Angular 上很棒的免费短视频:http: //www.egghead.io/
回答by Bryan A
This is actually a very complicated question as it really gets down to the design of your architecture.
这实际上是一个非常复杂的问题,因为它真正涉及到您的架构设计。
For large-scale single-page applications, it's best to use some sort of front-end MV* style framework such as backbone.js, which ties in to jQuery quite usefully. You should also think about using some sort of dependency management framework such as require.jsin order to load your scripts and dependencies asynchronously, and even better -- use the AMD pattern in your application design to make your architecture modular and easier to manage.
对于大型单页应用程序,最好使用某种前端 MV* 风格的框架,例如Backbone.js,它与 jQuery 的联系非常有用。您还应该考虑使用某种依赖项管理框架,例如require.js,以便异步加载您的脚本和依赖项,甚至更好——在您的应用程序设计中使用 AMD 模式使您的架构模块化且更易于管理。
As far as how this relates to your MVC4 project, you have some options:
至于这与您的 MVC4 项目的关系,您有一些选择:
- Do you want to use MVC as a "service layer" of sorts, that simply returns JSON objects, allowing your front-end to do the markup/template creation (think handlebars.js), or
- Do you want your MVC project to return partial views (HTML) as a response, where you leverage the Razor templating system and simply use the front end to display what comes back from the server?
- 您想将 MVC 用作各种“服务层”,它只返回 JSON 对象,允许您的前端进行标记/模板创建(想想handlebars.js),还是
- 您是否希望您的 MVC 项目返回部分视图 (HTML) 作为响应,您在其中利用 Razor 模板系统并简单地使用前端来显示从服务器返回的内容?
Either way, you will have to devise a way to handle front-end events and navigation (backbone provides both of these things when coupled with jQuery). Even more complicated is the pattern you choose to notify one view of another view's activities (there are many ways to do this) -- a publish/subscribe pattern for example.
无论哪种方式,您都必须设计一种方法来处理前端事件和导航(当与 jQuery 结合使用时,backbone 提供了这两种功能)。更复杂的是您选择将另一个视图的活动通知一个视图的模式(有很多方法可以做到这一点)——例如发布/订阅模式。
I hope I have helped a bit, I know I'm not fully answering the question, but the answer could get really long!
我希望我有所帮助,我知道我没有完全回答这个问题,但答案可能会很长!
回答by Misha Reyzlin
Lots of things are wrong with your approach. What I'd recommend is to watch some presentations on how people build Single Page Applications and what tooling is mostly used.
你的方法有很多问题。我推荐的是观看一些关于人们如何构建单页应用程序以及主要使用哪些工具的演示。
This seems like something reasonable: http://singlepageappbook.com/
这似乎是合理的:http: //singlepageappbook.com/
You will at least want
你至少会想要
- some kind of modules system (I recommend AMD –?http://requirejs.org)
- an MV* framework (Backbone, Ember.js etc.)
- DOM/AJAX Framework (jQuery, Mootools etc.). Some frameworks offer this and all of the above (Dojo, YUI, Sencha)
- build solution (to have different environment in development / production)
- 某种模块系统(我推荐 AMD –?http://requirejs.org)
- 一个 MV* 框架(Backbone、Ember.js 等)
- DOM/AJAX 框架(jQuery、Mootools 等)。一些框架提供了这一点以及以上所有内容(Dojo、YUI、Sencha)
- 构建解决方案(在开发/生产中有不同的环境)
Couple of good links:
几个好的链接:
回答by Adam Gent
If you don't need a complicated truly SOFEASingle Page App then I recommend you go the PJAXroute.
如果您不需要复杂的真正SOFEA单页应用程序,那么我建议您使用PJAX路线。
Then you just write your app as a normal web 1.0 app with the performance benefits of a single page load. I urge you to consider this an option as it allows you to do most of your validation work server side.
然后,您只需将您的应用程序编写为具有单页加载性能优势的普通 Web 1.0 应用程序。我敦促您考虑这是一个选项,因为它允许您在服务器端完成大部分验证工作。
The idea is very simple on every response your sending the whole page back minus the header and footer (which contains the javascript and css includes). DOM rendering time is incredible fast these days... whats not is a full page reload, So don't worry about the size of the HTML your returning back.
这个想法对于您将整个页面发送回减去页眉和页脚(其中包含 javascript 和 css 包括)的每个响应都非常简单。现在 DOM 渲染时间快得令人难以置信……重载整页不是这样,所以不要担心返回的 HTML 的大小。
Also the "PJAX way" is much easier to cache, Google SEO friendlyand is in fact what the new Basecamp does.
此外,“PJAX 方式”更容易缓存,对谷歌搜索引擎优化友好,事实上新 Basecamp 就是这样做的。
回答by TConere
Note: Wanted this to be a comment not an answer but don't have enough exp to post comments ;(
[any corrections by community members welcome!]
注意:希望这是评论而不是答案,但没有足够的经验来发表评论;(
[欢迎社区成员进行任何更正!]
Important points to consider for single page apps:
单页应用程序需要考虑的要点:
- Lazy loadingis vital as you don't want hundreds of js files loaded straight away as user first loads the page (very slow load time).
- Good File organisation,- helps making changes easier, reduces complexity a bit and promotes reusable components. Makes testing of components easier.
- Testing,- Since single page apps have a lot of javascript going on under the hood you would need a test framework for automatically testing components. This testing is on top of the tests you would use to confirm if certain user controls are rendered etc as you wouldn't want a viewless component making an ajax call to server when it shouldn't etc.
- 延迟加载至关重要,因为您不希望在用户首次加载页面时立即加载数百个 js 文件(加载时间非常慢)。
- 良好的文件组织- 有助于使更改更容易,稍微降低复杂性并促进可重用组件。使组件的测试更容易。
- 测试,- 由于单页应用程序在后台运行了大量 javascript,因此您需要一个测试框架来自动测试组件。此测试位于您用于确认是否呈现某些用户控件等的测试之上,因为您不希望无视图组件在不应该等时向服务器进行 ajax 调用。
+1 for gryzzly's point about using a framework.
对于 gryzzly 关于使用框架的观点,+1。
Sencha have a nice MVC like framework for their ExtJs product. They have data stores, ajax, lazy loading, class hierarchies and a lot more all bundled into the package. They have a good api pagealso to lookup object properties and methods (handy since there doesn't seem to be any intellisense for javascript :/ ). Their api page is; as far as I know, an example of a single page app. I know much of the stuff ExtJs does you can find an open source alternative but I like that it's just the one library and I don't have to download a couple of different frameworks to do various operations. [note: I have no affiliation to Sencha except for being a customer of theirs and like their stuff.]
Sencha 为他们的 ExtJs 产品提供了一个很好的类似 MVC 的框架。他们将数据存储、ajax、延迟加载、类层次结构等等都捆绑到包中。他们还有一个很好的 api 页面来查找对象属性和方法(很方便,因为 javascript :/ 似乎没有任何智能感知)。他们的 api 页面是;据我所知,一个单页应用程序的例子。我知道 ExtJs 所做的很多事情,你可以找到一个开源替代品,但我喜欢它只是一个库,我不必下载几个不同的框架来执行各种操作。[注意:除了作为他们的客户并喜欢他们的东西之外,我与 Sencha 没有任何关系。]
Conclusion:
结论:
I'd say it would be quite difficult to manage a large single page app without using some client side framework; whether open source or not, and without using some architectural pattern like a client side MVC.
我想说如果不使用某些客户端框架,管理大型单页应用程序将非常困难;无论是否开源,并且不使用客户端 MVC 等架构模式。
Single page apps I think are more complicated so your team would need to be quite handy at understanding the concept of a single page app and how to implement it. If you pull it off the site will be amazing in terms of user experience.
我认为单页应用程序更复杂,因此您的团队需要非常方便地理解单页应用程序的概念以及如何实现它。如果您将其从网站上撤下,则在用户体验方面将是惊人的。
回答by B.V.S Bharat Kumar
To handle large size single page application it's recommended to break the individual module into different apps.
example: apps like search page app and result page app.
要处理大型单页应用程序,建议将单个模块分解为不同的应用程序。
例如:搜索页面应用程序和结果页面应用程序等应用程序。
回答by marko
I would recommend to use Sammy.js and split various viewmodels in knockout to a separate url. And if you're using asp.net mvc 4, use partial views (user controls) so that you put all code in one file. And name all and split all js-code in a meaningful way, filename and namespace in js. That will help a lot in maintainability and your own sanity in the long run. And use common sense!
我建议使用 Sammy.js 并将淘汰赛中的各种视图模型拆分为单独的 url。如果您使用的是 asp.net mvc 4,请使用部分视图(用户控件),以便将所有代码放在一个文件中。并以有意义的方式命名所有并拆分所有 js 代码,js 中的文件名和命名空间。从长远来看,这将对可维护性和您自己的理智有很大帮助。并使用常识!
回答by slebetman
The way I did it was to include javascript code related to the template along with the template itself. And then load the whole template+script thing using ajax. If you want to try this be warned that most browsers don't execute <script>
tags injected into the page. Especially if done using innerHTML
. As such you should eval
the script tags yourself (alternatively you can use document.createElement to inject the script but this does not offer any additional advantages compared to eval since the browser will blindly execute the script anyway).
我这样做的方法是包含与模板相关的 javascript 代码以及模板本身。然后使用ajax加载整个模板+脚本。如果您想尝试此操作,请注意大多数浏览器不会执行<script>
注入页面的标签。特别是如果使用innerHTML
. 因此,您应该eval
自己标记脚本(或者,您可以使用 document.createElement 来注入脚本,但这与 eval 相比并没有提供任何额外的优势,因为无论如何浏览器都会盲目地执行脚本)。
In my case, to make it easier to grab the html and script portions of the template I store my templates in XML files instead of plain old HTML. That way I can simply use .responseXML
of the ajax request to parse the template. My template has the following basic structure:
就我而言,为了更容易获取模板的 html 和脚本部分,我将模板存储在 XML 文件中,而不是普通的旧 HTML。这样我就可以简单地使用.responseXML
ajax 请求来解析模板。我的模板具有以下基本结构:
<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>
You also need to remember to configure your server to reply with the correct content type for you templates. Otherwise resultXML
won't work. This is not the only way to implement this system of course. You could simply save your templates as HTML and then parse that HTML to extract scripts to execute.
您还需要记住配置您的服务器以使用正确的内容类型为您的模板进行回复。否则resultXML
将无法工作。当然,这不是实施该系统的唯一方法。您可以简单地将模板保存为 HTML,然后解析该 HTML 以提取要执行的脚本。
The main bulk of your code, the functions, constructors, objects etc. can be included in a js file. The template script only need to call those functions to tell the rest of the page how to work with the template.
大部分代码、函数、构造函数、对象等都可以包含在 js 文件中。模板脚本只需要调用这些函数来告诉页面的其余部分如何使用模板。
If you further separate your data from your template and only populate the template with data on the page or make a separate ajax request for JSON data then you can configure your server to make the browser cache your templates. This is especially useful for often used templates (such as templates for dialog boxes). This allows the browser to download the template only once and use the cached version the next time you call the template.
如果您进一步将数据与模板分开,并且仅使用页面上的数据填充模板或对 JSON 数据发出单独的 ajax 请求,那么您可以配置您的服务器以让浏览器缓存您的模板。这对于经常使用的模板(例如对话框模板)尤其有用。这允许浏览器仅下载模板一次,并在您下次调用模板时使用缓存的版本。
Anyway, that's how I did it last time. It scaled well enough to serve facebook users (the web app was a facebook app). Just sharing my experience. Hope it helps.
不管怎样,我上次就是这样做的。它的规模足以为 facebook 用户提供服务(网络应用程序是一个 facebook 应用程序)。只是分享我的经验。希望能帮助到你。
回答by xangxiong
I've written a few huge single page application using Dojo Toolkit. I'm pretty sure whatever JavaScript framework you choose will probably work for you. I use Dojo because it provides me with features that makes huge single page application development easier to manage.
我已经使用 Dojo Toolkit 编写了一些巨大的单页应用程序。我很确定您选择的任何 JavaScript 框架都可能适合您。我使用 Dojo 是因为它为我提供了使大型单页应用程序开发更易于管理的功能。
You can use Dojo's widget system to help you define all your screens and forms as widgets and then when you need them you can just instantiate and insert them wherever you need it. When ever you want to get rid of it, you can simply call destroy
or destroyRecursive
on that particular widget and its gone. Dojo's widget system also help you separate your HTML from your JavaScript, but still keep it together so that they are not located all over the place.
您可以使用 Dojo 的小部件系统来帮助您将所有屏幕和表单定义为小部件,然后当您需要它们时,您可以将它们实例化并插入到您需要的任何位置。当你想摆脱它时,你可以简单地调用destroy
或destroyRecursive
那个特定的小部件,它就消失了。Dojo 的小部件系统还可以帮助您将 HTML 与 JavaScript 分开,但仍将它们保持在一起,以免它们无处不在。
I've include a simple widget definition for a Login form.
我已经为登录表单包含了一个简单的小部件定义。
This is the HTML template.
这是 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>
This is the JavaScript portion for the widget.
这是小部件的 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
}
});
});
There are a few benefits that the widget system provides:
小部件系统提供了一些好处:
- The HTML template will be loaded for you when needed
- You can have widgets within widgets. Dojo takes care of instantiating and even destroying those widgets for you
- You can use dojo's simple template language to help with inserting strings. The example above uses
${form_name}
. If you want to use a more sophisticate facility dojo also supports a Django similar syntax templating language. This will allow you to use most of the tags available with Django like for, if-then-else, etc. data-dojo-attach-point
is very helpful. If you use this, you will never need to assign id to a DOM element anymore. You will not need to locate the element in your HTML DOM tree. The variable you name indata-dojo-attach-point
will be auto assign to reference the widget or DOM element you have defined in your template. The example above uses the_usrfld
,_pwdfld
, and_lgbtn
.data-dojo-attach-event
is also very helpful. If you use this, you will not have to manully add an event hook to the button, Dojo's widget system will hook it for you and will also clear the hook when your widget is destroyed.- If you use Dojo's build system, which is a system that takes all your JavaScript codes and compresses them, Dojo will replace your template with the actual HTML so that when you load your widget in production mode, Dojo doesn't have to make another AJAX request to grab your template.
- 需要时会为您加载 HTML 模板
- 您可以在小部件中使用小部件。Dojo 负责为您实例化甚至销毁这些小部件
- 您可以使用 dojo 的简单模板语言来帮助插入字符串。上面的例子使用
${form_name}
. 如果您想使用更复杂的工具,dojo 还支持类似 Django 的语法模板语言。这将允许您使用 Django 可用的大多数标签,如 for、if-then-else 等。 data-dojo-attach-point
非常有帮助。如果您使用它,您将不再需要将 id 分配给 DOM 元素。您不需要在 HTML DOM 树中定位该元素。您命名的变量data-dojo-attach-point
将被自动分配以引用您在模板中定义的小部件或 DOM 元素。上面的示例使用_usrfld
,_pwdfld
和_lgbtn
。data-dojo-attach-event
也很有帮助。如果您使用它,您将不必手动向按钮添加事件挂钩,Dojo 的小部件系统会为您挂钩,并且还会在您的小部件被销毁时清除该挂钩。- 如果您使用 Dojo 的构建系统,该系统获取您所有的 JavaScript 代码并对其进行压缩,Dojo 将用实际的 HTML 替换您的模板,这样当您在生产模式下加载您的小部件时,Dojo 不必再创建另一个 AJAX请求获取您的模板。
These are just some of the features I use daily when I develop my projects. Hopefully it will provide you with some insight that you can use to make your decision when choosing the proper JavaScript framework for your project. As a side note, I'm not advocating Dojo or anything, just wanted to share what works for me.
这些只是我在开发项目时每天使用的一些功能。希望它能为您提供一些见解,以便您在为项目选择合适的 JavaScript 框架时做出决定。作为旁注,我不提倡 Dojo 或任何东西,只是想分享对我有用的东西。