很难说出这里问的是什么。这个问题是模棱两可的、模糊的、不完整的、过于宽泛的或修辞的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,请访问帮助中心。 9年前关闭。
问题
这个答案之前已经回答过,但已经过时且不是最新的。我在一个文件中有超过 2000 行代码,我们都知道这是不好的做法,尤其是在我查看代码或添加新功能时。我想更好地组织我的代码,无论是现在还是未来。
我应该提到我正在构建一个工具(不是一个简单的网站),其中包含许多按钮、UI 元素、拖放、动作侦听器/处理程序以及多个侦听器可能使用相同功能的全局范围内的函数。
示例代码
$('#button1').on('click', function(e){
// Determined action.
update_html();
});
... // Around 75 more of this
function update_html(){ .... }
...
结论
我真的需要组织这段代码以供最佳使用,而不是重复自己,并且能够添加新功能并更新旧功能。我将自己解决这个问题。有些选择器可以是 100 行代码,而其他选择器是 1 行。我看了一下 require.js
,发现它有点重复,实际上写的代码比需要的多。我对任何符合此标准的可能解决方案持开放态度,并且链接到资源/示例总是一个加号。
谢谢。
我将介绍一些简单的事情,这些事情可能对你有帮助,也可能对你没有帮助。有些可能很明显,有些可能非常神秘。
第 1 步:划分代码
将代码分成多个模块化单元是非常好的第一步。把“一起”工作的东西收集起来,把它们放在自己的小封闭单元中。现在不要担心格式,保持内联。结构是后一点。
所以,假设你有一个这样的页面:
https://i.stack.imgur.com/YaaJ6.png
为了便于维护(并且不必筛选 1000 行),将所有与标头相关的事件处理程序/绑定器都放在其中是有意义的。
然后,您可以使用诸如 Grunt 之类的工具将您的 JS 重新构建为单个单元。
步骤 1a:依赖管理
使用诸如 RequireJS 或 CommonJS 之类的库来实现称为 AMD 的东西。异步模块加载允许您明确说明代码所依赖的内容,然后您可以将库调用卸载到代码中。您可以直接说“这需要 jQuery”,AMD 将加载它,并在 jQuery 可用时执行您的代码。
这也有一个隐藏的宝石:库加载将在 DOM 准备好后完成,而不是之前。这不再停止加载您的页面!
第 2 步:模块化
看到线框了吗?我有两个广告单元。他们很可能有共享的事件监听器。
您在这一步中的任务是识别代码中的重复点,并尝试将所有这些综合到模块中。现在,模块将涵盖所有内容。我们会在进行过程中拆分内容。
这一步的整个想法是从第 1 步开始,删除所有的复制面,用松散耦合的单元替换它们。所以,而不是:
ad_unit1.js
$("#au1").click(function() { ... });
ad_unit2.js
$("#au2").click(function() { ... });
我会有:
ad_unit.js
:
var AdUnit = function(elem) {
this.element = elem || new jQuery();
}
AdUnit.prototype.bindEvents = function() {
... Events go here
}
page.js
:
var AUs = new AdUnit($("#au1,#au2"));
AUs.bindEvents();
除了摆脱重复之外,它还允许您在事件和标记之间进行划分。这是一个相当不错的步骤,我们稍后会进一步扩展。
第 3 步:选择一个框架!
如果你想进一步模块化和减少重复,有一堆很棒的框架可以实现 MVC(模型 - 视图 - 控制器)方法。我最喜欢的是 Backbone/Spine,不过,还有 Angular、Yii、……不胜枚举。
模型代表您的数据。
视图代表您的标记以及与之关联的所有事件
控制器代表您的业务逻辑 - 换句话说,控制器告诉页面要加载哪些视图以及要使用哪些模型。
这将是一个重要的学习步骤,但奖励是值得的:与意大利面条相比,它更喜欢干净、模块化的代码。
你可以做很多其他的事情,这些只是指导方针和想法。
特定于代码的更改
以下是对您的代码的一些具体改进:
$('.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);
}
}
});
});
最好这样写:
$("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);
}
});
});
在您的代码前面:
window.Layer = function() {
this.instance = $("<div>");
// Markup generated here
};
window.Layer.prototype = {
setName: function(newName) {
},
bindToGroup: function(parentNode) {
}
}
突然之间,您有了一种无需复制粘贴即可从代码中的任何位置创建标准层的方法。你在五个不同的地方做这件事。我刚刚为你保存了五个复制粘贴。
多一个:
// 动作的规则集包装器
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);
如果您有非标准事件或创建事件,这是注册规则的一种非常有效的方法。当与 pub/sub 通知系统结合使用时,当绑定到创建元素时触发的事件时,这也是非常糟糕的。 Fire'n'forget 模块化事件绑定!
这是一种使用 require.js 将当前代码库拆分为多个文件的简单方法。我将向您展示如何将代码拆分为两个文件。之后添加更多文件将很简单。
第 1 步)在您的代码顶部,创建一个 App 对象(或您喜欢的任何名称,如 MyGame):
var App = {}
步骤 2) 将所有顶级变量和函数转换为属于 App 对象。
代替:
var selected_layer = "";
你要:
App.selected_layer = "";
代替:
function getModified(){
...
}
你要:
App.getModified = function() {
}
请注意,此时您的代码将无法工作,直到您完成下一步。
步骤 3) 转换所有全局变量和函数引用以通过 App。
更改以下内容:
selected_layer = "."+classes[1];
至:
App.selected_layer = "."+classes[1];
和:
getModified()
至:
App.GetModified()
第 4 步)在这个阶段测试你的代码——它应该都能工作。一开始你可能会遇到一些错误,因为你错过了一些东西,所以在继续之前修复这些错误。
步骤 5) 设置 requirejs。我假设您有一个网页,由网络服务器提供服务,其代码位于:
www/page.html
和 jquery
www/js/jquery.js
如果这些路径不完全像这样,则以下内容将不起作用,您必须修改路径。
下载 requirejs 并将 require.js 放入您的 www/js
目录。
在您的 page.html
中,删除所有脚本标签并插入一个脚本标签,例如:
<script data-main="js/main" src="js/require.js"></script>
使用以下内容创建 www/js/main.js
:
require.config({
"shim": {
'jquery': { exports: '$' }
}
})
require(['jquery', 'app']);
然后将您刚刚在步骤 1-3 中修复的所有代码(其唯一的全局变量应该是 App)放入:
www/js/app.js
在该文件的最顶部,输入:
require(['jquery'], function($) {
在底部放置:
})
然后在浏览器中加载 page.html。您的应用程序应该可以工作!
步骤 6) 创建另一个文件
这是你的工作得到回报的地方,你可以一遍又一遍地这样做。
从 www/js/app.js
中提取一些引用 $ 和 App 的代码。
例如
$('a').click(function() { App.foo() }
放入www/js/foo.js
在该文件的最顶部,输入:
require(['jquery', 'app'], function($, App) {
在底部放置:
})
然后将 www/js/main.js 的最后一行更改为:
require(['jquery', 'app', 'foo']);
而已!每次你想把代码放在自己的文件中时都这样做!
r.js
预处理器,迫使每个用户每个脚本每个页面加载都有 400 字节的浪费数据.此外,您实际上并没有解决 OP 的问题 - 只是提供了一个通用的 require.js
howto。
对于您的问题和评论,我假设您不愿意将您的代码移植到像 Backbone 这样的框架,或者使用像 Require 这样的加载程序库。您只是想要一种更好的方法来以最简单的方式组织您已经拥有的代码。
我知道滚动 2000 多行代码来查找您想要处理的部分很烦人。解决方案是将您的代码拆分为不同的文件,每个文件对应一个功能。例如 sidebar.js
、canvas.js
等。然后您可以使用 Grunt 将它们连接在一起进行生产,与 Usemin 一起您可以拥有这样的东西:
在您的 html 中:
<!-- build:js scripts/app.js -->
<script src="scripts/sidebar.js"></script>
<script src="scripts/canvas.js"></script>
<!-- endbuild -->
在你的 Gruntfile 中:
useminPrepare: {
html: 'app/index.html',
options: {
dest: 'dist'
}
},
usemin: {
html: ['dist/{,*/}*.html'],
css: ['dist/styles/{,*/}*.css'],
options: {
dirs: ['dist']
}
}
如果您想使用 Yeoman,它将为您提供所有这些的样板代码。
然后对于每个文件本身,您需要确保遵循最佳实践,并且所有代码和变量都在该文件中,并且不依赖于其他文件。这并不意味着您不能从另一个文件调用一个文件的函数,关键是要封装变量和函数。类似于命名空间的东西。我假设您不想将所有代码移植为面向对象,但如果您不介意重构一下,我建议添加与所谓的模块模式等效的东西。它看起来像这样:
侧边栏.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
}
})();
然后你可以像这样加载这段代码:
$(document).ready(function(){
Sidebar.init();
...
这将使您拥有更易于维护的代码,而无需过多地重写代码。
#sidebar #sortable
。您也可以通过内联代码并保存两个 IETF 来节省自己的内存。
使用 javascript MVC 框架以标准方式组织 javascript 代码。
可用的最佳 JavaScript MVC 框架是:
骨干
角
CanJS
余烬
反应JS
选择 JavaScript MVC 框架需要考虑很多因素。阅读以下比较文章,该文章将帮助您根据对项目重要的因素选择最佳框架:http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
您还可以将 RequireJS 与框架一起使用以支持异步 js 文件 &模块加载。
查看以下内容以开始 JS 模块加载:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
对代码进行分类。这种方法对我有很大帮助,并且适用于任何 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();
});
})();
在您喜欢的编辑器中(最好的是 Komodo Edit),您可以通过折叠所有条目来折叠,您只会看到标题:
(function(){//HEADER: menu_____________________________________
(function(){//HEADER: location bar_____________________________
(function(){//FOOTER___________________________________________
(function(){//PANEL: interactive links. e.g:___________________
我会建议:
事件管理的发布者/订阅者模式。面向对象命名空间
在您的情况下,杰西卡,将界面划分为页面或屏幕。页面或屏幕可以是对象并从某些父类扩展而来。使用 PageManager 类管理页面之间的交互。
我建议你使用 Backbone 之类的东西。 Backbone 是一个支持 RESTFUL 的 JavaScript 库。 Ik 使您的代码更简洁、更具可读性,并且与 requirejs 一起使用时功能强大。
Backbone 不是真正的库。它旨在为您的 javascript 代码提供结构。它能够包含其他库,如 jquery、jquery-ui、google-maps 等。在我看来,Backbone 是最接近面向对象和模型视图控制器结构的 javascript 方法。
另外关于您的工作流程。如果您使用 PHP 构建应用程序,请使用 Laravel 库。当与 RESTfull 原则一起使用时,它将与 Backbone 完美配合。在 Laravel 框架的链接和有关构建 RESTfull API 的教程下方:
http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/
下面是nettuts的教程。 Nettuts 有很多高质量的教程:
http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/
也许是时候开始使用 yeoman http://yeoman.io/ 等工具实施整个开发工作流程了。这将有助于控制您的所有依赖项、构建过程,如果您愿意,还可以进行自动化测试。一开始需要做很多工作,但一旦实施,未来的变化就会变得容易得多。
View
的概念简化并简化了层的创建。所以。这如何不适用于您的代码?layers.js
、sidebar.js
、global_events.js
、resources.js
、files.js
、dialog.js
。使用grunt
将它们重建为一个,使用Google Closure Compiler
进行编译和最小化。