组织 jQuery/JavaScript 代码的最佳方式 (2013)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16736483/
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
Best way to organize jQuery/JavaScript code (2013)
提问by Kivylius
The Problem
问题
This answer has been answered before but are old and not up to date. I have over 2000 lines of code in a single file, and as we all know this is bad practice, especially when i'm looking through code or adding new features. I want to better organize my code, for now and for the future.
这个答案之前已经回答过,但很旧而且不是最新的。我在一个文件中有 2000 多行代码,众所周知这是不好的做法,尤其是当我查看代码或添加新功能时。我想更好地组织我的代码,现在和将来。
I should mention that I'm building a tool (not a simple website) with lots of buttons, UI elements, drag, drops, action listeners/handlers and function in the global scope where several listeners may use the same function.
我应该提到我正在构建一个工具(不是一个简单的网站),其中包含许多按钮、UI 元素、拖放、动作侦听器/处理程序和全局范围内的功能,其中多个侦听器可能使用相同的功能。
Example code
示例代码
$('#button1').on('click', function(e){
// Determined action.
update_html();
});
... // Around 75 more of this
function update_html(){ .... }
...
Conclusion
结论
I really need to organize this code for best use and not to repeat myself and be able to add new features and update old ones. I will be working on this by myself. Some selectors can be 100 lines of code others are 1. I have looked a bit at require.js
and found it kinda repetitive, and actually writing more code than needed . I'm open to any possible solution that fit this criteria and link to resource / examples are always a plus.
我真的需要组织此代码以实现最佳使用,而不是重复自己,并且能够添加新功能并更新旧功能。我将自己解决这个问题。一些选择器可以是 100 行代码,其他是 1 行。我看过一点,require.js
发现它有点重复,实际上编写的代码比需要的多。我对符合此标准的任何可能的解决方案持开放态度,并且资源/示例的链接总是一个加分项。
Thanks.
谢谢。
采纳答案by Sébastien Renauld
I'll go over some simple things that may, or may not, help you. Some might be obvious, some might be extremely arcane.
我将讨论一些可能对您有帮助也可能没有帮助的简单事情。有些可能很明显,有些可能非常神秘。
Step 1: Compartmentalize your code
第 1 步:划分您的代码
Separating your code into multiple, modular units is a very good first step. Round up what works "together" and put them in their own little encased unit. don't worry about the format for now, keep it inline. The structure is a later point.
将您的代码分成多个模块化单元是非常好的第一步。将“一起”工作的内容收集起来,并将它们放在自己的小封闭单元中。现在不用担心格式,保持内联。结构是后点。
So, suppose you have a page like this:
所以,假设你有一个这样的页面:
It would make sense to compartmentalize so that all the header-related event handlers/binders are in there, for ease of maintenance (and not having to sift through 1000 lines).
为了便于维护(并且不必筛选 1000 行),将所有与标头相关的事件处理程序/绑定器都放在那里是有意义的。
You can then use a tool such as Grunt to re-build your JS back to a single unit.
然后,您可以使用 Grunt 等工具将您的 JS 重新构建回单个单元。
Step 1a: Dependency management
步骤 1a:依赖管理
Use a library such as RequireJS or CommonJS to implement something called AMD. Asynchronous Module Loading allows you to explicitely state what your code depends on, which then allows you to offload the library-calling to the code. You can just literally say "This needs jQuery" and the AMD will load it, and execute your code when jQuery is available.
使用 RequireJS 或 CommonJS 之类的库来实现称为AMD 的东西。异步模块加载允许您明确说明您的代码所依赖的内容,然后您可以将库调用卸载到代码中。您可以直接说“这需要 jQuery”,AMD 将加载它,并在 jQuery 可用时执行您的代码。
This also has a hidden gem: the library loading will be done the second the DOM is ready, not before. This no longer halts load-up of your page!
这也有一个隐藏的宝石:库加载将在 DOM 准备好后完成,而不是之前。这不再停止加载您的页面!
Step 2: Modularize
第 2 步:模块化
See the wireframe? I have two ad units. They'll most likely have shared event listeners.
看到线框图了吗?我有两个广告单元。他们很可能有共享的事件侦听器。
Your task in this step is to identify the points of repetition in your code and to try to synthesise all this into modules. Modules, right now, will encompass everything. We'll split stuff as we go along.
您在此步骤中的任务是确定代码中的重复点,并尝试将所有这些合成到模块中。现在,模块将涵盖一切。我们将在进行时拆分东西。
The whole idea of this step is to go from step 1 and delete all the copy-pastas, to replace them with units that are loosely coupled. So, instead of having:
这一步的整个想法是从步骤 1 开始,删除所有的复制面食,用松散耦合的单元替换它们。所以,而不是:
ad_unit1.js
ad_unit1.js
$("#au1").click(function() { ... });
ad_unit2.js
ad_unit2.js
$("#au2").click(function() { ... });
I will have:
我会有:
ad_unit.js
:
ad_unit.js
:
var AdUnit = function(elem) {
this.element = elem || new jQuery();
}
AdUnit.prototype.bindEvents = function() {
... Events go here
}
page.js
:
page.js
:
var AUs = new AdUnit($("#au1,#au2"));
AUs.bindEvents();
Which allows you to compartmentalize between your eventsand your markupin addition to getting rid of repetition. This is a pretty decent step and we'll extend this further later on.
除了摆脱重复之外,这还允许您在事件和标记之间进行划分。这是一个相当不错的步骤,我们稍后会进一步扩展。
Step 3: Pick a framework!
第 3 步:选择一个框架!
If you'd like to modularize and reduce repetitions even further, there are a bunch of awesome frameworks around that implement MVC (Model - View - Controller) approaches. My favourite is Backbone/Spine, however, there's also Angular, Yii, ... The list goes on.
如果你想进一步模块化和减少重复,有很多很棒的框架可以实现 MVC(模型 - 视图 - 控制器)方法。我最喜欢的是 Backbone/Spine,不过,也有 Angular、Yii ……不胜枚举。
A Modelrepresents your data.
一个模型代表你的数据。
A Viewrepresents your mark-up and all the events associated to it
一个视图代表您的标记以及与之关联的所有事件
A Controllerrepresents your business logic - in other words, the controller tells the page what views to load and what models to use.
一个控制器代表了你的业务逻辑-换句话说,控制器告诉页用什么意见负载和什么型号。
This will be a significant learning step, but the prize is worth it: it favours clean, modular code over spaghetti.
这将是一个重要的学习步骤,但奖励是值得的:它更喜欢干净、模块化的代码而不是意大利面条。
There are plenty of other things you can do, those are just guidelines and ideas.
你可以做很多其他的事情,这些只是指导方针和想法。
Code-specific changes
特定于代码的更改
Here are some specific improvements to your code:
以下是对您的代码的一些具体改进:
$('.new_layer').click(function(){
dialog("Create new layer","Enter your layer name","_input", {
'OK' : function(){
var reply = $('.dialog_input').val();
if( reply != null && reply != "" ){
var name = "ln_"+reply.split(' ').join('_');
var parent = "";
if(selected_folder != "" ){
parent = selected_folder+" .content";
}
$R.find(".layer").clone()
.addClass(name).html(reply)
.appendTo("#layer_groups "+parent);
$R.find(".layers_group").clone()
.addClass(name).appendTo('#canvas '+selected_folder);
}
}
});
});
This is better written as:
最好写成:
$("body").on("click",".new_layer", function() {
dialog("Create new layer", "Enter your layer name", "_input", {
OK: function() {
// There must be a way to get the input from here using this, if it is a standard library. If you wrote your own, make the value retrievable using something other than a class selector (horrible performance + scoping +multiple instance issues)
// This is where the view comes into play. Instead of cloning, bind the rendering into a JS prototype, and instantiate it. It means that you only have to modify stuff in one place, you don't risk cloning events with it, and you can test your Layer stand-alone
var newLayer = new Layer();
newLayer
.setName(name)
.bindToGroup(parent);
}
});
});
Earlier in your code:
在您的代码中的前面:
window.Layer = function() {
this.instance = $("<div>");
// Markup generated here
};
window.Layer.prototype = {
setName: function(newName) {
},
bindToGroup: function(parentNode) {
}
}
Suddenly, you have a way to create a standard layer from anywhere in your code without copy pasting. You're doing this in five different places. I've just saved you five copy-pastes.
突然之间,您有了一种无需复制粘贴即可从代码中的任何位置创建标准层的方法。你在五个不同的地方做这件事。我刚刚为您保存了五个复制粘贴。
One more:
多一个:
// Ruleset wrapper for actions
// 动作的规则集包装器
var PageElements = function(ruleSet) {
ruleSet = ruleSet || [];
this.rules = [];
for (var i = 0; i < ruleSet.length; i++) {
if (ruleSet[i].target && ruleSet[i].action) {
this.rules.push(ruleSet[i]);
}
}
}
PageElements.prototype.run = function(elem) {
for (var i = 0; i < this.rules.length; i++) {
this.rules[i].action.apply(elem.find(this.rules.target));
}
}
var GlobalRules = new PageElements([
{
"target": ".draggable",
"action": function() { this.draggable({
cancel: "div#scrolling, .content",
containment: "document"
});
}
},
{
"target" :".resizable",
"action": function() {
this.resizable({
handles: "all",
zIndex: 0,
containment: "document"
});
}
}
]);
GlobalRules.run($("body"));
// If you need to add elements later on, you can just call GlobalRules.run(yourNewElement);
This is a very potent way to register rules if you have events that are not standard, or creation events. This is also seriously kick-ass when combined with a pub/sub notification system and when bound to an event you fire whenever you create elements. Fire'n'forget modular event binding!
如果您有非标准的事件或创建事件,这是一种非常有效的注册规则的方法。当与发布/订阅通知系统结合使用时,以及绑定到您在创建元素时触发的事件时,这也是严重的问题。Fire'n'forget 模块化事件绑定!
回答by Lyn Headley
Here is a simple way to split your current codebase into multiple files, using require.js. I will show you how to split your code into two files. Adding more files will be straightforward after that.
这是使用 require.js 将当前代码库拆分为多个文件的简单方法。我将向您展示如何将您的代码拆分为两个文件。之后添加更多文件将很简单。
Step 1)At the top of your code, create an App object (or whatever name you prefer, like MyGame):
步骤 1)在代码的顶部,创建一个 App 对象(或任何您喜欢的名称,例如 MyGame):
var App = {}
var App = {}
Step 2)Convert all of your top-level variables and functions to belong to the App object.
步骤 2)将所有顶级变量和函数转换为属于 App 对象。
Instead of:
代替:
var selected_layer = "";
var selected_layer = "";
You want:
你要:
App.selected_layer = "";
App.selected_layer = "";
Instead of:
代替:
function getModified(){
...
}
You want:
你要:
App.getModified = function() {
}
Note that at this point your code will not workuntil you finish the next step.
请注意,此时您的代码将无法运行,直到您完成下一步。
Step 3)Convert all global variable and function references to go through App.
步骤 3)转换所有全局变量和函数引用以通过 App。
Change stuff like:
更改以下内容:
selected_layer = "."+classes[1];
to:
到:
App.selected_layer = "."+classes[1];
and:
和:
getModified()
to:
到:
App.GetModified()
Step 4)Test Your code at this stage -- it should all work. You will probably get a few errors at first because you missed something, so fix those before moving on.
第 4 步)在这个阶段测试你的代码——它应该都可以工作。一开始你可能会因为错过了一些错误而得到一些错误,所以在继续之前修复这些错误。
Step 5)Set up requirejs. I assume you have a web page, served from a web server, whose code is in:
第 5 步)设置 requirejs。我假设您有一个由 Web 服务器提供的网页,其代码位于:
www/page.html
and jquery in
和 jquery
www/js/jquery.js
If these paths are not exactlylike this the below will not work and you'll have to modify the paths.
如果这些路径不完全像这样,下面的将不起作用,您必须修改路径。
Download requirejsand put require.js in your www/js
directory.
下载requirejs并将 require.js 放在您的www/js
目录中。
in your page.html
, delete all script tags and insert a script tag like:
在您的 中page.html
,删除所有脚本标签并插入一个脚本标签,例如:
<script data-main="js/main" src="js/require.js"></script>
create www/js/main.js
with content:
www/js/main.js
用内容创建:
require.config({
"shim": {
'jquery': { exports: '$' }
}
})
require(['jquery', 'app']);
then put all the code you just fixed up in Steps 1-3 (whose only global variable should be App) in:
然后将您刚刚在步骤 1-3 中修复的所有代码(其唯一的全局变量应该是 App)放入:
www/js/app.js
At the very top of that file, put:
在该文件的最顶部,放置:
require(['jquery'], function($) {
At the bottom put:
在底部放:
})
Then load page.html in your browser. Your app should work!
然后在浏览器中加载 page.html。您的应用程序应该可以工作!
Step 6)Create another file
步骤 6)创建另一个文件
Here is where your work pays off, you can do this over and over.
这是您的工作得到回报的地方,您可以一遍又一遍地这样做。
Pull out some code from www/js/app.js
that references $ and App.
从www/js/app.js
引用 $ 和 App 中提取一些代码。
e.g.
例如
$('a').click(function() { App.foo() }
Put it in www/js/foo.js
把它放进去 www/js/foo.js
At the very top of that file, put:
在该文件的最顶部,放置:
require(['jquery', 'app'], function($, App) {
At the bottom put:
在底部放:
})
Then change the last line of www/js/main.js to:
然后把www/js/main.js的最后一行改成:
require(['jquery', 'app', 'foo']);
That's it! Do this every time you want to put code in its own file!
就是这样!每次你想把代码放在自己的文件中时都这样做!
回答by Jesús Carrera
For your question and comments I'll assume you are not willing to port your code to a framework like Backbone, or use a loader library like Require. You just want a better way to orgainze the code that you already have, in the simplest way possible.
对于您的问题和评论,我假设您不愿意将您的代码移植到像 Backbone 这样的框架,或者使用像 Require 这样的加载程序库。您只需要一种更好的方式来以尽可能最简单的方式组织您已有的代码。
I understand it is annoying to scroll through 2000+ lines of code to find the section that you want to work on. The solution is to split your code in different files, one for each functionality. For example sidebar.js
, canvas.js
etc. Then you can join them together for production using Grunt, together with Usemin you can have something like this:
我知道滚动 2000 多行代码以找到您想要处理的部分很烦人。解决方案是将您的代码拆分为不同的文件,每个文件对应一个文件。例如sidebar.js
,canvas.js
等等。然后你可以使用 Grunt 将它们连接在一起进行生产,与 Usemin 一起你可以有这样的东西:
In your html:
在你的 html 中:
<!-- build:js scripts/app.js -->
<script src="scripts/sidebar.js"></script>
<script src="scripts/canvas.js"></script>
<!-- endbuild -->
In your Gruntfile:
在你的 Gruntfile 中:
useminPrepare: {
html: 'app/index.html',
options: {
dest: 'dist'
}
},
usemin: {
html: ['dist/{,*/}*.html'],
css: ['dist/styles/{,*/}*.css'],
options: {
dirs: ['dist']
}
}
If you want to use Yeoman it will give you a boilerplate code for all this.
如果您想使用 Yeoman,它将为您提供所有这些的样板代码。
Then for each file itself, you need to make sure you follow best practices and that all the code and variables are all in that file, and don't depend on other files. This doesn't mean you can't call functions of one file from other, the point is to have variables and functions encapsulated. Something similar to namespacing. I'll assume you don't want to port all your code to be Object Oriented, but if you don't mind refactoring a bit, I'd recommend to add something equivalent with what is called a Module pattern. It looks something like this:
然后对于每个文件本身,您需要确保遵循最佳实践,并且所有代码和变量都在该文件中,并且不依赖于其他文件。这并不意味着您不能从另一个文件调用一个文件的函数,关键是要封装变量和函数。类似于命名空间的东西。我假设您不想将所有代码移植为面向对象,但如果您不介意重构一下,我建议添加与所谓的模块模式等效的东西。它看起来像这样:
sidebar.js
侧边栏.js
var Sidebar = (function(){
// functions and vars here are private
var init = function(){
$("#sidebar #sortable").sortable({
forceHelperSize: true,
forcePlaceholderSize: true,
revert: true,
revert: 150,
placeholder: "highlight panel",
axis: "y",
tolerance: "pointer",
cancel: ".content"
}).disableSelection();
}
return {
// here your can put your "public" functions
init : init
}
})();
Then you can load this bit of code like this:
然后你可以像这样加载这段代码:
$(document).ready(function(){
Sidebar.init();
...
This will allow you to have a much more maintainable code without having to rewrite your code too much.
这将使您拥有更易于维护的代码,而不必过多地重写您的代码。
回答by Rohit Tailor
Use javascript MVC Framework in order to organize the javascript code in a standard way.
使用 javascript MVC 框架以标准方式组织 javascript 代码。
Best JavaScript MVC frameworks available are:
可用的最佳 JavaScript MVC 框架是:
Selecting a JavaScript MVC framework required so many factors to consider. Read the following comparison article that will help you to select best framework based on the factors important for your project: http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
选择一个 JavaScript MVC 框架需要考虑很多因素。阅读以下比较文章,它将帮助您根据项目的重要因素选择最佳框架:http: //sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
You can also use RequireJSwith the framework to support Asynchrounous js file & module loading.
Look the below to get started on JS Module loading:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
您还可以在框架中使用RequireJS来支持异步 js 文件和模块加载。
查看下面开始加载 JS 模块:http:
//www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
回答by Rohit Tailor
Categorize your code. This method is helping me a lot and does work with any js framework:
对代码进行分类。这种方法对我有很大帮助,并且适用于任何 js 框架:
(function(){//HEADER: menu
//your code for your header
})();
(function(){//HEADER: location bar
//your code for your location
})();
(function(){//FOOTER
//your code for your footer
})();
(function(){//PANEL: interactive links. e.g:
var crr = null;
$('::section.panel a').addEvent('click', function(E){
if ( crr) {
crr.hide();
}
crr = this.show();
});
})();
In your preferred editor (the best is Komodo Edit) you may fold in by collapsing all entries and you will see only the titles:
在您喜欢的编辑器(最好的是 Komodo Edit)中,您可以通过折叠所有条目来折叠,您将只看到标题:
(function(){//HEADER: menu_____________________________________
(function(){//HEADER: location bar_____________________________
(function(){//FOOTER___________________________________________
(function(){//PANEL: interactive links. e.g:___________________
回答by Fazle Rabbi
I would suggest:
我会建议:
- publisher/subscriber pattern for event management.
- object orientation
- namespacing
- 事件管理的发布者/订阅者模式。
- 面向对象
- 命名空间
In your case Jessica, divide the interface into pages or screens. Pages or screens can be objects and extended from some parent classes. Manage the interactions among pages with a PageManager class.
在您的情况下,杰西卡将界面分为页面或屏幕。页面或屏幕可以是对象并从一些父类扩展而来。使用 PageManager 类管理页面之间的交互。
回答by Chris Visser
I suggest that you use something like Backbone. Backbone is a RESTFUL supported javascript library. Ik makes your code cleaner and more readable and is powerful when used together with requirejs.
我建议你使用像 Backbone 这样的东西。Backbone 是一个支持 RESTFUL 的 javascript 库。Ik 使您的代码更清晰、更具可读性,并且与 requirejs 一起使用时功能强大。
Backbone isn't a real library. It is meant to give structure to your javascript code. It is able to include other libraries like jquery, jquery-ui, google-maps etc. Backbone is in my opinion the closest javascript approach to Object Oriented and Model View Controller structures.
Backbone 不是一个真正的图书馆。它旨在为您的 javascript 代码提供结构。它能够包含其他库,如 jquery、jquery-ui、google-maps 等。在我看来,Backbone 是最接近面向对象和模型视图控制器结构的 javascript 方法。
Also regarding your workflow.. If you build your applications in PHP use the Laravel library. It'll work flawlessly with Backbone when used with the RESTfull principle. Below the link to Laravel Framework and a tutorial about building RESTfull APIs:
还有关于你的工作流程......如果你用 PHP 构建你的应用程序,请使用 Laravel 库。当与 RESTfull 原则一起使用时,它将与 Backbone 完美配合。下面是 Laravel 框架的链接和关于构建 RESTfull API 的教程:
http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/
http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/
Below is a tutorial from nettuts. Nettuts has a lot of High Quality tutorials:
以下是来自 nettuts 的教程。Nettuts 有很多高质量的教程:
http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/
http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/
回答by drobson
Maybe its time for you start implementing a whole development workflow using such tools as yeoman http://yeoman.io/. This will help control all your dependencies, build process and if you wanted, automated testing. Its a lot of work to start with but once implemented will make future changes a lot easier.
也许是时候开始使用 yeoman http://yeoman.io/ 之类的工具来实现整个开发工作流程了。这将有助于控制所有依赖项、构建过程以及自动化测试(如果需要)。开始时需要做很多工作,但一旦实施,将来的更改就会容易得多。