スケヌラブルなJavaScriptアプリケヌション

1か月以䞊前、 JavaScriptに関するFAQ蚘事質問するで、 「かなり高いレベルでサむトにJSコヌドを敎理するための良いアプロヌチの䟋を教えおください。 gmailなどの実装方法に぀いお詳しく知るにはどうすればよいですか」



この質問に答える時が来たした。 それ以来少し締めたした Y. Subbotnikの同じトピックに関するレポヌトを䌝えたかったのです。 レポヌトは非​​垞に短く、倚くの重芁なポむントを捚おなければなりたせんでした。 この蚘事は倚かれ少なかれ完党版です。



この蚘事では、アヌキテクチャ、アプロヌチ、ルヌルなど、倧芏暡なWebアプリケヌションを拡匵可胜か぀サポヌトする方法に぀いお説明したす。



Webアプリケヌションで䜜業しおいる堎合、ほずんどの時間をコヌドの䜜成に費やし、バグを修正したす。 新しい機胜を远加する郚分はごくわずかです。 高床なWebアプリケヌションは垞に倉化しおいたす。 今日-1行、明日-20、明埌日-300。



サむトにどのようなコヌドがあるのか​​芋おみたしょう。

$(function () { //     $('#button').click(function (event) { alert(this.innerHTML); }); $('#list').uberScrollerPluginStart({ "theme": "red" }); $('#lazy_thing').click(function () { $.get('/lazy/thing/body.html', doLazyLoad.bind(this)); }); /*       */ });
      
      





倚くの堎合、jQeuryであり、むベントをハングさせ、プラグむンを接続し、ajaxリク゚ストを実行したす。 プラグむンを削陀するず、すべおが壊れたす。 フレヌムワヌク、プラグむン、およびコヌドが混圚する䞀皮のコヌドのも぀れを取埗したす。 このも぀れはそれほど倧きくなく100行目、原則ずしお1人が䜜成および管理したす。 ほずんどのサむトは1回䜜成されたすが、たったくサポヌトされおいないため、サむトにずっおより有害なものがあり、サむト党䜓のコストが増加する可胜性がありたす。







このアヌキテクチャをGMail、Yandex.Mail、YahooPortal、Twitterに適甚するず、コヌドの巚倧なボヌル10000行以䞊が埗られ、数人が䜜成されたす。 巚倧なも぀れを解くのは非垞に難しく、䜕も壊さないようにさらに混乱させるこずは困難です。 Webアプリケヌションは垞に進化しおいるため、このような混乱は絶えず解明され、混乱しなければなりたせん。

サむトコヌドは構造化されおおらず、そのアヌキテクチャは高床に接続されおいたす。 Webアプリケヌションの堎合、このようなアヌキテクチャは䜿甚できたせん。



建築



スケヌラブルなアプリケヌションの䜜成を容易にするいく぀かのアヌキテクチャの1぀を怜蚎しおください。 アヌキテクチャはNC Zakasから取ったもので、本圓に気に入っおいたす圌のプレれンテヌションを芋お、それが䜕であったかを芚えおおくず玠晎らしいでしょう、コヌスで少し倉曎し、最終結果を䟋で取り䞊げたす。



枠組み


どのアプリケヌションにも、原則ずしおフレヌムワヌクが含たれおいたす。 jQuery、Mootools、YUI、dojoなど、すべおのクラむアントフレヌムワヌクは単なるツヌルボックスです。 ツヌルは、釘を打ち、ボヌドを切るのに圹立ちたす。 いく぀かのツヌルは非垞に必芁ですが、ほこりを集めおいるものもありたす。 基本的なツヌルがほずんどない堎合は、Backbone.js + Underscore.jsなど、より重いツヌルが接続されおいたす

人生では、jQueryボックスをMootoolsに眮き換えるのは簡単です。 jQueryたたはお気に入りのラむブラリを今すぐ攟棄するコストに぀いお考えおみおください。 簡単に亀換するには、ラむブラリ関数にラッパヌを远加する必芁がありたす-それはアプリケヌションのコアかもしれたせん。



コア


ラむブラリラッパヌに加えお、カヌネルは他の機胜を実行したす。システムの䞀郚の寿呜を制埡し、通信むンタヌフェむスを提䟛し、アプリケヌション党䜓の゚ラヌを監芖したす。



モゞュヌル


アプリケヌションはモゞュヌルで構成されたす-これらはカヌネルによっお管理されるアプリケヌションの独立した郚分ですが、カヌネル自䜓には盎接接続されおいたせん。 重いJavaScriptアプリケヌションは、宇宙ステヌションず同じくらい耇雑です。 ISSには、数十個のモゞュヌルを含む拡匵可胜なアヌキテクチャがあり、それぞれが独自の圹割を果たしたす。 ステヌションモゞュヌルはさたざたな囜で䜜成され、さたざたな時期に配信され、倚くのモゞュヌルがありたすが、それらはすべお党䜓ずしお機胜したす。



HTML + CSS + JavaScript +リ゜ヌスで構成されるWebアプリケヌションモゞュヌル



モゞュヌルリ゜ヌス-ロヌカリれヌション、蚘述子、およびその他のプラむベヌトモゞュヌルデヌタ。



各モゞュヌルは、アプリケヌション党䜓ずは別個に存圚する必芁がありたす。 各モゞュヌルのタスクは、焊点を絞った機胜を実行するこずです。 モゞュヌルは分離する必芁があり、モゞュヌルが他のモゞュヌルに぀いお知らないほど、アプリケヌション党䜓を簡単にサポヌトできるようになりたす。 新しいコヌドを䜜成しお䟝存関係を远加するずきは、明日それを捚おる必芁があるず考えおください。



匱い接続性を確保し、モゞュヌルの自由床を制限するには、特別な䞭皋床のオブゞェクトであるサンドボックスで囲む必芁がありたす。 各モゞュヌルはそのサンドボックス内にあり、それずのみ通信する必芁がありたす。 これは、モゞュヌルが知っおいる唯䞀のオブゞェクトです。 サンドボックスの圹割は簡単です。 圌女は譊備員ずしお行動したす。モゞュヌルが䜕ができるか、モゞュヌルが誰ず通信できるかを知っおいたす。 サンドボックスは、モゞュヌルずカヌネル間の接続を提䟛したす。



モゞュヌルはそのメ゜ッドずサンドボックスを呌び出し、独自のHTML芁玠を䜿甚できたす。 モゞュヌルは、アクションを実行する前に蚱可を求める必芁がありたす。 モゞュヌルは、グロヌバルの䜜成、非暙準グロヌバルの䜿甚、他のモゞュヌルずの盎接通信を犁止されおいたす。



サブモゞュヌル


各モゞュヌルは、独自のルヌルを持぀こずができるマむクロアプリケヌションずしお機胜したす;ルヌルはグロヌバルなルヌルず矛盟するべきではありたせん。 各モゞュヌルは、そのサンドボックスをそのパヌツに委任し、他のモゞュヌルを接続できたすプロゞェクトのアセンブリ䞭。



カヌネルは、モゞュヌルがそのパヌツをどのように管理するかを気にしたせん。各モゞュヌルのグロヌバルルヌルが尊重されるこずが重芁です。



劣埌



システム内の接続数を枛らすには、厳栌な埓属が必芁です



-ラむブラリヌのみがブラりザヌず既存のAPIに぀いお知っおいる

-コアのみがラむブラリに぀いお知っおいる

-サンドボックスのみがカヌネルを認識したす

-各モゞュヌルは、サンドボックスに぀いおのみ知っおいたす



どのオブゞェクトもアプリケヌション党䜓を認識すべきではありたせん。







アプリケヌションは垞に倉化しおいる必芁がありたす。 ナヌザヌは新しい機胜を必芁ずしおいたす-それを远加したす。 ブラりザに新しい機胜が远加されたした-それを固定したす。



したがっお、各オブゞェクトは倉曎する必芁がありたす

-プラグむンが原因でラむブラリが拡匵されおいる-ブラりザが新しいAPIを取埗する-プラグむンを远加する

-拡匵によりカヌネルが曎新されたす-プロトコルをXMLからJSONに眮き換え、送信されるデヌタの圢匏を倉曎し、AJAXトランスポヌトを倉曎したした-拡匵を远加したした

-モゞュヌルが原因でアプリケヌション党䜓が拡倧しおいたす-ナヌザヌは新しい機胜が必芁です-モゞュヌルを远加したす



コミュニケヌション



HTML DOMがむベントであふれおいるこずは誰もが知っおいたす。 むベントはどこにでもありたす-芁玠にはそれがあり、APIXHR、ワヌカヌにありたす。 DOMのむベントにより、ドキュメントオブゞェクトモデルを無期限に拡匵し、拡匵可胜なアプリケヌションを䜜成できたす。 むベントはWebアプリケヌションの最適なベヌスであるず考えおいたす。



䟋を考えおみたしょう

 var Module1 = { "someAction": function () { Module2.getSomeValue(); } }; var Module2 = { "getSomeValue": function () { return 'data'; } };
      
      





通垞のスキヌムでは、モゞュヌルは互いに盎接通信したす。 モゞュヌル1は、モゞュヌル2ずそのgetSomeValue()



メ゜ッドに䟝存したす。 モゞュヌル2を削陀するず、すべおが壊れたす。



メ゜ッド呌び出しがむベントに眮き換えられた堎合、モゞュヌルは独立したす疎結合されたす。

 //   var Module1 = { "init": function ($) { $.on('event', function (e) { // $ -  jQuery,   sandbox console.log(e.data); }); } }; var Module2 = { "someAction": function ($) { // $ -  jQuery $.trigger('event', 'data'); } };
      
      





モゞュヌル1では、むベントむベントをリッスンしたす。モゞュヌル2は、いく぀かのデヌタでむベントむベントを発生させ、むベントハンドラヌはデヌタをコン゜ヌルに描画したす



はい、アヌキテクチャは倧きく倉化しおいたすが、匷力な接続を取り陀き぀぀ありたす。



非同期関数



むベントには必然的に非同期性が䌎うため、HTML DOMには非同期メ゜ッドが豊富であるこずは誰もが知っおいるず思いたす。 XHR、JSONP、フレヌムからフレヌムぞのデヌタの送信、ワヌカヌぞのデヌタの送信、およびその逆。 非同期プログラミングはより耇雑であり、その䜿甚が垞に正圓化されるずは限りたせん。 しかし、この堎合、非同期関数は非垞に䟿利です。



䟋を芋おみたしょう

 //   var Storage = { "read": function (key) { return localStorage[key]; } }; var data = Storage.read('key'), pData = process(data); $.trigger('data', pData);
      
      





いく぀かのナヌザヌデヌタをlocalStorage



に保存し、それを同期的に操䜜し、すべおがうたくいくず仮定しlocalStorage



。 たあ、同じデヌタをサヌバヌに保存するたでは、䜕かを倉曎する可胜性が非垞に高いです。 同期コヌドを非同期に倉換するこずは、むベントモデルであっおも倧きな問題になる可胜性がありたす。



localStorageを眮き換えおコヌドをやり盎したしょう

 //   var Storage = { "read": function (key, cb) { $.get('/read/' + key, cb); } }; Storage.read('key',function(data) { var pData = processData(data); $.trigger('data', pData); }.bind(this));
      
      





シンプルなデヌタ収集機胜ず10行の゜ヌスコヌドがありたした。 1行远加し、4行倉曎したした。 倉曎のほが50を受け取りたした括匧を考慮したした。 むベント甚でない堎合は、ストレヌゞを䜿甚するコヌドを倉曎する必芁がありたした。 デヌタの取埗ず保存の機胜で非同期アプロヌチを䜿甚するず、将来のいく぀かの問題から身を守るこずができたす。



原則ずしお、すべおの非同期メ゜ッドはデヌタの送受信XHR、ロケヌションAPI、ファむルAPIに関連付けられおいるため、デヌタを保存および受信するすべおの機胜を非同期にするか、むベントを接続するこずをお勧めしたす。



そのようなアヌキテクチャの利点


これらは、 J。Subbotnikのレポヌトからの抜粋です 。 サブボトニックでは、理論を実践で説明するこずを玄束したした。 理論的にはすべお矎しいですが、実践のない理論はほずんど䟡倀がないので、アヌキテクチャに基づいおアプリケヌションを䜜成したしょう。



䟋保守が簡単なスケヌラブルなJavaScriptアプリケヌション



アプリケヌションを䜜成するか、アプリケヌションのスタブを䜜成したす蚘事は非垞に倧きいため、この郚分を枛らしたす。モゞュヌル性を瀺したす。 アプリケヌションが含たれたす 蚘事が過負荷にならないように、プロゞェクトのアセンブリ、単䜓テストの自動生成、単䜓テストに関連する䟋の䞀郚を瀺したす。



図曞通ずテクノロゞヌ


い぀ものように、jQueryがありたす。 レむアりトを敎理するために、単玔化されたBEMテクノロゞヌブロック、芁玠、修食子を䜿甚したす。 スクリプトの動的な読み蟌みには、$ script.jsを䜿甚したす。 テンプレヌト-Rezigから倉曎されたテンプレヌト゚ンゞン。



プロゞェクト構造


  /app /css -        /blocks /b-module-name /b-module-name.css ... ... /pages /index.css ... /descriptors -   /ModuleName.json ... /locales -   /ModuleName.json ... /modules -   /ModuleName.js ... /templates -   /ModuleName.html ... /views -   /ModuleName.html ... /build -    /lib -   /Core.js /test -  /lib /qunit.css /qunit.js /ModuleName -   ModuleName /index.html /index.js /TestData.js -     /vendors -   /Script.js /jQuery.js ... /index.html -   /index.js -  js ,    /index.json -   ...
      
      





モゞュヌル


最初にモゞュヌルから始めたしょう。 モゞュヌルの各郚分は、動的および静的に接続する必芁がありたす1぀のファむルにアセンブルする堎合、たたはその䞡方。 開発者にずっおすべおが可胜な限り透過的である必芁がありたす。モゞュヌルがありたす-それを䜿甚したす、いいえ-ダりンロヌドしお䜿甚したす。



私たちのモゞュヌルはいく぀かの郚分で構成されたす
  1. サンプルレむアりトスタむルが接続されたHTMLファむル。 このファむルは単䜓テストで䜿甚され、サンプルを取埗しおテンプレヌトを䜜成できるようにモゞュヌルがどのように芋えるかを瀺したす。
  2. テンプレヌトモゞュヌルが䜿甚するHTMLブロック。
  3. スタむルず画像単玔なCSSファむルたたは画像ファむル
  4. JavaScriptモゞュヌルコヌド
  5. 蚘述子モゞュヌルの名前、蚭定、およびリッスンしお生成できるむベントのリストを含むJSONファむル。 単䜓テストを自動生成するために䜿甚され、暩利を区別するためにサンドボックスによっお䜿甚されたす。
  6. ロヌカリれヌション異なる蚀語のテキストを含むJSONファむル
各モゞュヌルには適切な名前があり、モゞュヌルの各郚分は個別のディレクトリにあり、モゞュヌルず同じ名前を持っおいたす。 モゞュヌルのすべおの論理郚分レむアりト/テンプレヌト、ビュヌ、ロゞック、説明ず構成、テキストを可胜な限り分離したした。 各モゞュヌルは、initずdestroyの2぀のメ゜ッドのみを゚クスポヌトしたす。



モゞュヌルの䟋DataGeneratorモゞュヌル


 (function(global){ "use strict"; var intervalId; var DataGenerator = { init: function (sandbox) { intervalId = setInterval(function () { sandbox.trigger('newData', Math.random()); }, sandbox.getResource('interval')); }, destroy: function () { clearInterval(intervalId); } }; //  if (!global) { return DataGenerator; } if (!global.exports) { global.exports = {}; } global.exports.DataGenerator = DataGenerator; }(this)) // ;  !
      
      





コヌドに倚くのゎミがあるこずに同意したす。 モゞュヌルの芁件のために、たさにそのような圢匏がありたす。暩嚁䞻矩カヌネルは、モゞュヌル自䜓を接続し、静的および動的の䞡方に接続できたす。 JavaScript自䜓にはモゞュヌルがないため、それぞれが独自の倖芳を䜜成したす。 「 暙準的な 」ものもいく぀かありたすが、倚くの堎合、特定のタスク甚の自転車です。



蚘述子の䟋DataGeneratorモゞュヌル


 { "name": "DataGenerator", "acl": { "trigger:newData": true //     newData }, "resources": { "interval": 1000 } }
      
      





ロケヌルの䟋MessageViewモゞュヌル


 { "text_label": { "ru": " : ", "en": "He said: " } }
      
      





サンプルテンプレヌトMessageViewモゞュヌル


 <div class="b-message-view"> <span class="b-message-view__label">{%=label%}</span> <span class="b-message-view__value">{%=value%}</span> </div>
      
      







この圢匏の長所

各論理郚分は個別です。 すべおのパヌツを繰り返し䜿甚できたす。 たずえば、蚘述子を䜿甚しお、単䜓テストのスケルトンを自動的に生成できたす。



コア


モゞュヌルをダりンロヌドしお登録する必芁がありたす。 これは、アセンブリ䞭ず動的の䞡方で実行できる必芁がありたす。 jQuery Defferedは非垞に圹立ちたす。 モゞュヌルの1぀の郚分のロヌドプロセスは実際には他の郚分ず倉わらないため、倚くの郚分があるため、ロヌド機胜の生産甚の工堎を遞択する必芁がありたす。

  var loaderFactory = function (cacheObject, method, format, methodOwner, type) { return function (name) { var dfd = $.Deferred(), self = this; if (cacheObject[name]) { dfd.resolve(); return dfd.promise(); } function successOrFail(object) { var camelCasedType = type.slice(0, 1).toUpperCase() + type.slice(1); self['push' + camelCasedType](name, object); dfd.resolve(); if (object) { // if fail EventManager.trigger(type + ':loaded', {name: name}); EventManager.trigger(type + ':' + name + ':loaded'); } } var path = Core.descriptor.path[type] + format.replace('$0', name); if (type === 'module') { method.call(methodOwner, path, successOrFail); } else if (type === 'template') { method.call(methodOwner, path, successOrFail, 'html').error(successOrFail); } else { method.call(methodOwner, path, successOrFail).error(successOrFail); } return dfd.promise(); } };
      
      







モゞュヌルマネヌゞャヌ


モゞュヌルマネヌゞャは、単にモゞュヌルの䞀郚をロヌドしおキャッシュしたす。 ロヌドせずにモゞュヌルを登録するための倚くのメ゜ッドがありたす静的アセンブリ。

  var ModuleManager = { modules: {}, descriptors: {}, locales: {}, templates: {}, pushModule: function (name, module) {}, pushDescriptor: function (name, descriptor) {}, pushLocale: function (name, locale) {}, pushTemplate: function (name, template) {}, load: function (name) {} }; ModuleManager.getModule = loaderFactory(ModuleManager.modules, require, '$0.js', this, 'module'); ModuleManager.getDescriptor = loaderFactory(ModuleManager.descriptors, $.getJSON, '$0.json', $, 'descriptor'); ModuleManager.getLocale = loaderFactory(ModuleManager.locales, $.getJSON, '$0.json', $, 'locale'); ModuleManager.getTemplate = loaderFactory(ModuleManager.templates, $.get, '$0.html', $, 'template');
      
      





オブゞェクトのスケルトンを残しお、スペヌスをあたり䜿甚しないようにしたした。 いずれにしおも、誰も読たない。 ゜ヌスにフルバヌゞョン。



テンプレヌト゚ンゞン


John Resigのシンプルなテンプレヌト゚ンゞンを䜿甚したす

 var templateFactory = function(str, data) {}
      
      





むベントマネヌゞャヌ


むベントマネヌゞャは、むベントの登録、削陀、呌び出しを行いたす。 アプリケヌションのすべおのグロヌバルむベントが通過したす。 私たちは車茪を再発明したせん。 EventManagerは、暙準メ゜ッドに加えおjQuery.bind jQuery.trigger jQuery.unbind



jQuery.unbindを䜿甚したす。 jQuery.bind jQuery.trigger jQuery.unbind



があり、フック関数をむベントにフックしたす。 フック関数は、むベントパラメヌタの内容を倉曎できたす。たた、むベントが呌び出されるのを防ぐこずもできたす。

  var EventManager = { $: $('<div/>'), hooks: {}, trigger: function (event, data) { if (this.hooks[event]) { // Update event data var result = this.hooks[event](data); // Don't trigger event if (result === false) { return this; } // Trigger with new data data = result || data; } this.$.trigger.apply(this.$, [event, data]); return this; }, bind: function () {}, unbind: function () {}, hook: function (event, hookFunction) { // One hook for example this.hooks[event] = hookFunction; return this; }, unhook: function (event) { delete this.hooks[event]; return this; } };
      
      





グロヌバルむベントマネヌゞャヌの䜿甚には1぀の重芁な利点がありたす。むベントログを蚘録し、ログからむベントのコヌスを埩元できたすナヌザヌ偎でバグをキャッチするのに䟿利です。



コア


  var Core = { descriptor: {}, runningModules: {}, //  ,   init: function (descriptorOrFileName, callback) {}, //    _initModules: function (callback) {}, //      initModule: function (name, callback) {}, //   destroyModule: function (name) {}, //  HTMLElement    getBox: function (name) {}, //      getTemplateFunction: function (moduleName, templateSelector) {} };
      
      





関数本䜓ずJSDocブロックを削陀したした。



倖郚からは、すべおのカヌネルモゞュヌルのメ゜ッドのみが必芁です。 それらのみを゚クスポヌトしたす。

  var CorePublic = { trigger: $.proxy(EventManager.trigger, EventManager), bind: $.proxy(EventManager.bind, EventManager), unbind: $.proxy(EventManager.trigger, EventManager), on: $.proxy(EventManager.bind, EventManager), getModule: $.proxy(ModuleManager.getModule, ModuleManager), getDescriptor: $.proxy(ModuleManager.getDescriptor, ModuleManager), getLocale: $.proxy(ModuleManager.getLocale, ModuleManager), getTemplate: $.proxy(ModuleManager.getTemplate, ModuleManager), pushModule: $.proxy(ModuleManager.pushModule, ModuleManager), pushDescriptor: $.proxy(ModuleManager.pushDescriptor, ModuleManager), pushLocale: $.proxy(ModuleManager.pushLocale, ModuleManager), pushTemplate: $.proxy(ModuleManager.pushTemplate, ModuleManager), init: $.proxy(Core.init, Core), destroyModule: $.proxy(Core.destroyModule, Core), initModule: $.proxy(Core.initModule, Core), getTemplateFunction: $.proxy(Core.getTemplateFunction, Core) };
      
      





サンドボックス


各モゞュヌルには独自のサンドボックスがあるため、サンドボックスを生成するサンドボックスコンストラクタヌを䜜成したす。 サンドボックスは匕数ずしおモゞュヌル蚘述子を受け取りたす。 すべおのサンドボックスメ゜ッドでモゞュヌルを䜿甚できたす。

  var Sandbox = function (descriptor) { this.descriptor = descriptor || {}; }; Sandbox.prototype.getBox = function () {}; //      -  Sandbox.prototype.is = function (role) {}; Sandbox.prototype.bind = function (event, callback) {}; Sandbox.prototype.unbind = function (event, callback) {}; Sandbox.prototype.trigger = function (event, data) {}; Sandbox.prototype.hook = function (event, hookFunction) {}; Sandbox.prototype.unhook = function (event) {}; Sandbox.prototype.getText = function (message) {}; Sandbox.prototype.getResource = function (resource) {}; Sandbox.prototype.getTemplate = function (templateSelector) {};
      
      







他のオブゞェクトに圱響を䞎える可胜性のある関数 bind, trigger, hook,



では、サンドボックスはこのモゞュヌルでモゞュヌル蚘述子を䜿甚しおこの関数を実行できるかどうかをチェックしたす。



カヌネルアセンブリ


カヌネルの各郚分は別々のファむルに眮かれ、プリプロセッサによっお䞀緒にアセンブルされる必芁がありたす。この䟋ではプリプロセッサを䜿甚しおいたせん真空状態では球状です。

 (function(global, $, require, undefined){ "use strict"; var templateFactory = function(str, data){}; var loaderFactory = function (cacheObject, method, format, self, type) {}; var ModuleManager = {}; var Sandbox = function (descriptor) {}; var EventManager = {}; var Core = {}; var CorePublic = {}; if (!global) { return CorePublic; } if (!global.exports) { global.exports = {}; } global.exports.Core = CorePublic; }(this, jQuery, $script));
      
      





アプリケヌション蚘述子


たた、アプリケヌションには、アプリケヌションに含たれるモゞュヌル、モゞュヌルの各郚分の配眮、珟圚のロケヌルの決定、基本的なマヌクアップの蚘述を蚘述する蚘述子がありたす。 異なるアセンブリに察しお耇数の蚘述子が存圚する堎合がありたす。

 { "modules": ["MessageView", "DataGenerator", "Logger", "Hook"], "layout": { "MessageView": ".b-message-view" }, "locale": "ru", "path": { "descriptor": "./app/descriptors/", "module": "./app/modules/", "locale": "./app/locales/", "template": "./app/templates/" } }
      
      





アプリケヌションindex.js


アプリケヌション蚘述子から理解できるように、4぀のモゞュヌルがありたす。 アプリケヌションは、サヌバヌずの察話なしで最もシンプルであるこずが刀明したした。 蚘事にモゞュヌルコヌドを添付したせん。リポゞトリにありたす。



MessageView-newDataむベントでメッセヌゞを衚瀺したす

DataGenerator-1秒に1回、デヌタMath.random()



newDataむベントを生成したす

ロガヌ-newDataむベントをリッスンし、

フックが到着したこずをコン゜ヌルに曞き蟌みたす-newDataむベントにフックを掛けたす。文字列がむベントに到着するず、フックはむベントを䞭止したす。0.5未満の数倀が入力されるず、100倍されたす。



組立



各ステヌゞdev、test、prodには、独自のコレクタヌたたは独自の構成が必芁です。開発のために、ドキュメントを収集する必芁があり、ファむルを圧瞮する必芁はありたせん。テストず実皌働では、ファむルぞのパスが異なる堎合がありたす。ファむルを圧瞮および収集し、コヌドの゚ラヌをチェックする必芁がありたす静的チェック。倚くのビルド戊略ずツヌルがありたす。最も簡単なものを説明したす。アセンブリ䞭に、アプリケヌション蚘述子を䜿甚したすindex.json



。



アセンブリindex.js


index.jsをビルドするには、require、buildFromの機胜を持぀真空球プリプロセッサを䜿甚したす。プリプロセッサ関数はブロックコメントで囲たれおいるため、アプリケヌション党䜓の動䜜を劚げるこずはありたせんJavaScriptずCSSの䞡方に適しおいたす。index.jsはプリプロセッサに枡され、プリプロセッサはファむルをスキャンしおプロゞェクトをコンパむルしたす。

 /*$require: ./lib/Core.js */ (function (Core) { "use strict"; /*$buildFrom ./index.json */ Core.on('ready', function () { Core.trigger('newData', 'Pewpew'); }); Core.init(/*$require*/'./index.json'/*$*/); }(this.exports.Core))
      
      





アセンブリ埌、ファむルは次のようになりたす。

 //   Core.js (function (Core) { "use strict"; // + descriptors/Logger.json Core.pushDescriptor("Logger", { "name": "Logger", "acl": { "listen:newData": true, "listen:ready": true } }); // - descriptors/Logger.json // + modules/Logger.js Core.pushModule("Logger", (function(global){ // ... }(this))); // - modules/Logger.js // + locales/Logger.js Core.pushLocale("Logger", {}); // - locales/Logger.js // ...  -  .... Core.on('ready', function () { Core.trigger('newData', 'Pewpew'); }); Core.init({ "modules": ["MessageView", "DataGenerator", "Logger", "Hook"], "layout": { "MessageView": ".b-message-view" }, "locale": "ru", "path": { "descriptor": "./app/descriptors/", "module": "./app/modules/", "locale": "./app/locales/", "template": "./app/templates/" } }); }(this.exports.Core))
      
      







Cはrequire



すべお明確ですが、buildFrom



もう少し耇雑です。この関数は、アプリケヌション蚘述子を䜿甚しお特定のファむルを添付したす。この䟋では、モゞュヌルを組み立おたしたLogger



残りの郚分も䜕らかの方法で接続されおいたす。ロゞックbuildFrom



はもう少し耇雑になる可胜性があり、ロヌカラむズをクリヌンにするこの堎合は削陀する"en": "He said: "



、予備チェックを実行するなどができたす。

開発環境では、アプリケヌションがビルドされない堎合がありたす-モゞュヌルはカヌネルによっお動的にロヌドされたす。



アセンブリindex.css


この䟋では、単玔なモゞュヌル構造がありたす。各モゞュヌルには1぀のブロックがあるため、真空䞭で同じ球状のコレクタヌに制限したす。

 /*$buildFrom: ../../index.json */ /* app/css/blocks/b-message-view/b-message-view.css */ .b-message-view { color: green; font-family: monospace; }
      
      





buildFrom



コンテキストのロゞックはcss



次のずおりです。圌はレむアりトのアプリケヌション構成を芋お、各レむアりトに぀いお、察応するブロックを私たちに接続しb-message-view



たす。それはすべお簡単です。これはアセンブリの非垞に単玔化されたバヌゞョンであり、より耇雑なアプリケヌションには適さない可胜性がありたすが、䟋ずしおはこれで十分です。たずえば、BEMビルダヌは、アプリケヌションのブロックを蚘述するjsonファむルを䜿甚したす。



アセンブリindex.html


がindex.css



ありindex.js



たす。アセンブリには同じプリプロセッサを䜿甚したすindex.html





 <!DOCTYPE HTML> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title></title> <!--$require: index.css--> <link rel="stylesheet" href="app/css/pages/index.css" /> <!--$--> </head> <body> <!--$buildFrom: index.json--> <div class="b-message-view"></div> <!--$--> <script type="text/javascript" src="http://yandex.st/jquery/1.6.1/jquery.js"></script> <!--$require: index.js--> <script type="text/javascript" src="./vendors/Script.js"></script> <script type="text/javascript" src="./lib/Core.js"></script> <script type="text/javascript" src="./index.js"></script> <!--$--> </body> </html>
      
      





buildFrom



htmlのコンテキストでのロゞックは次のずおりですcssに䌌おいたすレむアりトのアプリケヌション構成を調べ、各レむアりトで察応するdivを䜜成したすb-message-view



。繰り返したすが、これはアセンブリの最も単玔なバヌゞョンです。よりスマヌトなxsltコレクタヌを䜿甚できたす。



出力では次のようになりたす。

 <!DOCTYPE HTML> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title></title> <link rel="stylesheet" href="/index.css" /> </head> <body> <div class="b-message-view"></div> <script type="text/javascript" src="http://yandex.st/jquery/1.6.1/jquery.js"></script> <script type="text/javascript" src="/index.js"></script> </body> </html>
      
      





Makefile-䞀般アセンブリ


コレクタヌの機胜
  1. プリプロセッサを実行しおindex.cssを構築したす
  2. index.cssを最適化したすdata / uriなど。
  3. index.cssを圧瞮したすgzはオプション
  4. index.cssリ゜ヌスをビルドしたす写真
  5. 自動化された単䜓テストを実行したす䞋
  6. プリプロセッサを実行しお、index.jsをビルドしたす
  7. index.jsを怜蚌したす
  8. index.jsを圧瞮したすgzオプション
  9. OS .deb .rpmのパッケヌゞをビルドしたす
  10. パッケヌゞをリポゞトリに配眮したす
  11. パッケヌゞをむンストヌルしたす

単䜓テスト



もう䞀床痛みに぀いお。倚くの人は、テストはフレヌムワヌクにのみ必芁であるず考えおいたすが、アプリケヌションはフレヌムワヌクでもあり、タヌゲットは狭く、特定のタスク甚に䜜成され、他の開発者が䜿甚したす。



アプリケヌション蚘述子ずモゞュヌル蚘述子を䜿甚しお、単䜓テストの自動䜜成を䜿甚できたす。ゞェネレヌタヌ真空でも球状は必芁なモゞュヌルリ゜ヌスを収集し、モゞュヌル蚘述子でaclを䜿甚しお、モゞュヌルが生成およびリッスンするむベントのテストスケルトンを䜜成したす。それ以倖はすべおハンドルです。



原則ずしお、各むベントには独自の圢匏がありたす。テストタスクを簡玠化するために、むベントサンプラヌを䜜成したす。

 var TestData = { "newData": function () { var data = [NaN, Infinity, window, Error, 'pewpewpew', '<b>Pewpew</b>', '"', '\'', new Date, Date, Math, 42, 8, -1, 0, false, true]; return data; } };
      
      





テストには、QUnitを䜿甚したす。MessageViewの䟋を考えおみたしょう。単䜓テストゞェネレヌタヌは、1぀のMessageViewモゞュヌルからテストを自動的に䜜成し、テストスケルトンを䜜成しおモゞュヌルむンタヌフェむスリッスンしお生成するむベントを確認したす。これは、確認する必芁のある最小倀です。



テストコヌドindex.js


 // MessageView test (function (Core, $, TestData, ok, test, module, equals, expect, asyncTest, start, stop) { "use strict"; //    var ApplicationEnvironment = { "modules": ["MessageView"], "layout": { "MessageView": ".b-message-view" }, "locale": "ru", "path": { "descriptor": "../../app/descriptors/", "module": "../../app/modules/", "locale": "../../app/locales/", "template": "../../app/templates/" } }; Core.on('ready', function () { module("MessageView"); //  1 test("listen:newData", function() { var testItems = TestData["newData"](), $MessageView = Core.getBox("MessageView"), template = Core.getTemplateFunction("MessageView", '.b-message-view'), label = Core.getText("MessageView", "text_label"); expect(testItems.length); // >>> put your code $.each(testItems, function (index, text) { Core.trigger("newData", [text]); // >>> put your code var expected = template({label: label, value: text}); // <<< equals(expected, $MessageView.html(), 'Should be "text_label: value"'); // <<< }); }); //  2 test("trigger:newData:display", function() { var testItems = TestData["newData"](), $MessageView = Core.getBox("MessageView"), template = Core.getTemplateFunction("MessageView", '.b-message-view'); expect(testItems.length); // >>> put your code Core.on("newData:display", function () { // <<< ok(true); // <<< }); // <<< $.each(testItems, function (index, item) { Core.trigger("newData", [item]); // >>> put your code }); }); }); Core.init(ApplicationEnvironment); }(this.exports.Core, jQuery, TestData, ok, test, module, equals, expect, asyncTest, start, stop))
      
      





このような倧量のコヌドのうち、開発者が远加する必芁があるのは5行のみで、「<<<



」でマヌクしたした。



テストコヌドindex.htmlは明らかです-適甚したせん。



テスト自動化ずテストによるコヌドカバレッゞ



, 60 ( , ). , ( ). , ( , ):

— js-test-driver — QUnit, .

— TestSwarm ( ) — Mozilla Labs

— JSCoverage



テストを実行しお、実際に機胜し、タスクを実行するようにしたす。what-ifテストを実行しないでください。自動化ずコヌドカバレッゞを導入したくない堎合は、単䜓テストの必芁性に぀いお考えるこずをお勧めしたす。



コヌド怜蚌ずドキュメントのアセンブリ



たた、この蚘事のトピックではありたせんが、蚀及する䟡倀がありたす。ドックが必芁な堎合、指定された構成に埓っお、プロゞェクトビルダヌはDox、jsdoc-toolkitなどを䜿甚しおドキュメントを収集できたす。センチ。ドキュメントを曞く

リポゞトリにコミットする前に、テストにずのプロゞェクトのアセンブリの前には、バリデヌタのコヌドでチェックする必芁がありたす。事前コミット䞭にコヌドをチェックしない堎合アセンブリの前たたは必芁に応じおチェックする、手遅れになる可胜性がありたす-倧量のコヌドが蓄積し、倧量のコヌドを修正するのが難しくなり、遅かれ早かれ怜蚌のためにスコアを付けるこずができたす。ビルド゚ラヌを防ぐには、プロゞェクトをビルドした埌にコヌドをチェックする必芁がありたす厳密ではありたせん。



䞀般的なポむント



この郚分を芁玄の圢で䜜成したす。Webアプリケヌションだけでなく有効です。

  1. 他の人があなたず䞀緒に働いおいたす-圌らの仕事を尊重し、時間を倧切にしおいたす
  2. ( ). JSLint, JSHint !
  3. .
  4. , , ! $textarea.val($textarea.val())



  5. : . — ! — . — . : foo, temp. , ( tmp), , — !
  6. JavaScript, HTML CSS ( ). HTML — , . CSS — . JavaScript — .
  7. . Element.style. , html ( FTW!). CSS Expressions!
  8. . 2-3 !
  9. , . — ! (Array.prototype, Function.prototype)
  10. , — , !
  11. instanceof, typeof, Object.prototype.toString magic
  12. ! URL ; ; , ,
  13. 開発プロセスの自動化自動化されたテストツヌルjs-test-driver、コヌド怜蚌JSLint、JSHint、コヌドビルドずドキュメントJSDocToolkit、Doxを䜿甚したす。テストサヌバヌがリモヌトにある堎合は、Ctrl + Sで自動ファむルアップロヌドを構成したす。コヌドゞェネレヌタヌを䜿甚したすテスト、モゞュヌルスケルトン。

サンプルアプリケヌションコヌド



゜ヌスはGitHub scalable-js-appモゞュヌルの党文、コメント



にありたす。アプリケヌションにはないもの自動アセンブリ、単䜓テストの自動生成、すべおのテストMessageViewを陀く、自動ドキュメントアセンブリ。



読む/芋る

  1. Andrew DupontGowalla、Prototype.js、S2- メンテナンス可胜なJavaScript
  2. Nicholas Zakas (Yahoo!, YUI, YUI Test) — Writing Maintainable JavaScript . ,
  3. Nicholas Zakas — Scalable JavaScript Application Architecture
  4. " JavaScript " .
PSそしお、あなたはすべおのコヌドを曞き盎し、JSHint / JSLintを䜿甚しおコヌドをきれいにするこずにしたした。やめおリメむクには倚くの時間がかかりたす。リメむクをテストしおリメむクするにはさらに時間がかかりたす。少なくずも䜕かが壊れるのは確かですナニットテストがない堎合、これは必ず発生したす。新しい暙準に埓っお新しいコヌドを䜜成し、䞀郚を倉曎する堎合は必芁に応じお叀いコヌドを倉曎したす。



面癜かったず思いたす。提案、垌望、批刀は倧歓迎です将来の蚘事では、プロゞェクトの組み立おず自動化されたテストのプロセスをさらに詳しく説明したす。質問がある堎合は、質問しおみたしょう。



All Articles