web-dev-qa-db-ja.com

jQuery / JavaScriptコードを整理する最良の方法(2013)

問題

この回答は以前に回答されましたが、古くて最新ではありません。私は単一のファイルに2000行以上のコードを持っていますが、特にコードを調べたり新しい機能を追加したりする場合、これは悪い習慣です。現在および将来のために、コードをより良く整理したいと思います。

多くのボタン、UI要素、ドラッグ、ドロップ、アクションリスナー/ハンドラー、および複数のリスナーが同じ関数を使用する可能性のあるグローバルスコープの関数を備えたツール(単純なWebサイトではない)を構築していることに言及する必要があります。

サンプルコード

$('#button1').on('click', function(e){
    // Determined action.
    update_html();
});

... // Around 75 more of this

function update_html(){ .... }

...

その他のコード例

結論

このコードを最適に使用するために整理する必要があります。自分自身を繰り返したり、新しい機能を追加したり、古い機能を更新したりすることはできません。私は自分でこれに取り組みます。一部のセレクターは100行のコードにすることができ、他のセレクターは1です。私はrequire.jsそしてそれはちょっと反復的であり、実際に必要以上のコードを書いていることがわかりました。私は、この基準に適合するあらゆる可能な解決策にオープンであり、リソース/例へのリンクは常にプラスです。

ありがとう。

101
Kivylius

私はあなたを助けるかもしれないし、助けないかもしれないいくつかの簡単なことを調べます。明らかなものもあれば、非常に不可解なものもあります。

ステップ1:コードを区画化する

コードを複数のモジュラーユニットに分離することは、非常に良い第一歩です。 「一緒に」動作するものを切り上げ、それらを独自の小さなケースに入れたユニットに入れます。現時点ではフォーマットを気にせず、インラインにしてください。構造は後のポイントです。

したがって、次のようなページがあるとします。

enter image description here

メンテナンスを容易にするために(および1000行を選別する必要がないように)すべてのヘッダー関連のイベントハンドラー/バインダーがそこにあるように区分することは理にかなっています。

その後、Gruntなどのツールを使用して、JSを単一のユニットに再構築できます。

ステップ1a:依存関係管理

RequireJSやCommonJSなどのライブラリを使用して、[〜#〜] amd [〜#〜]と呼ばれるものを実装します。非同期モジュールロードを使用すると、コードが依存しているものを明示的に記述でき、ライブラリ呼び出しをコードにオフロードできます。文字通り「これにはjQueryが必要です」と言うことができ、AMDはそれをロードし、コードを実行しますjQueryが利用可能な場合

これには隠されたgemもあります。ライブラリのロードは、DOMの準備ができた2番目に行われます。これにより、ページのロードが停止することはなくなりました!

ステップ2:モジュール化

ワイヤーフレームが見えますか?広告ユニットが2つあります。ほとんどの場合、イベントリスナは共有されます。

このステップでのタスクは、コード内の反復ポイントを特定し、これらすべてをmodulesに合成しようとすることです。現在、モジュールにはすべてが含まれます。作業を進めながら、ものを分割します。

このステップの全体的なアイデアは、ステップ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();

これにより、繰り返しを取り除くことに加えて、eventsmarkupを区分化できます。これはかなり適切な手順であり、後ほどさらに拡張します。

ステップ3:フレームワークを選択してください!

モジュール化して繰り返しをさらに減らしたい場合は、MVC(モデル-ビュー-コントローラー)アプローチを実装する素晴らしいフレームワークがたくさんあります。私のお気に入りはBackbone/Spineですが、Angular、Yiiなどもあります...リストは続きます。

モデルはデータを表します。

Viewは、マークアップとそれに関連付けられたすべてのイベントを表します

Controllerは、ビジネスロジックを表します。つまり、コントローラーは、どのビューをロードし、どのモデルを使用するかをページに指示します。

これは重要な学習ステップになりますが、賞品には価値があります。スパゲッティよりもクリーンでモジュール式のコードが好まれます。

他にもできることがたくさんあります。それらは単なるガイドラインとアイデアです。

コード固有の変更

コードの具体的な改善点は次のとおりです。

 $('.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) {
   }
}

突然、コードのどこからでもコピーペーストなしで標準レイヤーを作成する方法があります。これを5つの異なる場所で行っています。コピーペーストを5つ保存しました。

もう一つ:

//アクションのルールセットラッパー

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);

これは、標準ではないイベントまたは作成イベントがある場合にルールを登録する非常に強力な方法です。これは、パブ/サブ通知システムと組み合わせた場合、および要素を作成するたびに起動するイベントにバインドされた場合にも、真剣にキックされます。モジュール式のイベントバインディングを忘れないでください!

93

Require.jsを使用して、現在のコードベースを複数のファイルに分割する簡単な方法を次に示します。コードを2つのファイルに分割する方法を示します。その後、ファイルを追加するのは簡単です。

ステップ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をセットアップします。 Webサーバーから提供されるWebページがあり、そのコードは次の場所にあると仮定します。

www/page.html

およびjquery in

www/js/jquery.js

これらのパスがexactlyでない場合、以下は機能せず、パスを変更する必要があります。

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']);

それでおしまい!コードを独自のファイルに配置するたびにこれを実行してください!

13
Lyn Headley

あなたの質問やコメントについては、Backboneのようなフレームワークにコードを移植したり、Requireのようなローダーライブラリを使用したりしないと仮定します。できるだけ簡単な方法で、既に持っているコードを整理するより良い方法が必要です。

2000行以上のコードをスクロールして、作業したいセクションを見つけるのは面倒です。解決策は、コードを機能ごとに1つの異なるファイルに分割することです。例えば ​​sidebar.jscanvas.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を使用する場合は、これらすべての定型コードが提供されます。

次に、各ファイル自体について、ベストプラクティスに従い、すべてのコードと変数がすべてそのファイル内にあり、他のファイルに依存しないことを確認する必要があります。これは、あるファイルの関数を他のファイルから呼び出せないという意味ではありません。ポイントは、変数と関数をカプセル化することです。名前空間に似たもの。すべてのコードをオブジェクト指向に移植したくないと思いますが、リファクタリングを少し気にしないのであれば、モジュールパターンと呼ばれるものと同等のものを追加することをお勧めします。次のようになります。

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
  }
})();

その後、次のようなコードをロードできます。

$(document).ready(function(){
   Sidebar.init();
   ...

これにより、コードを書き直すことなく、はるかに保守性の高いコードを作成できます。

10
Jesús Carrera

JavaScriptコードを標準的な方法で整理するには、javascript MVCフレームワークを使用します。

利用可能な最高のJavaScript MVCフレームワークは次のとおりです。

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/

6
Rohit Tailor

コードを分類します。この方法は私を大いに助けてくれ、どんな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:___________________
4
user2377782

私はお勧めします:

  1. イベント管理用のパブリッシャー/サブスクライバーパターン。
  2. オブジェクトの向き
  3. 名前空間

Jessicaの場合、インターフェースをページまたは画面に分割します。ページまたは画面はオブジェクトであり、いくつかの親クラスから拡張できます。 PageManagerクラスを使用してページ間の相互作用を管理します。

3
Fazle Rabbi

Backboneのようなものを使用することをお勧めします。バックボーンはRESTFULがサポートするjavascriptライブラリです。 Ikはコードをよりクリーンで読みやすくし、requirejsと組み合わせて使用​​すると強力です。

http://backbonejs.org/

http://requirejs.org/

バックボーンは実際のライブラリではありません。 javascriptコードに構造を与えることを意図しています。 jquery、jquery-ui、google-mapsなどの他のライブラリを含めることができます。Backboneは、オブジェクト指向およびモデルビューコントローラ構造に最も近いJavaScriptアプローチです。

また、ワークフローに関しても。PHPでアプリケーションをビルドする場合は、Laravelライブラリを使用してください。 Laravel FrameworkへのリンクとRESTfull APIの構築に関するチュートリアル:

http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/

http://laravel.com/

以下はnettutsのチュートリアルです。 Nettutsには、高品質のチュートリアルが多数あります。

http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/

2
Chris Visser

たぶん、yeoman http://yeoman.io/ などのツールを使用して、開発ワークフロー全体の実装を開始します。これにより、すべての依存関係、ビルドプロセス、および必要に応じて自動テストを制御できます。最初は多くの作業が必要ですが、一度実装すると、将来の変更が非常に簡単になります。

0
drobson