Kiv*_*ius 102 javascript jquery design-patterns requirejs backbone.js
这个答案之前已经得到了回答,但已经过时而且不是最新的.我在一个文件中有超过2000行代码,而且我们都知道这是不好的做法,特别是当我查看代码或添加新功能时.我想更好地组织我的代码,无论是现在还是将来.
我应该提一下,我正在构建一个工具(不是一个简单的网站),它有许多按钮,UI元素,拖放,动作监听器/处理程序和全局范围内的功能,其中几个监听器可以使用相同的功能.
$('#button1').on('click', function(e){
// Determined action.
update_html();
});
... // Around 75 more of this
function update_html(){ .... }
...
Run Code Online (Sandbox Code Playgroud)
我真的需要组织这个代码以便最好地使用,而不是重复自己,并能够添加新功能和更新旧功能.我将自己做这件事.一些选择器可以是100行代码,其他的是1.我看了一下,require.js发现它有点重复,实际上编写了比需要更多的代码.我对任何符合此标准的可能解决方案持开放态度,并且链接到资源/示例始终是一个优势.
谢谢.
Séb*_*uld 94
我会回顾一些可能会或可能不会帮助你的简单事情.有些可能是显而易见的,有些可能是非常神秘的.
将代码分成多个模块化单元是非常好的第一步.将"合在一起"的作品整理成自己的小包装单元.不要担心现在的格式,保持内联.结构是后来的一点.
所以,假设你有一个这样的页面:

划分区域是有意义的,以便所有与标题相关的事件处理程序/绑定器都在那里,以便于维护(并且不必筛选1000行).
然后,您可以使用Grunt等工具将JS重新构建回单个单元.
使用诸如RequireJS或CommonJS之类的库来实现称为AMD的东西.异步模块加载允许您明确说明代码所依赖的内容,然后允许您将库调用卸载到代码中.您可以简单地说"这需要jQuery",AMD将加载它,并在jQuery可用时执行您的代码.
这也有一个隐藏的宝石:库加载将在DOM准备好的第二个时间内完成,而不是之前.这不再会停止页面的加载!
看到线框?我有两个广告单元.他们很可能会共享事件监听器.
您在此步骤中的任务是确定代码中的重复点,并尝试将所有这些合成到模块中.现在,模块将包含所有内容.随着我们的进展,我们会拆分东西.
这一步的整个想法是从步骤1开始,删除所有复制贴面,用松散耦合的单元替换它们.所以,而不是:
ad_unit1.js
$("#au1").click(function() { ... });
Run Code Online (Sandbox Code Playgroud)
ad_unit2.js
$("#au2").click(function() { ... });
Run Code Online (Sandbox Code Playgroud)
我会有:
ad_unit.js:
var AdUnit = function(elem) {
this.element = elem || new jQuery();
}
AdUnit.prototype.bindEvents = function() {
... Events go here
}
Run Code Online (Sandbox Code Playgroud)
page.js:
var AUs = new AdUnit($("#au1,#au2"));
AUs.bindEvents();
Run Code Online (Sandbox Code Playgroud)
除了摆脱重复之外,它还允许您在事件和标记之间划分区域.这是一个相当不错的步骤,我们将在稍后进一步扩展.
如果你想进一步模块化和减少重复,那么实现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);
}
}
});
});
Run Code Online (Sandbox Code Playgroud)
这写得更好:
$("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);
}
});
});
Run Code Online (Sandbox Code Playgroud)
在您的代码的前面:
window.Layer = function() {
this.instance = $("<div>");
// Markup generated here
};
window.Layer.prototype = {
setName: function(newName) {
},
bindToGroup: function(parentNode) {
}
}
Run Code Online (Sandbox Code Playgroud)
突然间,您可以从代码中的任何位置创建标准图层,而无需复制粘贴.你在五个不同的地方做这件事.我刚给你救了五个副本.
多一个:
//用于操作的规则集包装器
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);
Run Code Online (Sandbox Code Playgroud)
如果您有非标准事件或创建事件,这是一种非常有效的注册规则的方法.当与发布/订阅通知系统结合使用时,这也是非常严重的问题,当您绑定到每次创建元素时触发的事件时.Fire'n'forget模块化事件绑定!
Lyn*_*ley 13
以下是使用require.js将当前代码库拆分为多个文件的简单方法.我将向您展示如何将代码拆分为两个文件.之后添加更多文件将非常简单.
步骤1)在代码的顶部,创建一个App对象(或您喜欢的任何名称,如MyGame):
var App = {}
步骤2)将所有顶级变量和函数转换为属于App对象.
代替:
var selected_layer = "";
你要:
App.selected_layer = "";
代替:
function getModified(){
...
}
Run Code Online (Sandbox Code Playgroud)
你要:
App.getModified = function() {
}
Run Code Online (Sandbox Code Playgroud)
请注意,此时您的代码将无法工作,直到您完成下一步.
步骤3)转换所有全局变量和函数引用以通过App.
改变像:
selected_layer = "."+classes[1];
Run Code Online (Sandbox Code Playgroud)
至:
App.selected_layer = "."+classes[1];
Run Code Online (Sandbox Code Playgroud)
和:
getModified()
Run Code Online (Sandbox Code Playgroud)
至:
App.GetModified()
Run Code Online (Sandbox Code Playgroud)
步骤4)在此阶段测试您的代码 - 它应该都可以工作.一开始你可能会遇到一些错误,因为你错过了一些东西,所以在继续之前修复它们.
步骤5)设置requirejs.我假设您有一个网页,由Web服务器提供,其代码位于:
www/page.html
Run Code Online (Sandbox Code Playgroud)
和jquery in
www/js/jquery.js
Run Code Online (Sandbox Code Playgroud)
如果这些路径不完全相同,则下面的方法不起作用,您必须修改路径.
下载requirejs并将require.js放在您的www/js目录中.
在您的page.html,删除所有脚本标签并插入脚本标签,如:
<script data-main="js/main" src="js/require.js"></script>
Run Code Online (Sandbox Code Playgroud)
创建www/js/main.js内容:
require.config({
"shim": {
'jquery': { exports: '$' }
}
})
require(['jquery', 'app']);
Run Code Online (Sandbox Code Playgroud)
然后将您刚刚修复的所有代码放在步骤1-3中(其唯一的全局变量应该是App):
www/js/app.js
Run Code Online (Sandbox Code Playgroud)
在该文件的最顶部,放置:
require(['jquery'], function($) {
Run Code Online (Sandbox Code Playgroud)
在底部放:
})
Run Code Online (Sandbox Code Playgroud)
然后在浏览器中加载page.html.你的应用应该工作!
步骤6)创建另一个文件
这是你的工作得到回报的地方,你可以一遍又一遍地做到这一点.
从www/js/app.js引用$和App中拉出一些代码.
例如
$('a').click(function() { App.foo() }
Run Code Online (Sandbox Code Playgroud)
把它放进去 www/js/foo.js
在该文件的最顶部,放置:
require(['jquery', 'app'], function($, App) {
Run Code Online (Sandbox Code Playgroud)
在底部放:
})
Run Code Online (Sandbox Code Playgroud)
然后将www/js/main.js的最后一行更改为:
require(['jquery', 'app', 'foo']);
Run Code Online (Sandbox Code Playgroud)
而已!每次要将代码放在自己的文件中时都要这样做!
Jes*_*era 10
对于您的问题和评论,我假设您不愿意将代码移植到像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 -->
Run Code Online (Sandbox Code Playgroud)
在你的Gruntfile中:
useminPrepare: {
html: 'app/index.html',
options: {
dest: 'dist'
}
},
usemin: {
html: ['dist/{,*/}*.html'],
css: ['dist/styles/{,*/}*.css'],
options: {
dirs: ['dist']
}
}
Run Code Online (Sandbox Code Playgroud)
如果您想使用Yeoman,它将为您提供所有这些的样板代码.
然后,对于每个文件本身,您需要确保遵循最佳实践,并且所有代码和变量都在该文件中,并且不依赖于其他文件.这并不意味着你不能从其他文件中调用一个文件的函数,关键是要封装变量和函数.类似于命名空间的东西.我假设您不希望将所有代码移植到面向对象,但如果您不介意重构,我建议添加一些与模块模式等效的东西.它看起来像这样:
sidebar.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
}
})();
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样加载这段代码:
$(document).ready(function(){
Sidebar.init();
...
Run Code Online (Sandbox Code Playgroud)
这将允许您拥有更易于维护的代码,而无需过多地重写代码.
使用javascript MVC Framework以标准方式组织javascript代码.
最好的JavaScript MVC框架是:
选择JavaScript MVC框架需要考虑很多因素.阅读以下比较文章,该文章将帮助您根据对项目重要的因素选择最佳框架:http: //sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
您还可以在框架中使用RequireJS来支持Asynchrounous js文件和模块加载.
请看下面的JS模块加载入门:http:
//www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
| 归档时间: |
|
| 查看次数: |
42586 次 |
| 最近记录: |