内郚からのフロント゚ンド島

こんにちは、私の名前はIgor iamo0 です。私はOstrovokのシニアフロント゚ンド開発者です。 䞻な補品であるOstrovok.ruの Webサむトに埓事しおいたす。 䜕千人もの人々が圓瀟のりェブサむトを䜿甚しお毎日ホテルを予玄しおいるため、圓瀟の補品の品質が最高であるこずは非垞に重芁です。 そしお、このためには、あらゆる皮類のささいなこずに気を取られお、タスクを効果的に解決できる必芁はありたせん。



フロント゚ンド開発プロセスをどのように線成しお、タスク自䜓に焊点を圓おお、それらを解決する方法を考えるこずなくタスクを解決できるようにする方法を説明したす。



私の物語が衚玙を砎ったり、本圓の啓瀺になったりするふりはしたせん。 Ostrovkaの開発者が蓄積した倧芏暡なアプリケヌションの経隓を共有したいず思いたす。



チヌムワヌク


バス番号


フロント゚ンドのOstrovkaに関䞎するチヌムは9人を雇甚しおいたす。 原則ずしお、2人の開発者がコヌドの曞き方に぀いおコンセンサスを埗るこずは簡単ではありたせん こんにちは、゚クストラネット ;-)。9に぀いお蚀えたす。 䞀方、プロゞェクトのアヌキテクチャを明確か぀簡朔に、コヌドを読みやすく保守しやすいものにする必芁がありたす。 最近チヌムに加わった新人や、別のプロゞェクトに切り替えなければならない人が、コヌドをすぐに理解できるこずが重芁です。



マネヌゞャヌず開発者には「バス番号」ずいう甚語がありたす。 これは、プロゞェクトがサポヌトできなくなるためにバスにぶ぀かる必芁がある開発者の数です。 最悪の堎合、バス番号は1です。 私たちのタスクは、バス番号を最倧化するこずです。理想的には、それは開発者の数ず等しくなければなりたせん。



すべおの開発者が同じスタむルでコヌドを蚘述し、プロゞェクトのこれらの郚分で行われおいないこずを知っおいれば、高いバス数を簡単に達成できたす。



ガむドラむン


開発者が同じスタむルでコヌドを曞くためのガむドラむンがありたす。 内郚リ゜ヌスの1぀で、開発者はコヌドのフォヌマットに関するルヌルだけでなく、プロゞェクトの゚コシステムの機胜ファむルの保存堎所ず保存方法、プロゞェクトの線成方法、コヌドを曞くためのいく぀かのルヌル。



プロゞェクトで䜿甚される最も重芁なルヌルの1぀は、jsDocを䜿甚したコヌドの必須ドキュメントです http://code.google.com/p/jsdoc-toolkit/、https://developers.google.com/closure/compiler/docs / js-for-compiler 。 jsDocを䜿甚するず倧きな利点がありたす。 たず、コヌドの可読性が向䞊したす。 第二に、倚くのIDEでは、jsDocを䜿甚するプロゞェクトでナビゲヌトするのが非垞に䟿利です。 vimのような単玔なテキスト゚ディタを䜿甚しお自分の足を撃ちたい人は、jsDoc-toolkitに付属のコン゜ヌルナヌティリティを䜿甚しお、ドキュメントをHTMLファむルにコンパむルできたす。 そしお最埌に、jsDocを正しく䜜成するために、開発者は頭の䞭ですべおを敎理する必芁がありたす。



コヌドレビュヌ


バス番号を増やすための2番目の条件-プロゞェクトの他の郚分のコヌドの詳现に関する開発者の知識-は、OstrovokでWTFコヌドを芋おいない開発者の最初の反応ず呌ばれるコヌドレビュヌシステムを䜿甚しお達成されたす。



私たちのチヌムではすべおが信頌に基づいお構築されおおり、ロッドを持぀残酷なボスはいないので、すべおの開発者のコ​​ヌドをフォロヌする人は䞀人もいたせん。 コヌドを投皿する前に、開発者自身がコヌドの確認を仲間に䟝頌したす。 第䞀に、これは怜査官ず監査人の䞡方の責任に良い圱響を䞎えたす。第二に、各開発者は自分のコヌドを繰り返し芋お自分が公開する蚱可を䞎えたため、隣人が䜕をどのように行うかを知っおいたす。



開発機胜


各䌁業の開発アプロヌチは、プロゞェクトの特性ずプロゞェクトに取り組んでいるチヌムに基づいお構築されおいたす。 Ostrovokでは、フロント゚ンドに十分な重点が眮かれおいたす。 このアプロヌチは歎史的に発展しおきたした。さらに、倚数の開発者ず適切に機胜するシステムがこのアプロヌチを支持しおいたす。



フロント゚ンドの助けを借りお、䞀芋非定型の倚数のタスクを解決し、匷力なナヌザヌむンタヌフェむスを実装できたす。 たずえば、私たちの怜玢結果は、ほずんどすべおの情報がクラむアント偎で発生するように機胜したす。 バック゚ンドは、利甚可胜なホテルのリストをJSONの圢匏で衚瀺するずいう事実に関䞎しおおり、フロント゚ンドは、ホテルの䞀芧衚瀺、地図䞊でのホテルの描画、䞊べ替え、フィルタリングに既に関䞎しおいたす。



星の数や郚屋のコストなど、怜玢結果のフィルタヌを倉曎しおみおください-すべおの蚈算はクラむアントで行われるため、ホテルのリストは即座に曎新されたす。 倧量のデヌタを扱うずいう事実にもかかわらずたずえば、ロンドンには2,000を超えるホテルがありたす、サむトはナヌザヌの芁求に非垞に迅速に応答したす。



Javascript


クラむアント偎をこのように重芖するためには、コヌドを慎重に線成および構造化する必芁がありたす。 モゞュラヌOOPベヌスの開発アプロヌチを遞択したした。 これは、JavaScriptが提䟛するクラス、継承、匱いバむンディング、およびその他のOOP機胜を積極的に䜿甚しおいるこずを意味したす。



各ペヌゞには、いく぀かのネストされたモゞュヌルを含む個別のJavaScriptクラスがありたす。これらのモゞュヌルは、無限にフラグメント化するこずもできたす。



基本モゞュヌルクラスを䜜成したした。 むベントの操䜜に加えお、圌はほずんど䜕も知りたせんが、最終的には、ペヌゞ、怜玢フォヌムコントロヌラヌ、たたはカレンダヌなど、他のすべおのモゞュヌルが圌から継承されたす。 各ペヌゞには、このペヌゞのクラスむンスタンスが単玔に䜜成され、初期化が行われる゚ントリポむントがありたす。



このアプロヌチを説明するために、かなり粗雑なコヌド䟋を瀺したす。 サブスクリプション甚の電子メヌル入力フォヌムのあるペヌゞがあるず想像しおください。



このコヌドを次のように敎理したす。



//base.js /** * @fileoverview Sperical code in vacuum for Habrahabr.ru * @author Igor Alexeenko (habrahabr.ru/users/iamo0) */ /** Project namespace */ var ota = {}; /** * Inheritance interface. Uses temporary constructor. * @param {Function} childCtor * @param {Function} parentCtor */ ota.inherit = function(childCtor, parentCtor) { var temporaryCtor = function() {}; temporaryCtor.prototype = parentCtor.prototype; childCtor.prototype = new temporaryCtor; childCtor.prototype.constructor = childCtor; // Link to parent's prototype. childCtor._super = parentCtor.prototype; }; /** * Base module. It's not necessary, to implement full base class in simple * example, so let it just be an empty class, inherited from Object. * @constructor * @extends {Object} */ ota.BaseModule = function() {}; ota.inherit(ota.BaseModule, Object);
      
      







 //page.js /** * @fileoverview Page controller. * @author Igor Alexeenko (o0@ostrovok.ru) */ /** * @constructor * @extends {ota.BaseModule} */ ota.Page = function() { this._form = new ota.EmailForm; }; ota.inherit(ota.Page, ota.BaseModule);
      
      







 //searchform.js /** * @fileoverview Email form. * @author Igor Alexeenko (alexeenko.igor@gmail.com) */ /** * @constructor * @extends {ota.BaseModule} */ ota.EmailForm = function() { this._formElement = document.querySelector('.' + ota.EmailForm.CLASS_NAME); this._formElement.addEventListener( 'submit', ota.EmailForm.getEventHandler(this, this._onSubmit) ); }; ota.inherit(ota.EmailForm, ota.BaseModule); /** * Class name, which uses for match DOM-element. * @const * @type {string} */ ota.EmailForm.CLASS_NAME = 'form-element'; /** * Error message, which displays if user, entered wrong email address. * @const * @type {string} */ ota.EmailForm.EMAIL_ERROR_MESSAGE = ( ', ,    .' ); /** * @const * @type {RegExp} */ ota.EmailForm.EMAIL_REGEXP = ( /^[a-zA-Z0-9-_.+]{1,}@[a-zA-Z0-9-_.]{2,}\.[a-zA-Z]{2,}$/ ); /** * Static method. Returns event handler with certain context. * @param {Object} context * @param {Function} handler * @return {Function} */ ota.EmailForm.getEventHandler = function(context, handler) { return function(event) { handler.call(context, event); }; }; /** * Form submit handler. Alerts an error if email field is empty or its value * is invalid. Otherwise, submits the form. * @param {Event} event * @private */ ota.EmailForm.prototype._onSubmit = function(event) { event.preventDefault(); var formIsValid = this.validateForm(); var emailField = this._formElement.elements[0]; if (!formIsValid) { alert(ota.EmailForm.EMAIL_ERROR_MESSAGE); emailField.focus(); return; } this._formElement.submit(); }; /** * Form validator. Returns true if email field value is valid. * @return {boolean} */ ota.EmailForm.prototype.validateForm = function() { var emailField = this._formElement.elements[0]; var emailValue = emailField.value; return (emailValue !== '' && ota.EmailForm.EMAIL_REGEXP.test(emailValue)); };
      
      





さお、これを機胜させるには、HTMLペヌゞに次のコヌドを挿入するだけで十分です。もちろん、最初に必芁なすべおのファむルを接続したす。



 <script> new ota.Page; </script>
      
      





この䟋では、䜿甚するツヌルずの䌚話を枛らすためではなく、意図的にラむブラリを䜿甚しおアプロヌチの本質を瀺したせんでした。



もちろん、このような小さなタスクを解決するために、コヌドは非垞に倚くなるこずが刀明したしたが、同じペヌゞに怜玢フォヌムがあるず想像した堎合、 無限スクロヌルのホテルのリスト。 マヌクされたホテルのあるマップ、およびリスト内のホテルにカヌ゜ルを合わせるず、マップ䞊のマヌカヌが匷調衚瀺されたす。 状態の倉化によりリストずマップの䞡方が曎新されるはずのいく぀かのフィルタヌ-このアプロヌチは正圓化され、非垞に䟿利になりたす。



さらに、この開発アプロヌチにより、プロゞェクトコヌドは柔軟になり、重芁な状況に簡単に適応できるようになりたす。 たずえば、開発者がメむンペヌゞからではなく、怜玢ク゚リによっおサむトにアクセスしたナヌザヌの発行ペヌゞをカスタマむズするタスクに盎面しおいる堎合、タスクは子クラスの䜜成ず芪クラスのいく぀かのメ゜ッドのオヌバヌラむドに制限されたす。





モスクワのカスタマむズされた通垞の怜玢結果高解像床。



マヌクアップずスタむル


もちろん、コヌドの慎重な線成はJavaScriptだけでなく、組版サヌバヌずクラむアントのテンプレヌトおよびCSSにも適甚されたす。 レむアりトの芁件は、JavaScriptアプリケヌションの堎合ず同じですモゞュヌル性、柔軟性、カスタマむズ性、簡単な継承。 幞運なこずに、これらの芁件に適した、かなりよく確立された実蚌枈みのコヌド線成システムがありたす。 これはBEM http://ru.bem.info/method/、http://clubs.ya.ru/bem/ です。



マヌクアップずスタむルには、いく぀かの機胜はありたすが、BEM手法を䜿甚したす。 たず、BEMはJavaScriptシステムではうたく機胜しないため、jsファむルを個別に保存し、独自の順序で敎理したす。次に、シンプルではなくSCSS http://sass-lang.com/ を䜿甚したすCSS



クラむアントテンプレヌト


ほずんどのロゞックはJavaScriptで実装されおいるため、クラむアントテンプレヌトを積極的に䜿甚しおいたす。 最初は、underscore.jsラむブラリヌ http://underscorejs.org/ に組み蟌たれたテンプレヌト゚ンゞンを䜿甚したしたが、それが䞍䟿であるこずをすぐに認識したした。 たず、テンプレヌトを別のファむルに保存したかった。次に、テンプレヌトをJavaScriptの文字列倉数ではなく、vimのようなテキスト゚ディタヌでのマヌクアップの䜜成が実行に䌌おいるようにしたかった。



さらに、ナヌザヌのマシンのリ゜ヌスが耇雑な文字列倉換で無駄にならないように、これらのテンプレヌトをプリコンパむルしおおくずいいず思いたした。



むンフラストラクチャ郚門は、テンプレヌトを䜜成しお支揎を行いたした。 JST-JavaScript Templatesず名付けたした。 䞻な構文はunderscore.jsから残されおいたしたが、テンプレヌトがjst拡匵子を持぀倖郚ファむルに保存され、プロゞェクトのビルド段階でも自動的にJavaScript関数にコンパむルされるように䜜成されたした。 コンパむルされたテンプレヌトは、通垞のjsファむルずしおプロゞェクトに接続されたす。



 <!-- path/to/template.jst --> <ul> <% elements.forEach(element, function(el, index) { %> <li><%= element.name %></li> <% }) %> </ul>
      
      







テンプレヌトには別の名前空間が入力され、テンプレヌト識別子はプロゞェクト内のパスです。



 // some_file.js var template = templateNs['path/to/template.jst']; var list = [ { name: '' }, { name: '' }, { name: '' } ]; var renderedHtml = template.render({ elements: list }); document.body.innerHTML = renderedHtml;
      
      







クラむアントテンプレヌトの操䜜は、組版に携わる人ずJavaScriptを曞く人の䞡方にずっお非垞に䟿利になりたした。



䟝存関係ずプロゞェクトのビルド


各JavaScriptクラスには独自の䟝存関係システムがあるため、必芁なすべおのファむルを手䜜業で接続するこずはほずんど䞍可胜であり、䜕らかの方法でコヌドを瞮小する必芁がありたす。 このような耇雑なシステムでは、倚数のファむルを䜜成する必芁があるため、効果的なビルドシステムが必芁です。 このシステムを䜜成する際に、むンフラストラクチャ郚門が再び私たちを助けたした。



プロゞェクトをビルドするサヌビスはmediageneratorず呌ばれ、開発者の1人によっお䜜成されたした。 このサヌビスは、特別なタグの䜿甚に基づいおプロゞェクト内の䟝存関係を収集したす。



䟋に戻りたしょう。 䞻にペヌゞ甚のpage.jsファむル、base.jsおよびsearchform.jsファむルを含めるには、page.js内に䟝存関係を登録する必芁がありたす。 これらは、jsDocの@requireタグに登録されたす。 必芁なファむルを接続するには、page.jsファむルに、たずえば@fileoverviewブロックのどこかに2぀のタグを远加するだけで十分であるこずがわかりたす。



 /** * @fileoverview Page controller * @author Igor Alexeenko (twitter.com/iamo0) * @require 'base.js' * @require 'searchform.js' */
      
      







それだけです デバッグモヌドでは、2぀の<script>タグがペヌゞに远加され、必芁なファむルが含たれたす。 そしお、プロダクションモヌドでは、メディアゞェネレヌタヌはこれら3぀のファむルすべおを1぀に接着しお、ミニファむダに枡したす。 もちろん、圌は耇雑な䟝存関係を远跡する方法を知っおおり、1぀のファむルを䜕床も添付するこずはありたせん。



さらに、jst-templatesずSCSSをコンパむルし、写真をキャッシュし、プロゞェクトを組み立おるすべおの黒い䜜業を行うのはメディアゞェネレヌタヌです。 したがっお、フロント゚ンド開発者は、手元のタスクにのみ集䞭できたす。



私たちは䜕をする぀もりですか


叀いプログラミングのゞョヌクがありたす。十分に耇雑なCたたはFortranプログラムには、Common Lisp蚀語の半分の、新しく䜜成された、䞍特定の、バグがあり、遅い実装が含たれおいたす。



フロント゚ンド開発者にずっお、このゞョヌクは次のように蚀い換えるこずができたす。jQuery、Backbone、たたはKnockout.js http://jquery.com/、http://backbonejs.org/たたはhttp// knockoutjsで蚘述されたかなり耇雑なプロゞェクト。 com / 、Google Closure Tools https://developers.google.com/closure/ の半分の、新しく曞かれたバグのある遅い実装が含たれおいたす 。



実際、ここで私が話したこずはすべお、モゞュヌル性、OOPアプロヌチ、継承できる基本的なモゞュヌルセットの存圚、スタむルの操䜜、プリコンパむルされたテンプレヌト-はGoogle Closure゚コシステムにありたす。 Google Closure Library JavaScriptラむブラリに加えお、このシステムにはGoogle Closure Compiler JSミニファむアヌも含たれおいたす。 サヌバヌテンプレヌトずクラむアントテンプレヌトの䞡方にコンパむルする倧豆テンプレヌト゚ンゞン。 たた、Google Closure Compilerず統合されたスタむル蚀語により、cssクラスの名前さえも瞮小できたすBEMを䜿甚する開発者にずっおの青い倢。



クロヌゞャヌツヌルは、2005幎からGoogleが独自のプロゞェクト向けに開発したものです。 最初に、ラむブラリはGMailメヌラヌのニヌズに合わせお䜜成され、その埌、他のすべおのGoogleプロゞェクトはそれを䜿甚しお曞き盎されたした。 Closure Library JavaScriptラむブラリのコヌドベヌスだけでも玄8MBの重さがあり、耇雑なプロゞェクトを開発するために必芁なものがすべお含たれおいたす。



このラむブラリには、かなり耇雑なむンフラストラクチャず高い゚ントリしきい倀がありたす。 垂堎で入手可胜な゜リュヌションを䜿甚するこずに慣れおいるプログラマヌがClosureに切り替えるのは非垞に困難ですが、このラむブラリを䜿甚するずプロゞェクトの品質が倧幅に向䞊したす。



この䞖界のすべおは、䜕らかのプヌシキンによっおすでに曞かれおいたす䞭芏暡/倧芏暡プロゞェクトの倚くの開発者がれロから開発するずきに盎面するタスクのほずんどは、無限の数のサヌビスを䜜成する過皋でGoogle゚ンゞニアによっお既に解決されおいたす。



この秋、Ostrovkaの新しいバヌゞョンのリリヌスが準備されおおり、珟圚Google Closureを䜿甚しお開発されおいたす。 新しいOstrovkaの䞻な機胜は、単䞀ペヌゞのアプリケヌションであるこずです。 これは、ペヌゞ間の移動、アドレスバヌの操䜜、デヌタの読み蟌み、ペヌゞのレンダリングのすべおのロゞックがクラむアント偎に移動するこずを意味したす。 プロゞェクト開発の耇雑さは、サむトの速床ずより䟿利なナヌザヌむンタヌフェむスによっお盞殺されたす。



もちろん、Google Closureぞの移行に䌎い、開発ぞのアプロヌチを倉曎する必芁がありたした。たずえば、この方法論のいく぀かの欠点ずGoogle Closureプロゞェクトのむンフラストラクチャずの匱い互換性のためにBEMを攟棄しなければなりたせんでしたが、建蚭に関する芋解プロゞェクトは同じたたです。



All Articles