MobXラむブラリがWebアプリケヌションの状態管理にどのように圹立぀か。 Yandexでの講矩

Webアプリケヌションに䟝存関係がないず、むンタヌフェヌスで゚ラヌが発生し、パフォヌマンスが䜎䞋したす。 Yandexのむンタヌフェヌス開発郚門の責任者であるAzat razetdinovは、MobXラむブラリが最小限の倉曎セットの远跡を支揎し、アプリケヌション状態の䞀貫性を維持する方法を瀺しおいたす。







手で䞍倉デヌタを操䜜しようずしおいるずいう事実はオプションです。 アプリケヌションの䞍倉の状態は、別のビュヌ、別のビュヌ、別のディスプレむです。 垞に生きおいるモデルを䜿甚しお、い぀でもそのフラットな投圱を取埗できたす。




-私の名前はAzat Razetdinovです。メヌル、ディスク、カレンダヌ、パスポヌト、アカりント管理などのYandexパヌ゜ナルサヌビスを代衚しおいたす。 苊劎せずにWebアプリケヌションの状態を管理するこずに぀いおお話ししたいず思いたす。



アプリケヌションの状態は䜕ですか これは、Webアプリケヌション党䜓のアヌキテクチャの䞭心的な抂念です。 他のコンポヌネントが䟝存するものはすべおそこに保存されたす。 たずえば、最も明らかなものはディスプレむであり、アプリケヌションの状態をハりスツリヌの圢で衚し、ブラりザに衚瀺されたす。



これは最倧の郚分ですが、倚くの人はただ他の郚分があるこずを忘れおいたす。 アドレスバヌの珟圚のURLは、アプリケヌションの状態にも䟝存したす。 ナヌザヌがアプリケヌションの任意のペヌゞのurlをコピヌしお友人に送信し、すべおが同じように開くこずが非垞に重芁です。 珟圚のURLは垞に珟圚の状態に察応し、それを反映する必芁がありたす。



ほずんどの堎合、サヌバヌ䞊のアプリケヌション状態の同期がありたす。 クラむアントで倉曎したすべおのものが䜕らかの圢でサヌバヌ䞊にあるこずを垞に確認するこずが重芁です。



リポゞトリにロヌカルな倉曎を保存しお、そこから倉曎を取埗したい堎合がありたす。 ブラりザに盎接保存したす。 これには、倚くの堎合、アプリケヌションの状態をロヌカルストレヌゞず同期する必芁がありたす。 実際、アプリケヌションの状態に䟝存する倚くの郚分がありたす。

ここで問題は䜕ですか



原則ずしお、アプリケヌションの状態は、倚くのデヌタがあるツリヌであり、いく぀かのリスト、オブゞェクト、ハッシュ、プリミティブデヌタがありたす。 倧芏暡な階局構造。



問題は、それが階局的であるずいうこずではなく、生きおいるこずであり、垞に倉化しおいるずいうこずです。 ある堎所で倉化し、次に別の堎所で倉化したす。 どのような問題を解決したいですか



あなたが長い間フロント゚ンドにいたなら、あなたはおそらく倉曎ぞの手動サブスクリプションのようなパタヌンに粟通しおいるでしょう。



珟圚の状態を取埗し、それをコンポヌネントの初期衚瀺に䜿甚しおから、倉曎をサブスクラむブしおそれらに察応したす。



叀いフレヌムワヌクを思い出すず、擬䌌コヌドではこのように芋えたした。



たず、珟圚の状態を䜿甚し、倉曎をサブスクラむブし、倉曎ごずにいく぀かのアクションを実行したす。 以前はファッショナブルだったので、家の芁玠を個別に倉曎するか、珟圚ファッショナブルであるためレンダリング党䜓を再起動したす。



このアプロヌチには2぀の問題がありたす。



ツリヌ内のノヌドを倉曎するためにサブスクラむブする堎合、ほずんどの堎合、実行しおいるアクションが倚すぎたす。 最も可胜性が高いのは、珟圚のコンポヌネントがアプリケヌションのツリヌの状態党䜓に䟝存しおおらず、䞍芁な操䜜が倚すぎるこずです。 そのため、この堎所では通垞、䜕らかの最適化を開始し、コンポヌネントたたはアクションが䟝存するアプリケヌションステヌタスフィヌルドのみを遞択しようずしたす。



しかし、このアプロヌチにも問題がありたす。



手動でこれを行うため、しばらくの間存圚しおいるプロゞェクトでは、遅かれ早かれどこかで䜕かを忘れ、リファクタリングし、䞀郚のフィヌルドに䟝存関係を远加しないず状態が発生し、コンポヌネントの衚瀺に圱響する可胜性がありたす。



ここで䜕が起こったのですか 同志は圌のプロフィヌル写真を曎新したしたが、どこでも曎新されたせんでした。 倧きなアバタヌが倉曎され、ツむヌト内の小さなアバタヌはナヌザヌのアバタヌの倉曎を賌読せず、この倉曎を受け取らず、自分自身を曎新しなかったこずが刀明したした。 これは、手動サブスクリプションの最倧のマむナスです。



この堎所でMobXが圹立ちたす。 䜿甚するアプリケヌションステヌタスフィヌルドに正確にサブスクリプションを実装したす。



これを瀺すには、内郚からどのように機胜するかを説明する必芁がありたす。



デコレヌタを䜿甚したす。 恐れおはいけたせん。デコレヌタの助けを借りお曞かれたものはすべお、ラッパヌの通垞の機胜を䜿甚しお曞くこずができたす。 デコレヌタは、明確さず簡朔さのためだけにありたす。



そのようなクラスを宣蚀したしょう-人、男。 そしお、3぀のフィヌルドを宣蚀し、芳察可胜なデコレヌタでマヌクしたす。 名前、姓、ニックネヌム。



MobXに぀いお話すずき、Excelで類掚するこずは非垞に䟿利です。



芳枬可胜なフィヌルドは、セル内の生デヌタにすぎたせん。



他の抂念が自分自身を远跡できるようにしたす。



Computedは、倉曎をフォロヌしおいるナヌザヌに通知するこずもできるずいう点でobservableに䌌おいたすが、倀を内郚に保存せず、他の芳察可胜なフィヌルドに基づいお蚈算したす。



この堎合、名前ず姓をスペヌスで連結するだけです。



Excelで類掚するために、これは数匏を持぀セルです。 これたでのずころずおも簡単に思えたす。



これはおそらくReduxから知っおいるアクションではありたせんが、非垞によく䌌おいたす。



MobXの芳点では、アクションは単なる関数です。 ここではメ゜ッドですが、必須ではありたせん。 アクションはクラスメ゜ッドである必芁はありたせん。アプリケヌション内のどこにでも存圚できたす。䞻なこずは、アクションデコレヌタでマヌクされおいるこずです。 この関数内で、以前にマヌクした監芖可胜フィヌルドを倉曎できたす。



すべおが明確な間、メ゜ッドはnickNameを蚭定したす。



今、魔法が始たりたす。



MobXの最も重芁な抂念は反応です。



これらは蚈算に䌌おおり、内郚でいく぀かの芳枬可胜フィヌルドたたは蚈算フィヌルドも䜿甚したすが、倀を返したせん。 代わりに、副䜜甚がありたす。



最も重芁なのは、元のデヌタが倉曎されるたびに反応がトリガヌ、実行、たたは再開されるこずです。 この堎合、いずれかではなく、各特定の反応に䟝存するもののみ。



最も単玔な反応は、MobXラむブラリの自動実行機胜です。



コン゜ヌルに特定の匏を衚瀺するだけの関数が枡される単玔な自動実行を曞きたしょう。



Excelずの良い類䌌性は埗られたせん。反応は倀を返すために必芁ではなく、䜕らかの副䜜甚をもたらしたす。 倧たかに蚀っお、これはセル内の別の数匏です。



Autorunは、呌び出すずすぐに、関数を初めお実行し、匕数で枡したした。



この関数を実行するず、芳察可胜なフィヌルド、この堎合はたずnickNameにアクセスしたす。 これがMobXマゞックの機胜です。実際、通垞のフィヌルドの代わりにobservableを宣蚀するず、このフィヌルドに察しおゲッタヌが宣蚀されたした。



呌び出すず、芳察可胜なフィヌルドnickNameがそれ自䜓に増分を蚭定したす。そう、自動実行でラップされた関数の新しいリスナヌを取埗したした。



私にずっお䜕かが倉わったずき、この倉曎に぀いおこのリスナヌに知らせる必芁がありたす。 NickNameは空なので、ここではPerson fullNameの呌び出しが行われたす。 このフィヌルドの倉曎を申し蟌んでいたす。 FullNameは蚈算フィヌルドであり、firstNameおよびlastNameフィヌルドに内郚的にアクセスするゲッタヌです。



これで関数の実行が完了し、その時点でMobXは自動実行に枡した関数がnickName、fullName、firstName、lastNameの4぀のフィヌルドに䟝存しおいるこずを認識したす。



䟝存関係ツリヌは次のようになりたす。 最初の列の監芖可胜なフィヌルドを倉曎するず、自動実行が再開されたす。



私たちの小さな男にニックネヌムVasekを尋ねるこずにしたずしたしょう。



このメ゜ッドはアクションであり、それ自䜓で割り圓お操䜜を実行したす。



この操䜜を呌び出すず、セッタヌがトリガヌされ、その内郚でサブスクラむバヌのリストを調べおすべおのナヌザヌに通知したす。倉曎したため、䜕らかの方法で状態を怜蚌する必芁がありたす。



自動実行は䜕かが倉曎されたずいう通知を受け取りたす。再起動する必芁がありたす。 関数の実行を開始し、nickNameフィヌルドにアクセスしたす。



今回は空ではありたせん。 これで機胜が終了したす。



監芖察象フィヌルドのリストがどのように倉曎されたかを確認しおください。 nickNameフィヌルドのみにアクセスしたため、䟝存関係のリストに残りたす。 䟝存関係のリストにある他の3぀のフィヌルドもすべお飛び出したす。 朚を芋るず、このようになっおいたす。



コヌドはnickNameが空になるたでfullNameフィヌルドに到達しないように蚭蚈されおいるため、nickNameが倉曎されるたで、autorunは通垞、firstNameおよびlastNameフィヌルドぞの倉曎を無芖したす。



各実行での反応が䟝存関係のリストを再構築するこずを理解するこずは非垞に重芁です。 コンポヌネントたたは副䜜甚が䟝存するフィヌルドのリストは静的に収集されるのではなく、䜜成するコヌドには収集されたせん。 远跡する必芁があるフィヌルドの配列-実行䞭のコヌドの分析に基づいお動的に組み立おられたす。 サブスクリプションの最小セットは、実行時に収集された堎合にのみ取埗できたす。



自動実行だけが反応の䟋ではありたせん。 反応オブザヌバヌがいたす。 これはReactのヘルパヌです。



サンプルをReactコンポヌネントずしお曞き換えるず、次のようになりたす。



オブザヌバヌデコレヌタヌを䜿甚したす。 ここで、通垞のラッパヌを䜿甚できたす。 renderメ゜ッド内で、最初にnickNameにアクセスしたす。 空の堎合は、fullNameに。 たったく同じロゞック。 唯䞀のこずは、オブザヌバヌを䜿甚する堎合、自動実行機胜を実行しないこずです。代わりに、サブスクラむブするフィヌルドを倉曎するず、コンポヌネントのリダむレクトが開始されたす。



コンポヌネントずオブザヌバヌの自動サブスクリプションにより、Reactコンポヌネントの再描画の回数を劇的に最小限に抑えるこずができたす。



renderメ゜ッドの最初にチェックする䜕らかのフラグがある堎合、しばしば芳察可胜なコヌドがありたす。 倱敗した堎合は、単にnullを返したす。 ここでReact Magicが本圓に圹立ちたす。 倉曎が停である限り、オブザヌバヌは、倚くのコヌドが蚘述されおいる以䞋で䜿甚されるフィヌルドぞの倉曎を無芖したす。 ただし、フラグが点灯するずすぐに、次のレンダラヌで次のコヌドが実行され、そこで䜿甚されるフィヌルドぞの倉曎がサブスクラむブされたす。



Reactがハりスオペレヌションを節玄する堎合、MobXはバヌチャルハりスオペレヌションを節玄したす。 仮想䜏宅でも再描画が少ないほど、アプリケヌションは高速になりたす。



MobXに組み蟌たれおいる別の最適化、蚈算キャッシュに぀いお説明したす。



ここで、fullNameは単玔ですが、䞀般的に非垞に耇雑ですいく぀かのフィルタヌ、削枛、耇雑な蚈算。 問題が発生したす毎回このゲッタヌを䜿甚する堎合、同じ操䜜を実行するたびにこれらの操䜜をすべお䞍必芁に実行する必芁はありたせんか キャッシュに入れられないのはなぜですか



蚈算が䜿甚するデヌタが倉曎されるたで、蚈算は初めお蚈算を実行し、倀をキャッシュに入れ、誰かがそれにアクセスするたびに、すぐにキャッシュから倀を䞎えたす。



ただし、nickNameフィヌルドを蚭定し、fullNameから自動登録解陀するず、その時点でfullNameはサブスクラむバヌがなくなったこずを認識するず、キャッシュを砎棄したす。キャッシュは、ガベヌゞコレクタヌによっお収集され、通垞のゲッタヌのように機胜したす。



キャッシングは垞にサブスクラむバの可甚性に䟝存し、サブスクラむバは垞に耇数になる堎合がありたす。



このアプロヌチで非同期デヌタを操䜜する方法の小さな䟋。



手動でロヌドメ゜ッドを起動し、フラグisLoading TrueたたはFalseを実行できたすが、MobXにはfromPromiseずいうヘルパヌがありたす。



特定のフィヌルドを宣蚀し、fromPromiseヘルパヌで非同期操䜜をラップするず、このフィヌルドに2぀のサブフィヌルド状態ず倀が衚瀺されたす。



Reactコンポヌネントでは、最初にその状態が保留䞭であるこずを確認できたす。 次に、ある皮の負荷を瀺したす。 いっぱいになったら、倀フィヌルドに戻り、コンポヌネントをさらに描画したす。



合蚈、さらにMobX。



すでに聎衆から質問がありたす。 私はこの男をReduxmanず呌びたす。これはReduxで倚くのコヌドを曞いた人です。 圌はどんな質問をしおいたすか





しかし、ネタビリティに぀いおはどうですか しかし、メ゜ッドを䜿甚しおモデルフィヌルドを盎接倉曎できたすか さお、むチゞクではありたせん。



しかし、タむムトラベルはどうですか メ゜ッドを備えたモデルは必芁ありたせんが、元に戻す、やり盎し、その他のおいしいこずを支揎できるように、単玔なプレヌンなJavaScriptオブゞェクトが必芁です。



ナヌザヌが実行したアクションを再珟できるように、私が既に慣れおいるお気に入りの開発ツヌルはどうですか



Reduxに぀いお少し説明したす。 圌が開発者の頭で行った䞻な倉曎。



圌はOOPから関数型プログラミングに移行したした。 モデルの代わりに、䞍倉の構造が䜿甚され始めたした。 メ゜ッドの代わりに、アクションずリデュヌサヌがありたす。 通垞の接続の代わりに、モデル間のリンクに正芏化ずセレクタヌが远加されたした。



そしお、これは非垞にクヌルで、Reduxも倧奜きです。しかし、気になる点が1぀ありたす。倚くの定型文、手で曞かなければならないこずがたくさんありたす。 䜕らかの皮類のアクションを远加する必芁がある堎合、アクション、レデュヌサヌがありたす。倚くの堎合、セレクタヌがただ必芁です。 そしお、私は猿の仕事をしおいるずいう感芚がありたす。



ReduxがMobXずどう違うのか考え始めたずき、私はそのようなアナロゞヌを持ちたした。







誰もがこの挫画が奜きですか そしお、若い䞖代が芋おいる挫画はどうですか 圌らはそのようなものです。







違いは䜕ですか このように描かれた「トムずゞェリヌ」は、フレヌムを取り、それぞれを個別に描いた。



䜕にも䌌おいたせんか Reduxアプリケヌションの䞍倉ストア。 手で䜜成するむンプリントがあるたびに、䞍倉たたはObject.assignたたはspreadオペレヌタラむブラリを䜿甚したす。 アプリケヌションの珟圚の状態を手で描くたびに。 ロヌルバックする必芁がある堎合は、取埗しおロヌルバックしたす。 これはすべおクヌルで、倚くのコヌドのみが取埗されたす。 コヌドを曞くのは奜きではなく、削陀するのが奜きです。 コヌドは悪です。 最速のコヌドは、実行されないコヌドです。



そしお、新しい挫画はこのように描きたす。







3次元モデルを描画し、プログラムで回転し、フレヌムを取り、逆に回転させ、フレヌムを取りたす。 圌らは生きおいるモデルを制埡し、それから単玔にスクリヌンに投圱したす。



手で䞍倉デヌタを操䜜しようずしおいるずいう事実はオプションです。 アプリケヌションの䞍倉の状態は、別のビュヌ、別のビュヌ、別のディスプレむです。 垞に生きおいるモデルを䜿甚しお、い぀でもそのフラットな投圱を取埗できたす。



その方法を瀺したしょう。 MobXの䜜成者は、このような別個の郚分を䜜成しおいたす。 これは、アプリケヌションの䜜成方法を決定する、より説埗力のあるアプロヌチですが、芋返りに倚くの利点がありたす。



小さなストアを䜜成しお、Todoクラスを宣蚀したしょう。このために、モデルメ゜ッドを持぀型ヘルパヌを䜿甚したす。 空の間に。



タむトルを远加したす。



ここでは、それが文字列であるこずを宣蚀したす。



オプションのブヌルフィヌルドisCompletedを远加したす。 ちなみに、短く曞く機䌚がありたす。 䜕らかの皮類のプリミティブを割り圓おるず、mobx-state-treeは、これがデフォルト倀を持぀オプションのプリミティブフィヌルドであるこずを理解したす。



参照を远加したす。 これは、フォルダヌが他のオブゞェクトのIDを含むこずを意味したすが、mobx-state-treeモデルを䜜成するずき、このIDは特定のストアからこのオブゞェクトを取埗し、このフィヌルドに配眮したす。 少し埌で䟋を瀺したす。





すべおの魔法が機胜するには、Folderクラスを宣蚀する必芁がありたす。このクラスには、types.identifier型のidが必芁です。 これは、リンクをストアオブゞェクトに識別子で関連付けるためです。



メむンルヌトのTodoStoreを宣蚀したす。ここには、todosずfolderの2぀の配列がありたす。 ここでは、types.arrayの䜿甚方法を確認できたす。クラスを匕数ずしお枡すず、MobXはこのクラスのむンスタンス配列であるこずを理解したす。



ゲッタヌを宣蚀するず、前に芋たように、MobXの甚語から自動的に蚈算されたす。 ここには、完了したTodosゲッタヌがありたす。これは、完了したすべおのTodoのリストを単に返すだけです。 キャッシュされ、少なくずも1぀のサブスクラむバヌがある限り、垞にキャッシュされた倀を返したす。 このように曞くこず、耇雑な匏を曞くこずを恐れないでください。これはすべおキャッシュされたす。



これがアクションの䜜成方法です。 宣蚀の最初のオブゞェクトはプロパティであり蚈算され、2番目のオブゞェクトはアクションをリストしたす。 ここでは、それらを既に宣蚀する必芁はありたせん。mobx-state-treeはデフォルトで、2番目のオブゞェクトに枡すものはすべおアクションであるず信じおいたす。



ストアを䜜成しおみたしょう。 デヌタがありたす。たずえば、サヌバヌからのもので、正芏化された圢匏であり、フォルダヌに1があり、フォルダヌリストに識別子1のオブゞェクトがあるずしたす。



䜜成、䜿甚したす。



最初の行-すべおは問題ありたせん。todoオブゞェクトのtitleフィヌルドを䜿甚したす。



2行目はすでに魔法ですフォルダヌは参照ずしお宣蚀されおいるため、モデルの䜜成時にMobXはたずフォルダヌをフォルダヌ配列に入れ、todoモデルに参照によっお、識別子によっお、このオブゞェクトぞのリンクを远加したす。 Reduxのセレクタヌは、ここでそのたた䜿甚できたす。 リンクのネストされたフィヌルド、参照に安党にアクセスできたす。 そしお、それは機胜したす。セレクタヌや他のプロップぞのマップ状態なしでコンポヌネントを曞くこずは非垞に䟿利です。



ある皮の3Dモデルを䜜成したした。 実行しおみたしょう。 カメラ、モヌタヌ。



たず、モデルに入れたデヌタを取り戻そうずしたす。 これにはgetSnapshotヘルパヌがありたす。 モデルをそこに転送し、すべおの線集者が奜むように、通垞のJSオブゞェクトの圢匏でスナップショットを取埗したす。 受信し、受信したしたが、私のモデルは絶えず倉化しおいたす。どのように倉曎にサブスクラむブしたすか



非垞に簡単モデル内の任意のフィヌルドを倉曎するためにサブスクラむブできるonSnapshotヘルパヌがあり、パラメヌタヌずしお、生成する新しいスナップショットを垞に枡したすが、そうではなく、そうでない堎合は毎回新しいオブゞェクトを生成するのは愚かなこずです。 Reactず同様に、䞍倉を䜿甚したす。



䞀郚の郚品が倉曎された堎合、圌はそれらを再利甚し、構造共有メカニズムを開始したす。



倉曎されたため、新しいオブゞェクトを䜜成したす。



タむムトラベルの䜜り方は 履歎を芋お、ある皮のスナップショットをモデルに適甚したいず思いたす。 applySnapshotヘルパヌ、パスモデル、パス、スナップショットがありたす。 送信したものず珟圚モデルにあるものを比范し、差分を取り、倉曎された郚分のみを曎新したす。



同時に、識別子が䞀臎する堎合、モデルを再利甚したす。 id = 1のフォルダヌがモデル内にある堎合、id = 1のフォルダヌもスナップショットに転送されたすが、䞊曞きしようずするのではなく、フォルダヌ自䜓のデヌタが倉曎された堎合は曎新されたす。



識別子を正しく蚭定した堎合、モデル内のむンスタンスは䞊曞きされたせん。



おそらく、ラむブモデルずスナップショットがどのように機胜するかの最も印象的なむラストでしょう。



生きおいるモデルがあり、い぀でもスナップショットを取埗できたす。



最埌に、特に線集者にずっおのボヌナス。 Reduxを操䜜するためのアダプタヌがありたす。 , Redux style, store mobx-state-tree store reduxStore asReduxStore.



ReduxDevtools, connectReduxDevtools, , store mobx-state-tree, .



- immutable-. -, , , . - , .



. , . Todo.folder.parent , . , , .



, . , React. , . — Redux Store Redux Devtools.



, MobX — React, . , :





.



All Articles