日曜倧工のvdom

こんにちは







倚くのフロント゚ンド開発者は、特にReact.jsで䜿甚されるVDOMテクノロゞヌはブラックボックスのように機胜するず考えおいたす。 たた、npmのオヌプンスペヌスには、このテクノロゞヌを実装するラむブラリがたくさんありたすが、私にずっおは、悪魔の足がそれらに䟵入したす。 VDOMトピック自䜓に少し前から興味があり、自分で觊っお実隓を始めたした。 最終的には、VDOMの実装を䜜成し、それをデヌタグリッドフレヌムワヌクに組み蟌んだずいう事実に終わりたしたどうにかしおそれに぀いお説明したす。 簡単ではありたせんでしたが、非垞にシンプルでした。この蚘事では、䜕が、なぜ、なぜかを詳しく説明したす。 そしお、その仕組みを説明したす。 キャットの䞋に飛び蟌むず、興味深い䜓隓ができたす。







ディスクラマヌ



私はフロント゚ンド開発者ではありたせん。 スリッパで私をhitらないでください。

たた、「既成の枠組みを取りなさい、クラブ」ず曞いおはいけたせん。 既補のフレヌムワヌクがあるこずは知っおいたすが、玔粋に技術的な理由から、独自の実装が必芁です。







なぜそれが必芁ですか



TL; DR  jsfiddleに関するこの蚘事の ゜ヌス







ハブのどこかにVDOMに぀いおの蚘事が既にありたしたが 、問題の本質の説明が奜きではありたせん。 それで、指にそれを投げさせたす。 それで、あなたがりェブペヌゞを持っおいるずしよう 特定のHTML芁玠おそらくかなり倧きいをレンダリングしたした。 少しのコンテキストを远加するには-この芁玠は、ビュヌモデルでテンプレヌトを匕っ匵っお描画されたす。 たあ、誰もがこれを行うフレヌムワヌクを䜿甚したしたか そこにあるハンドルバヌ、lodashテンプレヌト、AngularJS ...芁するに、テンプレヌトがプルされるモデルが倉曎されたず想像しおください。 そしお、これらの倉曎をHTML芁玠内に描画する必芁がありたす。 問題が発生したす-どうやっおそれを行い、トラブルに巻き蟌たれないのですか ここでは、い぀ものように、いく぀かのオプションがありたす。







innerHTML



テンプレヌト゚ンゞンを䜿甚しおデヌタモデルを実行し、HTMLを取埗しお、 element.innerHTML = ourHtmlString;



実行するだけですelement.innerHTML = ourHtmlString;



。 安くお陜気ですが、䞀郚のタスクではかなり良いです。 ここで恐ろしいこずは䜕ですか







そしお、私はあなたに答えたすこのオプションは、 element



内にあるすべおの子芁玠がブラりザによっお容赊なく殺され、新しい芁玠に眮き換えられるため、䞻に悪いです。 だから はい、したがっお、あなたはあなたが持っおいたもちろんあなたの芁玠内でむベントぞのすべおのサブスクリプションを倱いたす。 新しく䜜成したアむテムに再床眲名する必芁がありたす。 ここでは、正盎に認めたすが、異なるjs゚ンゞンでガベヌゞコレクションがどのように機胜するかはわかりたせんが、以前に倉数などのコヌドにいく぀かの子芁玠を保存した堎合、これらの芁玠はツリヌから削陀されたすが、砎壊されたせん。 こんにちはメモリリヌク、私たちはあなたを逃した。 さお、これは、たずえばブラりザヌがDOM芁玠の参照カりントを䜿甚するこずを前提ずしおいたす。 誰が詳现を知っおいる-コメントに曞いおください。







これに加えお-このオプションは、特に子䟛が倚い堎合は非垞に遅くなりたす。 ぀たり、ブラりザは芁玠内に描画されたすべおを正盎に消去し、再カりントしお再描画したす。 想像しおみおください-あなたはグリシャからカヌドを匕きたした。そこには100の異なるフィヌルドがありたす。 たた、デヌタでは生幎月日のみが倉曎されおいたす。 そしお今、䞍幞なテキストのために、「すべおのゎミ、グリシャ、新しいやり方でやっおくる」ずいうこずですか グリシャ党䜓を殺し、数えお再描画したすか ブラりザでむンタヌフェヌスを再描画するのは、実際には長い時間がかかるこずをご存知ですか グリシャが150の芁玠で構成されおいる堎合、それでも圌はどこぞ行っおも。 しかし、1000を超える堎合怜死によりGrishaが倚数の小さな詳现を瀺しおいる堎合、䞀郚のマシンでは再描画が数秒に達する可胜性があり、むンタヌフェむスの応答性が少し䜎䞋したす。







悲しいこずに、すべおの芁玠がinnerHTMLを介しお描画されるわけではありたせん。 䟋ずしお、IE8 では 、 tbody



芁玠にinnerHTMLを蚭定するこずはできたせん 。 Google Chromeでは、テヌブルヘッダヌ th



のinnerHTMLによるレンダリングが䞍十分です。 innerHTMLを介しおそこに挿入するものはすべお、私には䞍明な理由でプレヌンテキストに切り捚おられたす。 Pruflinkはそうではありたせん-個人的な経隓からの情報。 おそらく今ではすでに修埩されおいたすが、堆積物は残っおいたす。







したがっお、innerHTMLは、迅速で汚いハックのようなものです。 䞀般的なケヌスでは信頌性が䜎く、倚くの副䜜甚がありたす。実際、私たちはそのような曞き方をするために孊校の9幎生ではありたせん。 プロから-膝の䞊に実装。







HTMLパヌサヌ



率盎に蚀っお、このオプションは、最埌の悲しい問題であるinnerHTMLのみを解決したす-䜜成された芁玠には極床の现心の泚意が必芁です。 しかし、䞻にVDOMを実装するために必芁になるため、蚀及する䟡倀がありたす。







どのように機胜したすか はい、動䜜したす。 HTMLを解析し、各ノヌドのdocument.createElement



を呌び出しおから、属性を蚭定したす。 次にelement.innerHTML = ''



を実行し、受け取った芁玠をinsertBefore



appendChild



/ insertBefore



呌び出しappendChild



。







たあ...たず、私たちは芁玠を殺す問題を解決したせんでした。 第二に、HTML解析では、この䞖界ではすべおがそれほどスムヌズではありたせん。







HTML解析はXMLドキュメントの解析方法に関するものであり、クラむアント偎のパヌサヌでは最も頻繁に䜿甚されたす 食べ物甚 ストリヌムパヌサヌ。 ダダ、 JavaのStAXに䌌おいたす 。 XMLストリヌムパヌサヌを食べるこずは健康に良いこずが蚌明されおいたす。 なんで はい、高速か぀簡単に蚘述できるためです。 XMLの堎合のように、このタスクにクラシックりッドパヌサヌを䜿甚しないでください。 ここでは単に必芁ありたせん。







HTMLパヌサヌの䞖界ではすべおが優れおいたすが、1぀だけ䟋倖がありたす。ブラりザヌに組み蟌たれおいるものはありたせん。 MDN はDOMParserに蚀及しおいたすが 、ただ実隓的であり、おそらく出力でツリヌを提䟛したす。 ええ、クラむアントサむドでは、ツリヌを歩き回っおdocument.createElement



を呌び出すだけでは十分ではありたせんでした。 䞀蚀で蚀えば、圌は奇劙です。 圌に觊れないようにしたしょう。 htmlparser2がありたす-npmにありたす 。 むベントストリヌムパヌサヌですが、フレヌムワヌクのむデオロギヌずしおサヌドパヌティの䟝存関係を䜿甚できないため、私自身は䜿甚したせんでした。 そしお、これはJohn Rezigのコヌドの蚘事ず䟋です jQueryから分離しおください。指ですべおを説明し、簡単なパヌサヌのコヌドを提䟛したす。 正芏衚珟で機胜するため、気に入らない。 このzeloは面倒です。 しかし、それはかなり実行可胜です。 私は長い間、TypeScript甚に再蚭蚈されたこの蚘事のコヌドのみを䜿甚したした私は告癜したす。 ただし、文字列の比范を最小限に抑え、仮想芁玠の䜜成をサポヌトするために、単玔なステヌトマシンのストリヌムパヌサヌに眮き換えたしたこれに぀いおは、今日共有したすたずえば、Johnはファむルを䜜成するのに少し䞍䟿でした。







Vdom self



そしお、パフォヌマンスず芁玠䞊のinnerHTMLのうるささの䞡方の問題を解決する、最もトリッキヌなアプロヌチにたどり着きたした。 ぀たり、HTMLパヌサヌを䜿甚するず、HTMLを突然解析できたすが、 document.createElement



ではなく各芁玠を呌び出すこずができたすが、タグ名ず属性を栌玍するオブゞェクトを䜜成するだけです。 このたさにオブゞェクトは、 仮想DOM-nodeず呌ばれ、それらの組み合わせ- 仮想DOM-treeたたはVDOMです。 ここで、 var a = {};



しおJSで1000個のオブゞェクトを䜜成するこずに泚意しおくださいvar a = {};



-それは速いです。 ずおも速い。 しかし、1000個の実際のDOMノヌドの䜜成は遅いです。 この状況から、sobsno、および生産性の向䞊に぀ながりたす。







OK、䜜成したした。 そしお、私たちはこの良いこずで䜕をしたすか ズボンを脱いで走る ズボンを脱ぐ必芁はありたせんが、実行する必芁がありたす。VDOMツリヌの構造を既に描画されおいるものず比范し、これから䜕らかの皮類の差分パッチを䜜成したす远加する必芁があるもの、削陀するもの、属性をドロップする堎所、コンテンツを倉曎する堎所既存のDOMツリヌにロヌルしたす。 これは、マヌゞ䞭にお気に入りのgitで発生するのずたったく同じです。 幞いなこずに、プログラミングでdiffを䜜成するタスクは非垞によく知られおいたす- 最も䞀般的なサブシヌケンスを芋぀けるこずによっお、突然グヌグルで怜玢されたす。 動的プログラミングによっお解決されたす。 アルゎリズムには2次の耇雑さがありたす。 優れた倧孊では、この問題の解決策は最初のコヌスで研究されおいたすが、ここでは論文を説明し、それを朚に適応させる方法を説明したす。







UPD コメントで、私はLCSがこのタスクに最適ではないずいう䞻題にすでにトマトを投げたした。LISを䜿甚する必芁がありたすが、蚘事ずコヌドをそれほど速く曞き盎すこずはできたせん。 そのため、差分の蚈算を扱う郚分をより厳密に最適化できるこずに泚意しおください。







その結果、VDOMアプロヌチは、倉曎されおいない䞍芁な芁玠を䜜成しないツリヌ芁玠を削陀せず、メモリを倧幅に節玄し、むベントサブスクリプションを節玄したす芁玠が砎棄されない堎合。ただし、HTML芁玠を䜜成するよりもCPUに比范を匷制したす。 たた、これにより、画面䞊の1000個の芁玠のうち1぀だけが倉曎されたずきに、パフォヌマンスが倧幅に向䞊したす。







さあ始めたしょう



巊偎のtextarea



で構成される小さなアプリケヌションを䜜成したす。このアプリケヌションでは、HTMLを操䜜および/たたは倉曎し、右偎のりィンドりで䜜業結果を確認したす。 远加のラむブラリはありたせん-TypeScriptずブラりザだけです。 圌らが蚀うように、私たちは空から魔法をかけたす。







ご存知のように、最初は行ごずの分析でこの蚘事に倚くのコヌドを远加したかったのですが、以前の蚘事を念頭に眮いお、これをやろうずは思いたせんでした。 コヌド自䜓が読みやすさを䜎䞋させ、悲しくなりたす。 したがっお、蚱可を埗お、Githubぞのリンクを提䟛し、どのように機胜するのかを説明したす。 VDOMは、HTMLパヌサヌ、盎接コンパレヌタヌ、バッチ蚈算機、およびapp.tsの3぀の䞻芁コンポヌネントで構成され、これらすべおapp.ts



たずめお機胜させたす。







パヌサヌ



ここで予玄する必芁がありたす。 私が理解しおいるように、React.jsはHTMLパヌサヌなしで機胜したす。これは、テンプレヌトjsx / tsxが既に察応するノヌド䜜成呌び出しに組み蟌たれおいるためです。 これはパフォヌマンスを改善する良い動きですが、ご存知のずおり...独自のテンプレヌト蚀語を䜜成し、そのためのコンパむラを䜜成するこずは、この蚘事を曞いたずきの私の蚈画にはありたせんでした。 したがっお、私たちは手で裞のHTMLを解析したす。 そのような実装は、私たちにどこにでも工芞を埋め蟌む胜力を保蚌し、私が意味するこずを理解しおいれば、玔粋に教育孊的な奇劙さを避けるこずができたす:)それでは、行きたしょう。







スタック



JavaScriptにはスタックを操䜜するための効果的なツヌルが含たれおいないため、必芁なツヌルが必芁です。 したがっお、 単玔なスタックを䜜成したす 。 コメントはありたせん。







ノヌドコンストラクタヌ



ご存知のように、JavaScriptの芳点では、HTMLドキュメントはノヌドで構成されおいたす。 その䞀郚は、HTMLInputElement input



タグ、HTMLDivElement div



タグなどの非垞にHTML芁玠です。 そしお、いく぀かはしたせんテキスト、コメント。 䜿甚可胜なすべおのノヌドタむプがMDNにリストされおいたす 。 シンプルなものから始めたす-いわゆるむンタヌフェヌスを宣蚀したす 「ノヌドのコンストラクタヌ」。 HTMLをハヌドコヌドしないために、パヌサヌにdocument.createElement



を呌び出し、VDOMず同じパヌサヌをDOMに䜿甚したす。 私の実装では、このように芋えたす。 ご芧のずおり、HTML芁玠、テキストコンテンツ、コメントの3皮類のノヌドに制限されおいたす。 むンタヌフェむスは非垞にシンプルで、3皮類すべおのノヌドを䜜成する機胜を提䟛したす。たた、HTML芁玠に察しおは属性のむンストヌルも提䟛したす。 興行から遠く離れないようにするために、実際のHTMLノヌドにすぐに実装したす。 ずおも簡単です。 子䟛でもそれを凊理できたす。







ここで、VDOMノヌドをどのように保存するかを考えたす。 タむプに加えお、ノヌドに぀いお知っおおくべき重芁なこずは䜕ですか HTML芁玠の堎合、タグず属性。 コメントずテキストの堎合-コンテンツ。 他のすべおに加えお、子ノヌドのリストも同様に重芁です。 玠晎らしい。 型のむンタヌフェむスずenum



を蚘述した埌、コンストラクタ自䜓を実装したす このように 。







HTMLパヌサヌ



パヌサヌ-これは、 状態の数が限られおいるようなものです。 圌女は のんびり 枡されたHTMLを文字ごずに远跡し、珟圚の文字に応じお状態を倉曎したす。 状態から状態に遷移するず、パヌサヌはノヌドコンストラクタヌメ゜ッドをプルし、適切なアクションを実行したす。







たずえば、パヌサヌの文字を読み取り、誰にも觊れたせん。 Op-met <



、圌が圌を芋た堎所を蚘憶し、状態を「泚意深く耳を傟け、今はHTMLタグがある」に倉曎したす。 読んで。 Op-空癜文字を満たしたす。 ここでは、タグの名前です。 圌は<



に出䌚った堎所を芚えおおり、そこから珟圚の䜍眮たでテキストをかぎたす-ここにタグ名がありたす。 したがっお、コンストラクタヌノヌドをプルし、タグ名を枡しおcreateを呌び出す必芁がありたす。 さらに、パヌサヌはすべおの空癜文字をスキップし、 >



堎合、元の状態に戻りたす。 そしお、アルファベットの文字を芋た堎合、属性名=



、匕甚笊で囲たれた属性の倀もキャッチし、コンストラクタヌノヌドをプルしたす。







䜜成したタグをスタックに保存したす。 開始タグに到達するたびに、スタックに配眮したす。 コンストラクタヌを介しお芁玠を䜜成する堎合、スタックの珟圚の最䞊郚を芪芁玠ずしお指定したす。 ぀たらない。 倧孊の最初の数幎で電卓を曞いたずき、誰もが少なくずも䞀床は䌌たようなこずをしたず思いたす。







すべおの状態、および遷移䞭のアクションは、 状態マシンに入れたす 。これは技術的には巚倧です 蟞曞、頭の䞭の血たみれのC 「状態」->「アクションの説明」ずいう圢匏のハッシュオブゞェクト。 この堎合のアクションの説明は、3぀の郚分で構成されおいたす。









これら3぀の関数をIStateInfoず呌ばれるものに組み合わせたした。 そこで、パヌサヌの考えられるすべおの状態を説明したした。 パヌサヌ自䜓は、いく぀かの有甚なテスタヌ機胜を提䟛したしたこれは、珟圚の文字を確認するずき、n文字が戻る、珟圚の文字の次のm文字がそのような単語に远加されるかどうかなどです。 fix()



関数は特に区別されたすパヌサヌの状態を倉曎するず、数文字をさらに移動するず、保存された䜍眮ず珟圚の䜍眮の間のテキストをかじるこずができるずいう考えで、珟圚のシンボルの䜍眮を蚘憶したす。 蚘憶された䜍眮から珟圚の䜍眮ぞのテキストの断片化は、 cut()



関数によっお実行されたす。 通垞、 cut()



呌び出した埌、ステヌトマシンはパヌサヌに信号を送信したす-「ああ、芋お、開始タグがキャッチされたした。それはその名前です」。 なぜそんなに耇雑なのですか たあ...私はth玄者です。 䞍必芁に䜙分な行を䜜成しないパヌサヌを䜜成したした。







実は、埌で、パヌサヌの考えられるすべおの状態ず考えられるすべおのアクションをリストしたした-圌らが蚀うように、ステヌトマシンをプログラムしたした 。 属性名には、属性、自己終了タグ、文字-



および:



を囲む匕甚笊に関連する倚くのニュアンスがありたす。 ここでは、これらの各ケヌスの詳现な咀wingを省略したす。すべおがコヌドに含たれおいるため、必芁に応じお自分で確認できたす。







別の機胜 style



script



ずscript



パヌサヌは個別に切り取りず远加を行い、パヌサヌナヌザヌが決定する それらを実行するか、慈悲を持぀ それらで次に䜕をすべきか。 たずえば、スクリプトのeval



を䜜成できたす。 しかし、スタむルをどうするかは、私にずっおも䞍明確な質問です。 私の実装で考慮されおいない唯䞀のものは、これらのタグの可胜性のあるトリッキヌな構造です。 script



内で、 return '</script>';



スピリットreturn '</script>';



テキストに遭遇したずしたしょうreturn '</script>';



、パヌサヌはこれをscript



タグを閉じるず誀っお認識しscript



。これは䞍快ですが、簡単に修埩されたす 私は怠け者です 。







差を蚈算する



そのため、珟圚の段階では、HTMLの䞀郚を枡し、それを実際のDOM芁玠たたはVDOM芁玠に解析できる有効なHTMLパヌサヌがありたす。 完璧です。

いく぀かのHTMLが既に画面にレンダリングされおいるず想像しおください。少し倉曎したいず思いたす。 それを受け取り、調敎を行い、パヌサヌにVDOMコンストラクタヌをフィヌドしたす。 VDOMノヌドのリストを取埗したす。 次に、画面に衚瀺されるHTMLず、パヌサヌが解析した事実ずの差を蚈算する必芁がありたす。 実際、HTMLが描画される芪ノヌドを取埗し、そのすべおの子の配列を取埗し、HTMLパヌサヌが䜜成した仮想子の同じ配列ず比范する必芁がありたす。 そしお、私が「比范する」ず蚀うずき-私は「䞀臎するかどうか」ずいう質問に答えるだけでなく、いわゆる 「バッチの曎新」-叀い朚材から新しい芁玠を取埗するために远加する芁玠ず削陀する芁玠のリスト。 その埌、倉曎の結果のバンドルは、すでにレンダリングされお倉曎されおいないHTMLを砎壊せず、圱響を受けたノヌドのみを巧劙に倉曎するだけで、すでにレンダリングされたHTMLにロヌルされたす。 この操䜜はVDOM updateず呌ばれたす。 そのようなもの。 しかし、たず最初に。







比范キャッシュ



最初に、HTMLノヌドずVDOMノヌドを比范する方法を孊ぶ必芁がありたす。 これは比范的簡単に行われたす







  1. タむプを比范したす。 䞀臎しない堎合、ノヌドは異なりたす
  2. 䞡方のノヌドがテキストたたはコメントである堎合-コンテンツを比范したす。 䞀臎したすか 同じです。 䞀臎したせんか 違う。
  3. 属性の数を比范したす。 䞀臎したせんか 違う。
  4. 属性の存圚ずその倀を比范したす。 䞀臎しないものがありたしたか さお、あなたはポむントを埗る。
  5. 子孫ノヌドごずに手順1〜4を実行したす。
  6. これを読んだ堎合、ノヌドはただ䞀臎しおいたす。


ご芧のずおり、毎回ノヌドを最初から比范するのは時間がかかりたす。 䞻にステップ5の再垰が原因です。 したがっお、1぀の曎新のフレヌムワヌク内に存圚し、ノヌドの盞互の比范結果を保存するキャッシュコンパレヌタヌを䜜成したした。 したがっお、2぀のノヌドが異なるずいう事実がすでにむンストヌルされおいる堎合、再床むンストヌルされるのではなく、キャッシュから取埗されたす。 同じコヌドがたくさん 。







LCS



これは、Longest Common Subsequenceの略です。 このクラスは基本的に、動的プログラミングを䜿甚しおLCS問題を解決するためのマトリックスです。 入力に2぀の配列䞀方は実際のノヌド、もう䞀方は仮想ノヌドを枡し、䞊蚘で説明した比范キャッシュにもフィヌドしたす。 次に、 produceModifyBatch



を呌び出しお、 バッチ゚ントリの配列䞀番䞋で説明を取埗したす 。これは、そのようなノヌドで䜕を行う必芁があるかを瀺しおいたす。







たず、 produceModifyBatch



を呌び出した埌、LCSは配列の最初ず最埌から同じ芁玠を切り捚おたす doSkips



関数。 次に、残りのデヌタは動的プログラミングマトリックスに基づいおいたす- このビデオで説明したずおりです。 次に、それをバむパスしお、 削陀するノヌドず远加するノヌドを決定したすビデオでも説明されおいたす。 この段階では、 曎新する必芁があるノヌドのリストを取埗しないこずに泚意しおください。 結果には削陀ず远加のみが含たれたすが、 ただし、同じ堎所ぞのノヌドの削陀ず远加たたは远加ず削陀はupdateです。 normalizeBatch



操䜜は、1回の操䜜で隣接する远加ず削陀を折りたたむ- 曎新 、バッチ゚ントリのプラむマリ配列の再フォヌマットを行うだけです。 結果は最終的に幞せなナヌザヌに返すこずができたす。







LCSはVDOMの最も難しい郚分であり、倚くの人にずっお恐ろしい魔法のように芋えたす。 あなたが芋るこずができるように、圌らが蚀うように、アルゎリズム自䜓はトリッキヌですが、非垞に理解可胜です。 ちなみに、その耇雑さは二次ですが、心配するこずはありたせん。 倉曎が比范的少なく、芁玠の束の途䞭にある堎合、それらのほずんどはdoSkips



ステヌゞでdoSkips



られ、その結果、動的プログラミングマトリックスのサむズが3x3を超えるこずはたれです。 もちろん、ナヌザヌがお互いの䞋に立っお1䞇個のボタンを再描画しない堎合。 実際には、そのようなケヌスはたれです。 したがっお、芁玠が2぀たたは1぀ある堎合はLCSマトリックスを䜜成せず、結果を手で凊理するのが理にかなっおいたす。 実際には、倚くの堎合、芁玠の倧きなネストがありたす。 ただし、JSの呌び出しスタックを䞭断するには䞍十分ですさらに再垰的な凊理を行いたす。 そしお私たちは生きおいたす。 , React.js — . — . .







DOM Differ



, update, parent-, LCS-, HTML- . , , — , . コメントはありたせん。







! !







update: parent-, VDOM-. LCS, update batch. , , , , — . , — updateAttributes



, update, (, VDOM--). : , — , parent.insertBefore



. , — parent.removeChild



. . .







diff



, update batch-. , .







揃っお



HTML- , app.ts , .







たずめ



, VDOM. , , , JS-. -, , HTML- — , .







, .








All Articles