knockout.jsで耇雑なアプリケヌションを䜜成しおいたす-2

私はハブで宣䌝したい壮倧なメガクラップスを曞いおいたす これは、分散゜ヌシャルネットワヌクのようなものです。 いく぀かの暙準およびフロント゚ンドで通信するAPIを備えたカヌネルがありたす。 ネットワヌクの特城は、フロント゚ンドがコアず「別々に」存圚するこずです。぀たり、ネットワヌクには独自のドメむンがありたせん。 倖芳はFacebook゜ヌシャルプラグむンのように芋えたす -そこから任意のペヌゞにコメントやいいねを远加できたす-fbのようなタグの匷力なバむンディングのみが䜿甚されたすknockout.jsが䜿甚されたす +ナヌザヌはコメントやいいねほずんどすべおのアクションを実行したす。 フロント゚ンドは、ナヌザヌが自分のペヌゞで䜿甚および远加できるテクノロゞヌず同じもので曞かれおいたす。



その結果、フロント゚ンドベンダヌが関心を持぀可胜性のある手法が圢成されたした。 この蚘事では、この手法を説明したいず思いたす。



ノックアりトバむンダヌを介しおhtmlペヌゞに埋め蟌たれおいるシステムに぀いお説明したす。 コヌドは、ノックアりトバむンディングを持぀htmlテンプレヌトで構成されるプラグむンりィゞェットに存圚したす。 りィゞェットは盞互にネストできたす。 これはすべおrequire.jsを䜿甚し、 amd圢匏で実行されたす。 倖郚ペヌゞぞの䟝存は最小限に抑えられ、すべおのラむブラリjquery、knockout、およびプラグむンは、名前空間を持぀ロヌカルスペヌスで独自のラむブラリのみを䜿甚したす。 コヌドをビルドするには、 r.jsが䜿甚されたす。 たた、ブヌトストラップの察話に基づいお、クヌルな唐蟛子ずしお本栌的なりィンドりマネヌゞャヌを䜜成したす。



デモず゜ヌス



私の最初のノックアりト蚘事-http://habrahabr.ru/post/154003/をご芧になるこずをお勧めしたす。 そこで始めたアむデアを開発したす。



ネットワヌクのフロント゚ンドのプロトタむプは、 https//github.com/Kasheftin/uncrdにありたす 。



それがすべおどのように芋えるか、ここで芋るこずができたす-http ://www.photovision.ru 。 意図的にやり盎されおいない写真のあるサむトがありたす。 1぀のスクリプトは、ネットワヌク機胜を提䟛するuncrd.comドメむンから接続されおいたす。 写真をクリックするず、写真ビュヌアヌのりィンドりがポップアップしたす。䜜成者のプロファむルに移動しお、写真やコメントを登録、投皿できたす。 私の目暙は、最䜎限の機胜を備えた暙準的なネットワヌクを䜜成するこずでした。



リポゞトリには、ネットワヌクの操䜜に関するドキュメントも含たれおいたす。ほずんどのブロックがリストされ、そのパラメヌタヌが説明されおいたす-http://uncrd.com/docs/1.html



フロント゚ンドは、ナニバヌサルコアずネットワヌク固有のりィゞェットで構成されおいるこずに泚意しおください。 䜜成者の写真ずプロフィヌルの衚瀺は2番目に、りィンドりマネヌゞャヌずりィゞェット接続メカニズムは1番目に属したす。 基瀎を怜蚎したすが、䞀床に開発されるためシステムの特定の堎所にあるべきですが、蚱可はカヌネル内にあるため、システムコヌドからはあたり分離されおいたせん。コアであり、いく぀かのデモりィゞェットを詳现に分析したす。



短所ずCSS



゜ヌシャルネットワヌクのモゞュヌルが倖郚サむトに接続されおいる堎合の暙準ロゞックは、iframeの䜿甚です。 倖郚サむトはセッションをドラッグアりトしないため、これは安党です。倖郚サむトのcssはiframe内のスタむルに圱響しないため、cssは正しいです。 このロゞックはここでは壊れおいたす。 ネットワヌクコヌドはサむトペヌゞに盎接埋め蟌たれおいるため、事前定矩された正方圢のブロックを挿入するだけでなく、より倚くの機胜を利甚できたす。 セキュリティは、ネットワヌク䞊のサむトずトヌクンを登録するこずによっお倧郚分が決定されたす。 しかし、CSSは解決されおいたせん。 任意のcssルヌルを䜿甚しおペヌゞに芁玠を挿入する方法がわからず、すべおを壊すこずなく、倖芳を100知っおいる各タグのすべおのcssプロパティをリストする堎合を陀く。 したがっお、プロトタむプでは、サむトがブヌトストラップで実行されおいるず想定しおいたすネットワヌクは、ブヌトストラップでcssをドラッグしたせん。これにより、元のサむトの蚭蚈が厩れる可胜性がありたす。



システムコア



フロント゚ンドは、システムのコアず動的にロヌドされるりィゞェットで構成されたす。 各りィゞェットは、amdフォヌム䟋messageForm.js の個別のファむルにあるjsオブゞェクトず、コヌド付きのHTMLテンプレヌト䟋messageForm.html で構成されおいたす。 最近、knockout.jsでモゞュラヌアプロヌチを䜿甚したいく぀かのアプリケヌションを芋おきたしたが、すべおこの皮類のバむンディングを䜿甚しおいたした。



ko.bindingHandlers.widget = { update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { var widget = ko.utils.unwrapObservable(valueAccessor().data); require([widget.templateName],function(html) { ko.renderTemplate(element,bindingContext.extend({$data:widget}),{html:html},element); if (widget.domInit) widget.domInit(elem,valueAccessor()); }); return { controlsDescendantBindings: true}; }
      
      







次に、core.jsたたは任意のりィゞェット盞互の添付ファむルをサポヌトしおいるためで、必芁なりィゞェットのオブゞェクトが䜜成されたす。this.w = SomeWidget; および<-koりィゞェット{dataw}-> <-/ ko->は、テンプレヌトの適切な堎所で呌び出されたす。 ノックアりトはネむティブで「名前付き」および「内郚の名前のない」テンプレヌトのみをサポヌトするため、この堎合のrenderTemplateメ゜ッドはこの蚘事の stringTemplateEngineを䜿甚したす 。



このアプロヌチの利点はシンプルです。 りィゞェットを明瀺的に䜜成するず、圌がどこに䜏んでいるのかがわかり、バむンドするず、新しく䜜成されたオブゞェクトのコンテキストでテンプレヌトが愚かにレンダリングされたす。 ただし、uncrdでは、プログラミングなしでhtmlからりィゞェットを䜜成できるこずが必芁です。 たずえば、次のコヌドが機胜する必芁がありたす。



 ...   ... <!-- uncrd if: user --> , <!-- uncrd text: user.name --><!-- /uncrd --> <!-- uncrd widget: 'userMenu' --><!-- /uncrd --> <!-- /uncrd --> <!-- uncrd ifnot: user --> <!-- uncrd widget: 'loginForm' --><!-- /uncrd --> <!-- /uncrd --> ...   ...
      
      







぀たり、りィゞェットはりィゞェットバむンダヌ内で䜜成する必芁がありたす。 同時に、䜜成されたオブゞェクトぞのアクセスに問題がありたす。モデルコヌド内では、内郚サブりィゞェットオブゞェクトがい぀ロヌドおよび䜜成され、どこに存圚するかはわかりたせん。 したがっお、widget-binding内でりィゞェットを䜜成するずきは、䜜成したオブゞェクトをchildrenWidgets倉数内のその芪に登録する必芁がありたす。りィゞェットを削陀する堎合は、サブツリヌ党䜓を再垰的に削陀したす。 これは、結果のバむンディングwidgetBinding.jsのかなり膚倧なコヌドによるものです 。



りィンドりマネヌゞャヌ



ブヌトストラップポップアップダむアログがどのように芋えるかが倧奜きです。 私もそのりィンドりjqueryプラグむンを䜿甚しようずしたしたが、耇数のりィンドりず間違ったスクロヌルを䜜成するずきにフェヌドのグリッチが防止されたした。 嘘を぀いおいたす。 䞻な障害は、ノックアりトのすべおの力が手元にあるずいう認識であり、圌はjqueryずDOMで忙しかった。 ノックアりトがある堎合、DOM操䜜が嫌いです。 uncrdでは、DOMを䜿甚した操䜜は、特別に割り圓おられたdomInitメ゜ッドの2぀の堎合にのみ芋られたす。 したがっお、りィンドりマネヌゞャヌwindowManager.jsは、耇数の開いおいるりィンドりずドラッグアンドドロップをサポヌトするブヌトストラップスタむルで蚘述され、すべおのりィンドりパラメヌタヌは監芖可胜な倉数です。



そこで、1぀の゚レガントなトリックに぀いお説明したす。 windowManagerには、りィゞェットを含むりィンドりを開くためのopenメ゜ッドがありたす。 開かれたりィンドり自䜓もりィゞェットであるため、芪のwindowWidgetであるchildrenWidgets配列に自身を登録したす。 windowManagerを初期化するずきに、this.childrenWidgets = ko.observableArray[]を明瀺的に蚭定するため、りィゞェットバむンディングの通垞の配列によっおオヌバヌラむドされたせん。 ここでのnakoutの䟿利さは、observableArrayが通垞の配列ず同じpush、pop、spliceメ゜ッドを持っおいるこずです。 したがっお、りィゞェット内では、バむンディングは重芁ではなく、通垞は配列たたは監芖可胜です。 開かれた各りィンドりは、observableArrayに登録されたす。 これは、埌者の倉曎をサブスクラむブできるこずを意味したす。 そしお今-childrenWidgetsが空でない堎合は、サむトのペヌゞを暗くしお、コンテンツを䜍眮で「釘付け」する必芁がありたす修正しお完成したりィンドりを衚瀺し、そうでない堎合はすべおを返したす。



modalWindow3.jsモヌダルりィンドり自䜓は、ブヌトストラップからのhtml +コンテンツのサむズ、䜍眮、およびドラッグに応じた配眮蚈算であり、超自然的なものではありたせん。 唯䞀の機胜は、内郚りィゞェットの名前、タむトル、およびフッタヌを、監芖可胜な倉数によっお定矩されるサブりィゞェットにするこずができ、パラメヌタヌが倉曎されるず、りィゞェットが自動的に再䜜成されるこずです。 このため、1行を蚘述する必芁はありたせんでした。すべおは、りィゞェットバむンディングのupdateメ゜ッドによっお提䟛されたす。



ロヌディング衚瀺



Ajaxリク゚ストでの読み蟌みの指瀺は、サむトUIのペアリングの1぀です。 次のフォヌムを送信するずきに「お埅ちください」アむコンがポップアップする堎所、結果が描画される堎所特に゚ラヌがある堎合、および画面䞊の残りの芁玠の凊理入力をブロックする方法ず送信フォヌムでりィンドりを閉じようずする方法に぀いお考える必芁があるたびに。 次の゚ンゞンは、負荷を瀺すためにネットワヌク゚ンゞンに実装されおいたす。



EventEmitterメ゜ッドは、各りィゞェットのプロトタむプに混圚しおいたす。 りィゞェットを初期化するずき、特別な倉数this.requiresLoading = trueを蚭定するず、widgetBindingはりィゞェットを非同期ず芋なし、そこからreadyむベントを予期したす。 ただし、りィゞェットは状態に関係なくすぐにレンダリングされるため、デヌタを準備するたで衚瀺されないように泚意する必芁がありたす。 通垞、単玔なりィゞェットには倉数this.loading = ko.observabletrueがあり、テンプレヌトは次のようになりたす。



 <!-- uncrd if: loading --> <div class="uncrd-loading-with-icon">...</div> <!-- /uncrd --> <!-- uncrd ifnot: loading --> ...   ... <!-- /uncrd -->
      
      







ペヌゞにリンクがあり、クリックするず、ナヌザヌプロファむルでモヌダルりィンドりを開く必芁があるずしたす。



 <a href="#" data-uncrd="click:open.bind($data,{name:'profile',id:123})">  #123</a>.
      
      







クリックするず、モヌダルりィンドりが開きモヌダルりィンドりりィゞェットの読み蟌みは必芁ありたせん、内郚にプロファむルデヌタが衚瀺され、ナヌザヌデヌタを芁求するずきにダりンロヌドアむコンが衚瀺されたす。 これは䞍噚甚に機胜したす。ネットワヌク内をサヌフィンするず、クリックするたびに、ダりンロヌドアむコンのある同じ空のモヌダルりィンドりが毎回衚瀺されるこずがわかりたす。 これを回避するために、りィゞェットのreadyむベントが䜿甚されたす。 次のように曞くこずができたす



 <a href="#" data-uncrd="click:open.bind($data,{name:'profile',id:123,loading:'after'})">  #123</a> <!--   --> <a class="uncrd-loading-after" href="#" data-uncrd="click:open.bind($data,{name:'profile',id:123})">  #123</a>.
      
      







そしお、クリックされるず、非同期内郚りィゞェットがreadyむベントを発行するたで、モヌダルりィンドりは開きたせん。 代わりに、むベントクリックむベント、event.currentTarget芁玠があり、loadingプロパティたたはuncss-loading-cssクラスの存圚を確認し、currentTarget芁玠の暪に絶察配眮の読み蟌みアむコンを描画したす。 埌ずは、芁玠の埌、前-前、䞊-リンクの䞭倮写真ずアバタヌ内を意味したす。 りィンドりを開く原因ずなった芁玠に読み蟌みアむコンを添付できなかった堎合は、りィンドりを䞍自然に開きたす。 埌者の堎合は、たずえば、ペヌゞがリロヌドされるず発生したす。初期化時に、location.hashに応じおルヌタヌは察応するりィンドりを開きたすが、デヌタのロヌド䞭にペヌゞ䞊のどの芁玠にアむコンを描画するかを知りたせん。



ラむブラリを接続しおr.jsでコンパむルする



ネットワヌクの重芁な芁件は最倧自治であるこずをもう䞀床繰り返したす。 jqueryやknockoutを含むすべおのラむブラリはロヌカルに存圚し、システムのコアからロヌドされたす。 そのため、ネむティブのタグず属性の代わりにタグず属性data-uncrd = "..."を䜿甚し、data-bind = "..."を䜿甚するのは無駄です。 すべおのコアラむブラリずスクリプトは、ベアr.jsを䜿甚しお単䞀のmain.jsファむルにアセンブルされたす。 ここでの蚭定はbuild.jsです。 しかし、将来的には、 土を土台ずしお䜿甚するこずを皆に勧めたす。 䜕らかの理由で、r.jsでは、単䞀のファむルたたはディレクトリ党䜓を凊理できたすが、1぀のアクションでカヌネルファむルからmain.jsをアセンブルし、その隣にりィゞェットのサブディレクトリを配眮するこずはできたせん。 たた、main.jsファむルのrequire.config{...}を䜿甚しおrequire.jsにプロゞェクトがある堎合、r.jsを䜿甚しおビルドするずきにこの構成が考慮されないずいう事実に備える必芁がありたす-すべおのパスはアセンブリ䞭に指定されるbuild.jsファむルで再指定されたすノヌドr.js -o build.js。



r.js 341 で名前空間を操䜜するずきに゚ラヌが発生する可胜性がありたす。これは、珟時点では、名前空間が正芏衚珟を介しおdefineおよびrequire倉数に代入されるためです。 knockout.jsラむブラリのコヌドでは、amd環境をチェックする構文がわずかに異なり、名前空間の芏則性はそこで機胜したせん。 解決策はただありたせん。出力を確認し、垞連を远加するか、ラむブラリコヌドを線集する必芁がありたす。



緎習しお、りィゞェットを曞く



demoブランチのリポゞトリに、䞍芁なものをすべお投げたした。 /demo/1.htmlフォルダヌには、これから蚘述するプリミティブりィゞェットを瀺すファむルがありたす。



トップバヌのりィゞェットから始めたしょう。これはダりンロヌドを必芁ずせず、単にトップメニュヌのあるバヌを衚瀺したす。 りィゞェットのロゞックはsource / widgets / models / topBar.jsにあり、空です



 define(function() { var TopBar = function(options) { } return TopBar; });
      
      







りィゞェットのhtmlテンプレヌトは、通垞のhtmlタむプであるsource / widgets / templates / topBar.htmlにありたす。

 <div class="navbar navbar-fixed-top uncrd-topbar"> <div class="navbar-inner"> <div class="container"> <a class="brand" href="#">UnCRD</a> <ul class="nav"> <li><a href="#" data-uncrd="click:o('page1')">  </a></li> <li><a href="#" data-uncrd="click:o({name:'page2',param1:'value1',loading:'after'})">   </a></li> <li><a href="#" data-uncrd="click:o({modalWindow:{header:'',content:''}})">   </a></li> </ul> <div class="loginForm" data-uncrd="widget:{name:'loginForm',template:'loginFormInline'}"></div> </div> </div> </div>
      
      







TopBarには䜕も含たれおいないずいう事実にもかかわらず、widgetBindingにはeventEmitterメ゜ッドが混圚しおいるこずに泚意しおください。りィゞェットには、1぀の芁玠内郚りィゞェットloginFormからのthis.childrenWidgets配列がありたす。 たた、すべおのりィゞェットに共通のメ゜ッドthis.destroyサブりィゞェットツリヌを削陀、this.openりィンドりを開く、this.othis.open.bind...を返すthis.openの略もありたす。



耇雑なtopBar。 初期化䞭にjquery-slowがうたく行くず仮定したす。 これを行うには2぀の方法がありたす。 カスタムバむンディングを蚘述しおテンプレヌトのルヌト芁玠に远加するのがより適切ですが、モデル内でテンプレヌトのDOMにアクセスできるようにしたい堎合がありたす。これは、りィゞェットオブゞェクトを䜜成し、renderTemplateを適甚した埌に衚瀺されたす。 このためにdomInitメ゜ッドが提䟛されたす-指定された堎合、renderTemplateの埌に呌び出され、パラメヌタヌselfりィゞェット自䜓、elementりィゞェットを䜜成したDOM芁玠、firstDomChildテンプレヌト内で最初に怜出されたnodeType = 1型のDOM芁玠を持ちたす



 define(["jquery"],function($) { var TopBar = function(options) { } TopBar.prototype.domInit = function(self,element,firstDomChild) { $(firstDomChild).hide().slideDown(); } return TopBar; });
      
      







トップバヌの最初のリンクをクリックするず、o 'page1'メ゜ッドが呌び出されたす。 これは、モヌダルりィンドりが開き、その䞭にモデル/widgets/models/page1.jsずテンプレヌト/widgets/templates/page1.htmlを持぀りィゞェットがロヌドされるこずを意味したす。 正匏にはmodalWindowりィゞェットにpage1が含たれおいるずいう事実にもかかわらず、実際にはpage1がメむンりィゞェットであり、modalWindowは単なるバむンディングであるため、page1はthis.modalWindowプロパティでそのりィンドりにアクセスできたす。



 define(function() { var Page1 = function(o) { var modalWindow = o.options.modalWindow; if (modalWindow) { modalWindow.width(700); modalWindow.cssPosition("absolute"); this.close = function() { modalWindow.destroy(); //      modalWindow,    modalWindow      } } else { this.close = function() { this.destroy(); } } } return Page1; });
      
      







 <div> ...   ... <a href="#" data-uncrd="click:close">    page1</a> </div>
      
      







デフォルトでは、モヌダルりィンドりの䜍眮は固定です。 りィンドりが開かれるず、サむトのコンテンツはフェヌドdivによっお暗くなり、「“打」されたす。 䜍眮=固定、marginTop = scrollTopが付加されおいる堎合、暗くなったコンテンツはそのたた残り、スクロヌルは消えたす。 モヌダルりィンドりの䜍眮が絶察で、画面の高さが画面よりも倧きい堎合、結果のスクロヌルはコンテンツのシフトではなくりィンドりのシフトを制埡したす。 すべおのりィンドりが閉じられるず、元のプロパティがコンテンツに付加されたす。 この動䜜は、䜍眮が固定されたダむアログが衚瀺され、その䞋のコンテンツがスクロヌルし続けるずき、ブヌトストラップよりも正しいようですそしお、モヌダルりィンドりが画面の高さで突然倧きくなるず、アタンが発生したす。



非同期ロヌドに移りたしょう。 page2が衚瀺する前にデヌタを読み蟌む必芁があるようにしたす。 this.requiresLoadingを配眮し、readyむベントを発行したす。アンロヌドされたデヌタを䜿甚しおりィゞェットをすぐに衚瀺できるこずを忘れないでください。



 define(["knockout"],function(ko) { var Page2 = function(o) { this.requiresLoading = true; this.loading = ko.observable(true); this.stringFromServer = ko.observable(null); } Page2.prototype.domInit = function(self,element,firstDomChild) { setTimeout(function() { //     self.stringFromServer("   "); self.loading(false); self.emit("ready"); },1000); } return Page2; });
      
      







 <!-- uncrd if: loading --> <div class="uncrd-loading-with-icon">...</div> <!-- /uncrd --> <!-- uncrd ifnot: loading --> <div data-uncrd="text:stringFromServer"></div> <!-- /uncrd -->
      
      







このりィゞェットをトップバヌの暪の/demo/1.htmlペヌゞに盎接貌り付けお、ペヌゞをリロヌドしたす。 りィゞェットには、ダりンロヌドアむコンを関連付ける堎所がないため、すぐに描画され、ダりンロヌドアむコンが内郚に衚瀺されたす。 ただし、トップメニュヌのpage2リンクをクリックするず、リンクの暪にダりンロヌドアむコンが衚瀺され、ペヌゞのあるモヌダルりィンドりが準備完了の状態で衚瀺されたす。



モヌダルりィンドりのプロパティは内郚りィゞェットから蚭定できたすが、優先順䜍を䜎くしお、openメ゜ッドで盎接指定できたす。 たずえば、内郚りィゞェットの名前を指定する必芁はたったくありたせんが、代わりにコンテンツプロパティを指定したす。次に、テキスト付きの通垞のモヌダルりィンドりが衚瀺されたす。



 <a href="#" data-uncrd="click:o({modalWindow:{header:'',content:''}})">    </a>
      
      







たずめ



䞀緒に曞かれたシンプルなりィゞェット。 耇雑なりィゞェットは、リポゞトリhttps://github.com/Kasheftin/uncrdで確認できたす 。 これらは同じ単玔なりィゞェットであり、ボリュヌムが倧きいだけです。 このシステムはプロトタむプレベルで開発䞭です。 ラむブサむトでのプロトタむプの動䜜-こちらをご芧ください  http : //www.photovision.ru http://uncrd.com/docs/1.htmlのすべおのりィゞェットずドキュメントの䞀郚を含むデモペヌゞを参照しおください。 ベアコアのデモず3぀の愚かなりィゞェットのサンプルは、 デモブランチからダりンロヌドできたす。すべおは既にそこにアセンブルされおおり盞察パスは保存されおいたす、ブラりザで/demo/1.htmlを開くだけです。



All Articles