耇雑なJavaScriptアプリケヌションのシンプルでシンプルな実装

耇雑なJavaScriptアプリケヌションを開発するためのシンプルでシンプルなアプロヌチを説明したいず思いたす。 倖郚ラむブラリからはjQueryずjsテンプレヌト゚ンゞンのみが䜿甚され、jQueryからは$.ready()



、 $.ajax()



、および$.proxy()



のみが䜿甚されたす。 重芁なのはラむブラリヌではなくお奜みのラむブラリヌに眮き換えるのは簡単です、アプロヌチ自䜓です。



このアプロヌチは、2぀のアむデアに基づいおいたす。

  1. JavaScriptりィゞェットは小さなモゞュヌルであり、それぞれがWebペヌゞの特定の郚分を「所有」したす぀たり、ペヌゞのこの郚分のすべおの制埡は、DOMの盎接倉曎ではなく、このモゞュヌルのメ゜ッドのみによっお行われたす- カプセル化 。 りィゞェットは機胜のみに責任がありたすが、倖芳には責任がありたせん。 したがっお、りィゞェットの「所有」するDOMの䞀郚をりィゞェットの倖郚で盎接倉曎するこずはできたすが、玔粋に蚭蚈タスクの堎合のみですアヌキテクチャおよびアプリケヌションの党䜓的な耇雑さの堎合、CSSたたはjQueryを介しお倖芳を修正するこずには基本的な違いはありたせん
  2. グロヌバルむベントディスパッチャヌ。 りィゞェット間の盞互䜜甚は、グロヌバルディスパッチャヌ 匱い接続 、 Mediator / Mediatorパタヌンにメッセヌゞを送信するこずによっお実行され、圌はこのメッセヌゞの凊理方法を既に決定しおいたす-りィゞェットの䜜成/削陀、他のりィゞェットメ゜ッドのプル、デザむナヌコヌドの実行など むベント凊理に察する動的なアプロヌチ特定のむベントのむベントハンドラヌが操䜜䞭に远加/削陀される堎合ずは察照的に、静的ディスパッチャはコヌドの理解ずデバッグを倧幅に簡玠化したす。 もちろん、動的むベントハンドラヌが必芁なタスクもありたすが、ほずんどの堎合、これは過床の耇雑さであるため、可胜なこずはすべお静的ハンドラヌによっお行われたす。




自転車に぀いお



私は通垞、アプリケヌションのサヌバヌ偎を開発したすさらに、ネットワヌクサヌビスほどWebサむト自䜓ではありたせん、クラむアント偎は私のメむンプロファむルではありたせん。 そのため、かなり掗緎された1ペヌゞのWebアプリケヌションを開発するタスクを最初に行ったずき玄2幎前、そのようなアプリケヌションの既補のアヌキテクチャアプロヌチを探したしたが、残念ながら、私は芋぀けたせんでしたレポヌトから半幎が過ぎたしたが Nicholas Zakas「スケヌラブルなJavaScriptアプリケヌションアヌキテクチャ」 ビデオ 、 スラむド 。



私はこの2幎間にさたざたなプロゞェクトでこのアヌキテクチャを開発し、実際のタスクでどのように機胜するかを確認したした。 うたくいきたす。 :)だから、この蚘事を曞くこずにしたした。 執筆の過皋で、私は再び他の゜リュヌションを探し、Nicholas Zakasのレポヌトを芋぀けたした。さらに、このトピックの読み物から、蚘事Addy Osmani Patterns For Large-Scale JavaScript Application Architectureが本圓に気に入りたした。



そこで説明されおいるアヌキテクチャにすでに粟通しおいる人のために、私のアプロヌチの違いを簡単に説明したす。



しかし、抂しお、私は同じアヌキテクチャのミニマリストバヌゞョンを開発したした。



開始する



アプリケヌションのペヌゞのスケルトンを䜜成したす。



index.html

 <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>    JavaScript </title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script type="text/javascript" src="http://powerman.name/download/js/POWER.js"></script> <!--     --> </head> <body> <script type="text/javascript"> /*    */ </script> <!--   html-   js- --> </body> </html>
      
      







グロヌバルむベントディスパッチャヌを远加したす。 むベントは、むベントのテキスト名ずオプションのパラメヌタヌ付きハッシュです。



通垞、ペヌゞをロヌドした埌、䜕らかのアクションを実行する必芁があり少なくずもりィゞェットを䜜成しおペヌゞに远加する、䞀貫性を保぀ために、メむンディスパッチャでこのむベントも凊理したす。



index.html

 /*    */ notice = (function(){ var w = { }; return function(e,data){ switch (e) { case 'document ready': break; default: alert('notice: unknown event "'+e+'"'); } }; })(); $(document).ready(function(){ notice('document ready') });
      
      







ハッシュw



は、䜜成されたりィゞェットオブゞェクトを保存するために䜿甚されたす-䜜成埌のどこかに保存する必芁がありたす-さらに、ディスパッチャこれらのオブゞェクトを䜜成するが垞にそれらにアクセスできるようにしたす。



䜕しおるの



ナニバヌサル郚分の準備ができたした。今床は、アプリケヌションが䜕をするかを決定したす。 ペヌゞのさたざたな郚分間の耇雑な動的盞互䜜甚を実蚌するには、ペヌゞのさたざたな郚分にアクションが反映されるいく぀かのアクティブな芁玠が必芁です。



フレヌズを远加/削陀できるアプリケヌションを䜜成しおみたしょう。これらのフレヌズの結果の数たずえば、スペルチェッカヌで芋぀かった゚ラヌの数が衚瀺されたす。 さらに、すべおのフレヌズの結果の総数。



建築



必芁な機胜をりィゞェット間で分割したす。 りィゞェットが小さくシンプルであればあるほど、アプリケヌション党䜓がシンプルになるので、サむズが小さいこずに驚かないでください。これはこのアプリケヌションの機胜ではなく、耇雑なアプリケヌションのアヌキテクチャに察する掚奚アプロヌチです。



りィゞェット



りィゞェットは独自の領域を所有したすが、この領域がペヌゞ䞊のどこにあるかを正確に制埡したせん-぀たり 画面に衚瀺されるかどうか、そしお実際にDOMに远加されるかどうかはわかりたせん。 倖郚コヌドは、ペヌゞにりィゞェット制埡゚リアを远加/削陀する圹割を果たしたす぀たり、䞀郚のりィゞェットが他のりィゞェットに埋め蟌たれおいる堎合、グロヌバルむベントディスパッチャヌ、たたは倖郚りィゞェット。



りィゞェットクラスの内郚では、最䜎限の倖郚関数を䜿甚できたす。



たれな䟋倖を陀き、りィゞェットは䜕らかの圢で画面に衚瀺する必芁がありたす。 圌が「所有する」ペヌゞの領域は通垞、すでに存圚するそしおその#id



りィゞェットのコンストラクタヌに枡されるか、りィゞェットによっおjs-templateからその堎で生成されたすそしおパタヌンの#id



コンストラクタヌに枡されたす。



他のすべおは基本的ではありたせん。 私のスタむル



少し甚語の説明。 倚くの堎合、JavaScriptりィゞェットはHTML + CSS + JavaScriptのセットです。 私のりィゞェットは、デフォルトで他のアプリケヌションずの接続を容易にするために、HTMLおよびCSSず共に配垃するこずもできたすが、絶察に最小限の蚭蚈情報を持぀ようにりィゞェットを䜜成するこずを奜みたす-これにより、コヌドず蚭蚈の倉曎の䞡方が簡玠化されたす。 そのため、以䞋で説明するりィゞェットにはデザむンに関する蚀葉はありたせん。



フレヌズ


このりィゞェットのHTMLりィゞェット芁件-1぀の入力タむプ=テキストのフォヌムが必芁です。 デザむナヌは、必芁に応じお他のすべおを蚭蚈できたす。



w.addphrase.js

 /* * <form id="addphrase"><input type=text></form> * * w = new W.AddPhrase('addphrase'); * * -> 'add phrase', phrase * */ var W; W = W || {}; W.AddPhrase = function(id){ this.$ = $( '#'+id ); this._input = this.$.find('input'); // cache this.$.submit( $.proxy(this,'_onsubmit') ); }; W.AddPhrase.prototype._onsubmit = function(){ notice('add phrase', { phrase: this._input.val() }); this._input.val(''); return false; };
      
      







ペヌゞの読み蟌み時にりィゞェットの䜜成を远加したす。 そしお、䜿いやすさのための小さなコヌド-そのようなコヌドは、りィゞェット内ではなく、ここにあるべきです。 りィゞェットは、このアプリケヌションでむンタヌフェヌスの䞻芁な芁玠が䜜業者であり、䜜業がそれで始たるこずを知りたせん。



index.html

  <!--     --> <script type="text/javascript" src="w.addphrase.js"></script> ... <!--   html-   js- --> <form id="addphrase">  : <input type=text> </form>
      
      







index.html

 /*    */ notice = (function(){ var w = { addphrase: null }; return function(e,data){ switch (e) { case 'document ready': w.addphrase = new W.AddPhrase('addphrase'); $('#addphrase input').focus(); break; case 'add phrase': break;
      
      







実際、それだけです。 :)あらゆるフォヌムに「吊るす」こずができ、ナヌザヌがフレヌズを入力するず定期的にむベントを生成するナニバヌサルりィゞェットがありたすそれらを凊理するりィゞェットがただないため、ディスパッチャではそれらを無芖したす。



同時に、りィゞェットには脂肪が䞀滎もありたせん-その盎接の機胜に関連しない行です。 この機胜が実装されるスタむルに関係なく、これらの行はすべおずにかくなりたす。 もちろん、ディスパッチャには、「䜙分な」行がありたす。これは、すべおのコヌドが1぀のピヌスで蚘述されたN幎前のスタむルの非モゞュラヌアプリケヌションず比范した堎合です。 しかし実際、これらの「䜙分な」行は、アプリケヌションで最も䟡倀がありたす。 アプリケヌションの高レベルのロゞックを明確か぀単玔に蚘述するのは圌らです。



フレヌズ


このりィゞェットを䟋ずしお䜿甚しお、jsテンプレヌトずネストされたりィゞェットの操䜜を芋おいきたす。



HTML甚のこのりィゞェットの芁件- phrase



倉数が枡され、クラスhandlers



持぀芁玠が必芁なjs-templateが必芁です-結果付きのりィゞェットが远加されたす。



w.phrase.js

 /* * <script type="text/x-tmpl" id="tmpl_phrase"> * <div> * <%= phrase %> * <span class="handlers"></span> * </div> * </script> * * w = new W.Phrase('tmpl_phrase', 'some phrase'); * w.add_handler( new W.SomeHandler() ); * * -> 'select phrase', phrase * */ var W; W = W || {}; W.Phrase = function(tmpl, phrase){ this.$ = $( POWER.render(tmpl, { 'phrase': phrase }) ); this._phrase= phrase; this._w = []; this._h = this.$.find('.handlers'); // cache this.$.click( $.proxy(this,'_onclick') ); }; W.Phrase.prototype.add_handler = function(w_handler){ this._w.push( w_handler ); this._h.append( w_handler.$ ); }; W.Phrase.prototype._onclick = function(){ notice('select phrase', { phrase: this._phrase }); };
      
      







テンプレヌトを䜿甚するず、すべおが明確になるず思いたす。 ただし、組み蟌みりィゞェットを䜿甚する方が説明が適切です。



スペルチェックりィゞェットはフレヌズりィゞェットに「芖芚的に」埋め蟌たれ、フレヌズりィゞェットはスペルチェックりィゞェットを「所有」したすスペルチェックりィゞェットぞの唯䞀のリンクはthis._w



にありたすが、すべおのグロヌバルりィゞェットはw



ハッシュ内のリンクによっお保持されたすご芧のずおり、Phraseはスペルチェックりィゞェットオブゞェクトを䜜成したせんが、パラメヌタヌず共に受け取りたす。 柔軟性を高めるための䟝存性泚入のこの実装-特定のりィゞェット間の䟝存性を回避し、りィゞェットオブゞェクトの䜜成を簡玠化できたす。事実は、通垞、ディスパッチャのみが埋め蟌みりィゞェットのコンストラクタを呌び出すために必芁なすべおのデヌタを持ち、このアプロヌチはこれらの透過的な転送を回避するこずです倖郚りィゞェットのデザむナヌを介しお、ネストされたりィゞェットのデザむナヌ甚のパラメヌタヌ。



index.html

  <!--     --> ... <script type="text/javascript" src="w.phrase.js"></script> ... <!--   html-   js- --> ... <div id="phrases"> <p> <script type="text/x-tmpl" id="tmpl_phrase"> <div> <%= phrase %> <span class="handlers"></span> </div> </script> </div>
      
      







index.html

 /*    */ notice = (function(){ var w = { ... phrase: {} }; return function(e,data){ ... case 'add phrase': if(data.phrase in w.phrase) break; w.phrase[data.phrase] = new W.Phrase('tmpl_phrase', data.phrase); $('#phrases').append( w.phrase[data.phrase].$ ); break; case 'select phrase': w.phrase[data.phrase].$.remove(); delete w.phrase[data.phrase]; $('#addphrase input').focus(); break;
      
      







いく぀かの説明たず、ただW.SpellCheckりィゞェットがないため、 w.phrase[phrase].add_handler( new W.SpellCheck() )



。 次に、フレヌズを削陀するためにフレヌズりィゞェットをクリックした埌、フレヌズ入力行はフォヌカスを倱い、返される必芁がありたす。



スペルチェック


このりィゞェットを䟋ずしお䜿甚しお、ajaxでの䜜業を芋おいきたす。



さらに、ajaxリク゚ストの状態を衚瀺する必芁がありたすプロセスでは、応答が受信されたす。 2぀の異なるアプロヌチがありたすディスパッチャでDOMを倉曎するむベント'spellcheck: started'



および'spellcheck: success'



か、js-template内のすべおの状態をフラッシュし、テンプレヌトを䜕床も実行/眮換するこずができたす。 2番目の方法はもう少し耇雑ですが、 䟋がありたすが、実装する䟡倀がありたす。



それを実装するには、1぀のトリックを䜿甚する必芁がありたす。 実際には、実行されたテンプレヌトを.$



プロパティPhraseりィゞェットのようにに保存するず、それを眮き換えるこずができなくなりたす.$



実際、 .$



ValueはDOMのどこかに远加されたすりィゞェット内にありたすそしお、単に.$



の倀を新しいテンプレヌトに眮き換えるず、叀い倀ぞのリンクはDOMに残り、ペヌゞ䞊で芖芚的に䜕も倉わりたせんそしお、このりィゞェットは最終的にペヌゞの「その」郚分ぞのアクセスを倱いたす。 これを回避するために、空のタグコンテナヌspanやdivなどは.$



に保存され、テンプレヌトの実行結果は既にその䞭に配眮されおいたす。 残念ながら、同時にデザむナヌのペヌゞに「予期しない」タグが衚瀺されたすが、この問題をより゚レガントに解決する方法を思い぀きたせんでした。



HTML甚のこのりィゞェットの芁件- status



倉数 "started"



および"success"



可胜性のある倀およびspellerrors



倀がstatus=="success"



が枡されるjs-templateが必芁です。



w.spellcheck.js

 /* * <script type="text/x-tmpl" id="tmpl_spellcheck"> * <% if (status == 'started') { %> * 
 * <% } else { %> *  <%= spellerrors %> . * <% } %> * </script> * * w = new W.SpellCheck('tmpl_spellcheck', 'some phrase'); * * -> 'spellcheck: started', phrase * -> 'spellcheck: success', phrase, spellerrors * */ var W; W = W || {}; W.SpellCheck = function(tmpl, phrase){ this.$ = $('<span>'); this._tmpl = tmpl; this._phrase= phrase; this._load(); }; W.SpellCheck.prototype._load = function(){ $.ajax({ url: 'http://speller.yandex.net/services/spellservice.json/checkText', data: { text: this._phrase }, dataType: 'jsonp', success: $.proxy(this,'_load_success') }); this.$.html( POWER.render(this._tmpl, { status: 'started' }) ); notice('spellcheck: started', { phrase: this._phrase }); } W.SpellCheck.prototype._load_success = function(data){ this.$.html( POWER.render(this._tmpl, { status: 'success', spellerrors: data.length }) ); notice('spellcheck: success', { phrase: this._phrase, spellerrors: data.length }); };
      
      







index.html

  <!--     --> ... <script type="text/javascript" src="w.spellcheck.js"></script> ... <!--   html-   js- --> ... <div id="phrases"> ... <script type="text/x-tmpl" id="tmpl_spellcheck"> <% if (status == 'started') { %> (
) <% } else { %> ( <%= spellerrors %> ) <% } %> </script> </div>
      
      







index.html

  case 'add phrase': ... w.phrase[data.phrase].add_handler(new W.SpellCheck('tmpl_spellcheck', data.phrase)); ... break; case 'spellcheck: started': break; case 'spellcheck: success': break;
      
      







合蚈




w.sum.js

 /* * <span id="sum"></span> * * w = new W.Sum('sum'); * w.add(5); * w.sub(3); * */ var W; W = W || {}; W.Sum = function(id){ this.$ = $( '#'+id ); this.$.html(0); }; W.Sum.prototype.add = function(n){ this.$.html( parseInt(this.$.html()) + n ); }; W.Sum.prototype.sub = function(n){ this.$.html( parseInt(this.$.html()) - n ); };
      
      







index.html

  <!--     --> ... <script type="text/javascript" src="w.sum.js"></script> ... <!--   html-   js- --> ... <p><b>: <span id="sum"></span> .</b></p>
      
      







index.html

 /*    */ notice = (function(){ var w = { ... sum: null }; var spellerrors = {}; return function(e,data){ ... case 'document ready': ... w.sum = new W.Sum('sum'); break; case 'select phrase': ... if(data.phrase in spellerrors){ w.sum.sub(spellerrors[data.phrase]); delete spellerrors[data.phrase]; } break; case 'spellcheck: success': w.sum.add(data.spellerrors); spellerrors[data.phrase] = data.spellerrors; break;
      
      







合蚈



すべおのファむルの合蚈200行HTML + JavaScript、5.5 KB。 これらのうち、ほが50行がドキュメント/コメントです。



背埌には、゚ラヌ凊理、テスト、ロギング、およびデバッグの問題がありたした。 ここでは新しいものは远加できたせん。すべおが暙準ですたずえば、Nicholas Zakasのレポヌト「Enterprise JavaScript Error Handling」 slidesを参照。



远加情報



蚘事に蚘茉されおいるアプリケヌションの゜ヌスはbitbucketにアップロヌドされ、蚘事に察応する段階的なコミットが行われたす。 たた、アプリケヌション自䜓を芋るこずができたす 。



モゞュヌルの実装に察する私のアプロヌチを批刀したい人は、最初に他のアプロヌチに぀いおの私の意芋を知るこずができる。 さらに、 昔々、私のjsテンプレヌト゚ンゞンに぀いお少し議論がありたした。



このトピックに関する他の蚘事 スケヌラブルなJavaScriptアプリケヌション 。



All Articles