こんにちは、この記事では、我々は人気の枠組みのボンネットの下に見てみましょうアンギュラ2.今日のメカニズムを説明しています。
角度2コンパイラ - この記事は、レポートトビアスボッシュ(トビアスボッシュ)の意訳です。 元のレポートへのリンクは記事の最後に見つけることができます。
概要
トビアスボッシュ - グーグルの社員およびコンパイラの構成部品のほとんどを作成ワーキンググループアンギュラ開発者の一員。 今日、彼はボードは、角の内側をどのように機能するかについて語っています。 そして、それは素晴らしく、複雑なものではありません。
私はあなたが自分のアプリケーションで得た知識の一部を使用することを願っています。 おそらく、あなたは、私たちのチームのように、独自のコンパイラや小さなフレームワークを作成します。 私は、コンパイラ、我々は我々の目標を達成する方法についてお話します。
コンパイラは何ですか?
これは、入力データ・アプリケーション間で起こるすべては、あなたの実行中のアプリケーション(彼らはあなたのチーム、テンプレートなども可能)です。

間で起こるすべてが - コンパイラの活動の球です。 表面上では、これは純粋な魔法であると思われるかもしれません。 しかし、これはそうではありません。
私たちは、今日見ていますか?

まず、我々は、迅速な仕事とは何を意味するのかのパフォーマンスについて少し話を? 我々は、我々が持っている種類の入力のかについて話しています。
我々はまた、それが何であるかの字句解析について話すと、その角度2コンパイラは、分析を行います。 私たちは、角度2が分析されたデータを受け取り、それらをどのように処理するかについてお話します。 まず、単純な実装し、その後、さらに改善された改善で一般的には、このプロセスの実装では、我々は、3回を持っています。 だから我々はそれをやりました。 これらは、我々はコンパイラの加速仕事を達成することができましたそれによって段階です。
我々はまた、ダイナミック(ジャスト・タイム)と静的(前もって)コンパイルの長所と短所を別の環境について説明します。
パフォーマンス:高速な作業を意味?
状況を想像:私はそれが高速に動作することをアプリケーションと確認を書きました。 私は何を意味するのですか? あなたが考えるかもしれない最初の事は、アプリケーションの負荷に非常に迅速であるということです。 あなたは、AMP-ページGoogleのを見たことがあるかもしれませ? 彼らはすぐに非現実的であるロードします。 これは、「高速」の概念に起因することができます。
おそらく、私は偉大なアプリを使用しています。 私は1つの列から別のものに切り替えます。 例えば、簡単なレビューから拡大ページに、この移行は非常に速く起こっています。 また、速度を特徴づけることができます。
それとも、のは、私は大きなテーブルがあるとしましょう、と今私は、その値のすべてを変更します。 私は構造を変更することなく、新たな価値を創造します。 それは本当に速いです。 このすべて - 同じコンセプトの異なる側面、あなたがアプリケーション内に注意を払う必要があることを異なるもの。
方法に行きます
私は、多くの方法(経路切替)の遷移に詳述集中したいと思います。
あなたがもう一度、水路上の枠組みの破棄と、再作成をクリックします - 重要なポイント。 彼は、構造物に付着しない:すべて破壊され、すべて新たに再作成します。
どのように私はより速く、このプロセスを作ることができますか?すぐに何かを行うには、速度を測定する必要があります。 これはテストが必要です。 深い木法のベンチマーク - 私たちが使用するパフォーマンステストの一つ。 あなたはこのことについて聞いたことがあります。 二回使用されているこのコンポーネント、。 あなたが特定の深さに到達するまでになるまで、この再帰コンポーネント。

我々は512個の部品及び破壊するか、これらのコンポーネントを作成することができるボタンがあります。 次に、我々は、部品の破壊と創造に費やされているどのくらいの時間を測定します。 そして方法で移行があります。 もう1つのビューからの移行。 すべての破壊 - すべての創造。
私たちは、入力を持っていますか?コンポーネント私たちは、食材を持っている、と私は、彼らはすべてを知っていると思います。

彼らは、テンプレート(テンプレート)を持っています。 あなたは、内蔵のテンプレートを作るか、別のファイルに入れて、彼らがコンテキストを持っていることを覚えておく価値であることができます。
その性質のコンポーネントのコピーは - テンプレート(テンプレート)を構築するために使用されるコンテキストデータです。 私たちのケースでは、我々はユーザーを持って、ユーザーが名前(この場合は、私の名前を)持っています。
模様次に、我々はテンプレートを持っています。 これは、ここであなたはその申し出をHTMLのすべてを適用することができ、フォームのようなものを挿入することができ、HTMLの簡単です。

ここでは、いくつかの新しい構文を持っている:二重の中括弧、角括弧、丸括弧を覚えていますか? この角度のプロパティやイベントを結合。 今日、我々は、中括弧とその意味についてお話します。 手段意味論の観点から、「コンポーネントからデータを取得し、特定の場所でそれらを置くために。」 データは、テキストを更新する必要があるとき。
ガイドライン
CSSセレクタ - ディレクティブは、セレクタが含まれています。 一番下の行は、角度は、マークアップを通過するとき、それはいくつかの要素に対応する指令である場合、それを実行することです。
私たちは、フォーム要素を作成するたびに、このディレクティブを作成してください - 私たちはフォームのためのセレクタを持っており、これは我々が言っているようだと仮定します。
同様に、ngModelと。 あなたはngModel属性を作成するときは、作成およびディレクティブなければなりません。
これらのディレクティブは、ベースとすることができます。 それは私たちの依存関係の表現です。

依存性はそれほど要求されたngModel ngFormことを、階層構造を持っています。
そして、何角度を作りますか?彼は、ツリーの1つの上のレベルが高いです最寄りngForm、の検索ですべての木に見えます。 彼は兄弟、両親だけを見ていないだろう。
そこ他の入力がありますが、我々は詳細にそれらに住むことはありません。
角度で行われているすべては、コンパイラを通過します。
まあ、我々は、入力を持っています。 私たちが必要とする次のことは - それを理解することです。
それは妄想のセットだけだ、あるいは、まだ、それはいくつかの意味を持っていますか?
プロセスの字句解析
第一段階
例えば、我々は特定のパターンを持っているHTMLと仮定する。
提出テンプレート
我々は、コンパイラがそれを理解するように、想像できるように?
これは、アナライザを扱います。 彼は、各文字を読み込み、意味を分析します。 つまり、ツリーを作成し、です。 各要素は、1つのオブジェクトのみを持っています。 いくつかの名前があると - 要素の名前が子を持ちます。 レッツ言うには、テキストノードは、JSONのプロパティを持つテキストオブジェクトです。 また、属性を持っています。 私たちは、ネストされたリストとしてそれをすべてをエンコードし、ちょうど言ってみましょう。 最初の値は - これはキー、第二である - 属性値です。 ように、複雑なものはありません。 このような表現は抽象構文木( - AST AST、英語)と呼ばれています。 あなたは、多くの場合、この用語を聞くことができます。 HTML - それがすべてです。
どのように我々は、データとの接続要素を表すことができますか?
次のように我々はそれを表示することができます。 これはある - であるテキストノードは、我々はテキストの特性を持つJSONオブジェクトを持っています。 もともと表示するテキストがありませんので、テキストは、空です。 テキストは、着信データに依存します。 着信データは、この表現で提示しました。
任意の式はまた、彼らは何を意味するかについて評価されます。
あなたはアナウンスや関数表現isrolzovatサイクル内のため、まだ我々はあなたが表現を扱うことができ、それを通してパイプのようなものを、持つことができません。
私たちは、財産への道として、この表現、user.nameを表すことができます。 そしてまた、私たちはあなたのテンプレートは、それの発現があるから、それがどのように把握することができます。
表現の場所を決定しますでは、なぜそれはとても重要なのですか? なぜ表現があるSDAから来ている場所を知ることが重要なのですか?
私たちは、プログラムの実行中に、あなたにエラーメッセージを表示したいためです。 ちょうどあなたのユーザーがこれらのエラーについて知っていることを言ってみましょう。
そして、例外がありますか? 例えば、未定義の名前を読み取ることは不可能です。 この問題が発生した場合は、に行くとエラーデバッガをチェックする必要があり、最初のエラーでブレークポイントを設定します。 その後、エラーが発生した場所を正確に理解する必要があります。
角度のコンパイラは、より多くの情報を提供します
テンプレートこのエラーの「足を育てる」に置き、そこからそれを示し、。 目標 - 、例えば、エラーが発信することをお使いのテンプレート列の2行目14で、この特定の補間をお見せします。 これを行うために、私たちはラインに必要と列番号はSDAにありました。
さらに、我々はASDそれを構築するための分析装置を必要とするもの?ここでは多くの機会があります。 たとえば、私たちは、ブラウザを使用することができます。
ブラウザ - 偉大アナライザHTML、右? 彼は毎日これをやっています。 このようなアプローチは、我々は角1、我々は、角2の開発に使用し始めた同じアプローチを開発しなければなりませんでした。
今、私たちは二つの理由から、このような目的のために、ブラウザを使用しないでください。
- ブラウザは、行と列の数を引くことはできません。 HTMLブラウザを分析する場合、単純にそれらを使用しないでください。
- 私たちは、角度がサーバー上で働いていたします。
サーバーが何のブラウザを持っていないことは明らかです。 我々は、我々はブラウザと私たちは何か他のものを使用して、サーバーを使用するブラウザを言うかもしれません。 そしてそれはでした。 しかし、その後、私たちは、このようなSVGとして、デッドロックになった、またはカンマの配置、私たちはどこにでもそれを持っていた同じセマンティクスました。 したがって、JavaScriptを抜粋、およびアナライザを挿入する方が簡単です。 これは、私たちが何をすべきかを正確です。
そこで、我々は、HTMLや表現について話しました。
我々は見つけることの指針を提示したよう?私達はちょうど別のプロパティを追加することで、要素を表すJSONオブジェクトを介してそれらを表示することができます:ディレクティブを。 そして、我々はこれらのディレクティブのコンストラクタ関数を参照してください。
ngModelと入力データとの例では、JSONオブジェクトとしてこの要素を表すことができます。 彼は、入力、属性、ngModelとディレクティブの名前を持っています。 私たちは、デザイナーへのポインタを持っている、と我々はことを指摘する必要があるため、我々はngModelを作成し、我々はngFormを必要とし、私たちは、この情報をピックアップする必要がある場合、我々は、依存関係をキャッチ。
HTML情報、通信、指令にSDAを考える - あなたの生活の中で我々はそれがすべてだ実現して? これは最も簡単な方法を行うことができますか?
まずHTMLを理解しています。 DOM要素を作成する最も簡単な方法は何ですか? まず、あなたがinnerHTMLプロパティを使用することができます。 第二に、あなたは、既存の要素を取り、それを複製することができます。 そして第三に、あなたはのdocument.createElementを呼び出すことができます。
投票をしてみましょう。 最速 - 誰がinnerHTMLのへの道と考えていますか? そして、誰が最速のアイテムをelement.cloneNodeだろうと言いましたか? それとも、最も簡単な方法 - それはelement.createElement?
明らかに、物事は、時間の経過とともに変化します。 しかし、現時点では:
- innerHTMLプロパティ - これは最も遅いオプションです。 ブラウザは、あなたのアナライザを呼び出すあなたの文字列を取る、各文字を歩くとDOM要素を構築する必要があるので、これは、明らかです。 明らかに、これは非常に遅いです。

- element.cloneNodeは - ブラウザがすでに投影が構築されているので、最速の方法であり、それはちょうどこの投影のクローン。 それはちょうど別の要素のメモリに追加することです。 それは必要なことすべてです。
- document.createElement - それは前の2つの方法の間のものです。 この方法は、element.cloneNodeに非常に近いです。 少しは、しかし非常に近く、遅くビット。

あなたは「まあ、»の使用element.createElementは、新しい要素のDOMを作成するためにしましょう、と言います。
そこで彼は、角度1を働いた、そして我々が開発を始めとして2角度と伝統によると、これは、少なくともではない角度の場合には、公正な比較ではないことが判明します。 アンギュラ使用の場合は、我々はいくつかの要素を作成する必要がありますが、それ以外は、私たちはこれらの要素を配置する必要があります。 私たちのケースでは、我々は新しいテキストノードを作成したいのですが、また、我々は後に、我々はそれを更新したいので、user.nameする責任があるものを見つける必要があります。
我々は比較すればそのため、我々は、作成と配置と比較することがあります。 あなたがinnerHTMLプロパティまたはcloneNodeをを使用する場合は、すべての方法のDOM構造を再渡す必要があります。 createElementのかはcreateTextNodeを使用している場合、あなたはこのアクション側を迂回します。 あなたは、単にメソッドを呼び出すと、すぐにそれが実行されます。 新設ず、他人ありません。
この比較点、及びのcreateElementはcreateTextNodeでは、両方が同じ速度(バインディングの数に応じて)程度です。
第二に、それははるかに少ないデータ構造をとります。 あなたは、このような方法は簡単で、ほぼ同じ速度ので、これらすべての指標などを追跡する必要はありません。 したがって、我々はこれらのメソッドを使用し、他のフレームワークも、このアプローチに切り替えます。
だから我々はすでにDOM要素を作成することができます。
今、私たちは、ディレクティブを作成する必要があります私たちは、親要素に子会社による依存性注入する必要があります。 私たちはDOM要素と、この要素のためのガイドラインが含まれてngElementと呼ばれるデータ構造を、持っていると仮定します。 親もあります。 DOMツリー内のこの単純なツリー。
そして、どのように我々はSDAのDOM要素を作成することができますか?私たちは、テンプレートを持って、我々はSDAを構築し、そこから要素を持っています。 我々は、すべてこれで何ができますか?

私たちのngElementとコンストラクタ呼び出しのdocument.createElementでは、属性をスキャンして、要素に割り当て、その後、私たちの親に要素を追加します。 あなたが見ることができるように、魔法はありません。
そして、ディレクティブに進みます。 それがどのように動作しますか?私たちは、結合を確認し、何とかそれらを得る(後でそれについての詳細を教えてくれ)と、単に新しいデザイナーのためのコールを再、スナップ彼を与えると店舗地図。 地図は、ディレクティブのインスタンスへのディレクティブのタイプ(ngModel)に切り替わります。
そして、すべてこの検索ディレクティブは、このように動作します:我々は、最初にチェック要素自体のディレクティブを、(それはディレクティブを持っているかどうか)を取得する方法があります。 そうでない場合、私たちは親に戻って、そこに確認してください。
これは、あなたが行うことができます最も単純なものです。 私たち自身がこの方向で開発しました。 そしてそれは動作します。
重要な詳細:結合。 バインディングを表示するには?ノード - あなたは、単に目標を持っているバインディング・クラスを作成します。 彼らは、テキストノードになります。
私たちは、プロパティは、この場合には、ノードの値である目標、これは値を配置する場所です。 そして、表現。

スナップは、このように動作します:たびにあなたが式を評価するか、それがちょうど変更されたとき、あなたはターゲットに保管してください。
- ターゲットの更新と以前に格納された他の値を最初にそれが変更された場合は、式を評価する:それはあなたが次のメソッドを持つことができる、です。

我々は先に述べたこれらの例外については、 - 私たちは、評価パスをトラッキングするためのキャッチ方法を試してみてください呼び出します。 例外がトリガされると、我々は再生成し、彼のために、行と列の数のモデルを作成します。
だから我々は、行とエラーが発生した列数を取得します。 それは我々がビューに関連するすべてです。 これが最後のデータ構造です。

プレゼンテーション - このテンプレート要素。 我々は、エラーコードを見た場合には、ある - 私たちはたくさんのアイデアが表示されます。 それはちょうどテンプレート要素です。 そして、我々は表現でそれらを結合します。 プレゼンテーションは、要素をngのとスキャンとチェックは、その結合がダーティチェック方法ならびに結合、成分を指します。
だから我々は最初のステージを終えました。 私たちは、新しいコンパイラを持っています。 これまで我々は、高速のように? ほとんど角度1.持って良いと同じレベル。 より簡単なアプローチを使用して、我々は同じ速度を達成しています。 しかし、角度1は遅いではありません。
第二段階

どのように我々は、プロセスをスピードアップすることができますか? 次のステップは何ですか? 我々は見逃しているということ? 正しくしましょう。
私たちは、データ構造に関係している何かを必要としています。 それはデータ構造になると、実際には、これは非常に難しい質問です。 最後に比較したとき、我々はのtry-catchがあるプログラムを書いたが、私たちはそれを無視した場合、我々はゆっくりとしたプロセスダウン機能の多くが表示され、ポイントの多くのことをあなたが最適化したいです。 あなたがプログラムのデータ構造の遅い仕事の理由を考える場合、彼らはあなたのプログラムに散在しているので、それは、非常に難しい問題です。 これは、データ構造のことだ、ただの推測です。
我々は実験を行い、理解しようとしました。 地図の内側のngElements:我々は、これらのガイドラインを見ました。

これは、DOMツリー内の各要素のために、我々は新しいマップを作成することが判明しますか? 一つは、我々はそれらを作成していない、何の指針が存在しないことを言うことができます。 それでも、私たちは常に、マップを作成ガイドラインと読み出した情報とそれを埋めます。 これは無駄なことができ、それはメモリをオーバーロードすることができ、読み取りは、まだしばらくの間、あまりにもかかります。
別のアプローチは、さて、私たちは一つの要素にのみ10ディレクティブを許可する」、と言うことかもしれません。 次は、私たちは、ディレクティブの要素ディレクティブと種類のクラスinlinengElement 10プロパティを作成し、»なら、私たちは10条件文を作成しているディレクティブを見つけること。 それは高速ですか! おそらく。
それは右、メモリリソースを大量に消費しないのですか?たとえば、インストール:あなたはプロパティではなく、マップを設定します。 読み取りがあるため10の条件の少し遅いことができます。 これは、正確にはJavaScript VM用に最適化された場合、です。 JavaScriptのVMは(あなたがあなたのレジャーでそれらをグーグルことができます)隠しクラスを作成することができます。 これは、より高速なJavaScriptのVMを作成します。 このデータ構造への切り替え - これが加速するものです。 その後、我々は、パフォーマンステストの結果を見ていきます。 あなたが最適化データ構造にするもう一つ - 既存のコピーの再利用。
いくつかのラインが破壊された場合、もう一方は、なぜ、キャッシュ内のこれらの行をキャッシュや線が現れるとすぐにデータを変更しないで、回復 - あなたは論理的な質問を指定することができますか? だから我々はやりました。 私たちは、提出の古いコピーを復元する、いわゆるビューキャッシュを作成しました。

あなたは新しいプールに行く前に、状態を破壊する必要があります。 条件は、ディレクティブに含まれています。 我々はすべてのディレクティブを殺すようにします。 プールから抜け出す方法がある場合にさらに、あなたは、これらのディレクティブを再作成する必要があります。 この方法では、水和物と脱水を行います。 モデルで - すべては、モデル、全体の状況から来ているようDOMは、我々が保持しているノード。 我々だからとは、それを保ちました。 ここでも、パフォーマンステストを持っていました。
テスト環境手動のJavaScriptコードとハードコーディングされたとするプログラムです - あなたはこれらのテストの結果を理解するために、ベースラインがあることに留意されたいです。 このようなプログラムは、任意のフレームワークで使用されていないで、プログラムは、ちょうどこの深いツリーテストのために書かれました。 プログラムはdirtyCheckingを生産しています。 私たちは、このプログラムユニットを引き継ぎました。 他のすべての比率で比較されます。 1の角度は、5.7のマークを受けました。

以前、我々は、最適化されたデータ構造とし、ビューキャッシュなしで同じ速度を示しました。 私たちは、2.7のレベルにありました。 だから、良い指標です。 私たちは、による特性への容易なアクセスに二回の速度を増加しています。 最初に我々はここで我々の仕事が終わると思いました。
2段目の欠点私たちは、これに基づいてアプリケーションを作成しました。 しかし、その後、私たちは欠点を見ました:
- 不良メモリとViewCacheの友人。 クエリ処理ルートを切り替えて想像してみてください。 それらがキャッシュされるので、あなたの古い質問は、メモリ内に残ることはないですか? キャッシュからクエリを削除する際の質問はありますか? 実際には、これは非常に難しい質問です。 あなたは、ユーザーがキャッシュ何かするかどうかを選択できるようになるいくつかの簡単な要素を、作成することができます。 しかし、それは、少なくとも奇妙だろう。
- もう一つの問題:DOM要素が隠された状態を持っています。 例えば、要素は、焦点に配置されます。 あなたが焦点に結合し、いない場合でも項目が焦点にし、それから、それを削除するか、またはリターンがこのまたは他の要素のフォーカスを変更することが両方にすることができます。 それについて、私たちは考えていませんでした。 関連のバグがありました。 あなたはこの道を行くことができます:我々は完全に彼らの条件を削除する項目を削除し、さらにそれらを逆にすることができます。 私たちはDOMを再作成しなければならない場合しかし、これは、ViewCache指標を否定するでしょう。 すべての後、我々は2.7の率を示しました。 我々は、このようなAの状況での速度に達してきたように?
第三段階
クラスビュー次に考えたのは、私たちに起こった:私たちは私たちのクラスビューで再び見てみましょう。 私たちは何がありますか?
これらは、すでに権利、ngelementを最適化している - 私たちは、コンポーネントを持っていますか? 私たちは、結合しています。 しかし、クラスビューは、まだこれらの配列が含まれています。 我々はまた、プロパティのみを使用していますInlineViewを作成できますか? いいえ配列ありません。 それは可能ですか? それはそう、判明しました。
どのように見えますか? 約そう。
模様そこで、我々は、以前のように、テンプレートになり、すべての要素のために、私たちはただ、書き込みコードはよ。 このパターンのために、私たちは私たちのクラスのビューを表示するコードを作成します。
各要素のコンストラクターで、document.createElementを呼び出します。この要素はNode0プロパティに格納されます。最初の要素では、2番目の要素では、再びnode1に格納されるdocument.createElementを呼び出します。さらに、要素を親にアタッチする必要がある場合、プロパティがありますか?正しい順序ですべてを行う必要があります。このプロパティを使用して、以前の状態を参照できます。これが、DOMで行うことです。
ディレクティブディレクティブについても同様です。各インスタンスのプロパティがあります。繰り返しになりますが、アクションの順序が正しいことを確認する必要があります。依存関係が最初に来てから、これらの依存関係を使用するコンポーネントです。まずngFormを使用し、次にngModelを使用します。バインディング次に、バインディング。ダーティチェックを行うコードを作成するだけです。式を取得し、JavaScriptに変換します。この場合、this.component user.nameになります。これは、コンポーネントからuser.nameを取得し、それをプロパティでもある以前の値と比較することを意味します。値が変更された場合、テキストノードを更新します。最後に、すべてをデータ構造の表現に縮小します。プロパティのみがあります。プロパティを介したクイックアクセスが使用されるすべての場所に、Mapという配列はありません。これにより、プロセスが大幅に高速化されます。すぐにこれを見ることができるように数字を表示します。問題は、これをどのように行うかです。誰かがこの新しいクラスを評価する文字列を作成する必要があるとしましょう。これはどのように行われますか?
現在の実装では、実装101で行ったことをそのまま適用します。一番下の行は次のとおりです。以前にDOMノードを作成した場合、DOMノードを作成するコードを作成しています。要素を比較するために使用した場合、要素を比較するコードを作成しています。最後に、ディレクティブインスタンスまたはDOMノードを参照していた場合、ディレクティブインスタンスとDOMノードが保存されている場所にプロパティを保存します。この場合、コードは次のようになります。
以前はngelementがありましたが、compileElementができました。実際、これらのクラスは現在コンパイラに存在しています。 compileElement、compileViewなどがあります。表示は次のようになります。DOM要素を取得する前は、DOM要素が格納されているプロパティしかありません。以前はdocument.createElementを呼び出していましたが、この新しい文字列補間を使用して行を作成しています。これは、this.document +そのプロパティ名がADDという名前のdocument.createElementと等しいというコードを作成するのに最適です。最後に、以前にappendChildを呼び出した場合、子を親にアタッチするコードを作成しています。ディレクティブの依存関係の検索でも同じことが起こります。すべてが同じアルゴリズムに従って行われますが、現在はこれらの目的のためにのみコードを作成しています。
インジケーターを見ると、速度が大幅に向上していることがわかります。以前のインジケーターが2.7だった場合、1.5になりました。ほぼ2倍の速度です。それでも、ViewCacheは少し高速のままです。しかし、私たちはそれを使用するオプションを排除し、あなたはすでに私たちの決定の理由を知っています。私たちは素晴らしい仕事をし、すでに終わっていたかもしれません。しかし、ありません。
動的(ジャストインタイム)コンパイルそれでは、最初に動的(ジャストインタイム)コンパイルについて説明しました。動的-これは、ブラウザーでコンパイルすることを意味します。
これは次のように機能することを思い出してください。サーバー上に受信データがあります。ブラウザはそれらをロードして選択し、すべてを分析して、元のビュークラスを作成します。次に、別のクラスを取得するためにこのソースコードを評価する必要があります。その後、このクラスを作成し、動作するアプリケーションを取得できます。この部分には特定の問題があります。- , . - , , . . , . , . .
- – , , Angular . , , .
- – eval. eval , - - . eval, , .
(, — eval )
- , (, ). , , . , Angular . これはあまり良くありません。
- , , . ( , , ), - , :
- -, , , , .
- -, . , , . Closure ( « »), Google .
そのため、コンパイラの実行中に改善された縮小を使用すると、これが起こります。マークアップのコンポーネントがブラウザに読み込まれ、ブラウザがテンプレートを解析してビュークラスを作成します。これまでのところ、すべてが順調に進んでいます。ブラウザはuser.nameを使用し、コンポーネントにはuser.nameも含まれます。このuser.nameのみが高度な縮小技術を使用して縮小されます。したがって、コンポーネントはC1などと呼ばれ、ユーザーは突然Uになり、名前はNになります。問題は、ミニファイヤがテンプレートを認識していないことです。実際、user.nameはまだテンプレートにあります。そのため、テンプレートが実行され、user.nameが作成されますが、これはコンポーネントには存在しません。この問題には特定の解決策があります。このプロパティを縮小する必要がないことをコンポーネントに伝えることができます。しかし、それは私たちが必要とするものではありません。このプロパティを縮小できる必要がありますが、これはオンラインでのコンパイルおよび評価では機能しません。静的(先行)コンパイルこの理由から、次のステップは静的(先行)コンパイルの出現でした。彼女は次のように働いています。
この場合も、サーバーで解析される入力があり、ビュークラスもサーバーで作成されます。次に、ブラウザはそれらを取得し、通常のスクリプトテキストのように(通常のJavaScriptコードをロードするように)ロードし、必要な要素を作成します。このコンパイル方法は素晴らしいです。なぜなら、分析はサーバー上で行われ(したがって、迅速に行われる)、コンパイラーをブラウザーに転送する必要がないからです(これも素晴らしいことです)。また、これはスクリプトであるため、評価を使用しなくなりました。したがって、このようなコンパイルはどのブラウザーにも適しています。また、静的なコンパイルは、サーバー上にビュークラスを作成し、ミニファイヤを実行すると、ビュークラスもミニファイできるため、ミニファイの改善に最適です。ミニファイヤが仕事を終えたので、クラスの名前が変更されたプロパティを取得します。素晴らしい、今改善された縮小を使用できます。したがって、速度インジケータはさらに低下しています。静的コンパイルの欠点したがって、静的コンパイルができました。これですべてうまくいきましたよね?しかし、いつものように、欠点があります。最初の問題は、静的コンパイルを使用して別のコードを作成する必要があることです。ブラウザーで評価するには、ES5標準に従ってコードを作成する必要があります。ローカル変数を使用するコード、または関数に渡される引数を受け取るコードを作成しています。require.jsなどを使用していません。コードはサーバーで生成されるため、次の2つの理由でTypeScriptコードを生成する必要がありました。- 最初に、式のタイプ(コンポーネントに存在するかどうか)を確認したかった。
- -, TypeScript. , , . — , require.js, system.js, Google Closure, .
- , ES6 2016.
ES6 2016のサポートをどのように提供しましたか?実際、コンパイラーに精通している場合、共通のパターンがあります。文字列を作成する代わりに、出力に似たデータ構造が作成されますが、このデータ構造(このADD)は異なる出力データにシリアル化できます。ASDには、変数宣言、メソッド呼び出しなどの機能に加えて、タイプが含まれています。次に、ES5の場合は型なしですべてシリアル化し、TypeScriptの場合は型でシリアル化します。次のようになります:宣言された変数内で生成されたASD出力(declare var name ELを指定します)。これにより、TypeScriptで変数コードが作成され、タイプが作成されます。 ES5では、タイプは省略されます。次に、メソッドを呼び出すことができます。まず、ドキュメント変数を読み取ります(グローバル変数であるため)。そして、彼女のために、これらの引数でcreateElementメソッドを呼び出すことができます。文字列を解析する場合は、文字列を正しくエスケープする必要があるため、リテラルを値「div」で配置しました。値には引用符が含まれている可能性があるため、コードを読み取るときにエスケープする必要がある場合はスキップしてください。したがって、この方法でそれを行うことができます。良いニュースは、生成されたコードがサーバーとブラウザーの両方で同じように見えることです。異なるコードはありません。静的コンパイラの開発中に発生した2番目の問題は、メタデータの割り当てです。このコードを見てください。
このコードの問題は何ですか?Cookieに依存するある種のディレクティブがあり、Cookieがある場合、ディレクティブは別のことをするとします。動作します。コンパイルできます。素晴らしい。
しかし、これは静的コンパイルでは機能しません。あなたがそれについて考えるなら、なぜですか?これらすべてをES5のレベルに下げると、まさにそのようなコードが得られます。デコレータは最終的に何をしますか?メタデータのコンストラクタにプロパティを追加します。最後に、彼は単にSomeDirにメモを追加します。問題は、サーバーでこれを実行すると機能しないことです。結局のところ、サーバー上にドキュメントはありません。どうする?
サーバー上にブラウザ環境を構築し、ドキュメント変数、ウィンドウ変数などを宣言することを提案できます。これは機能する場合もありますが、ほとんどの場合機能しません。 2番目の方法(現在、ASDに精通していますか?)ASDを処理し、コードを評価せずにASDからメタデータを削除します。 ASDでは、何らかの形で表現できます。
したがって、ADD SomeDirクラスには、呼び出す要素を参照するdecoratorsプロパティがあります(これは、ディレクティブと引数が定義されている式です)。次のことが起こります。メタデータをJSONファイルに取り込み、静的コンパイラーがこれらのファイルを取得して、それらからテンプレートを作成します。明らかに、制限があります。 JavaScriptコードを直接評価することはありませんので、ブラウザで慣れていることをすべて行うことはできません。そのため、私たちの静的コンパイラは私たちを制限しますが、あなたはメモを置くことができます。ただし、上記の方法を使用する場合、これはすべての場合に機能します。さて、もう一度パフォーマンステストについて説明しましょう。
これは私たちが書いた簡単なプログラムです。また、読み込み時間が大幅に短縮されました。これは、分析を行わなくなったためであり、Angular 2コンパイラがロードされなくなったためです。ほぼ3倍高速です。サイズは、すでにgzip形式で縮小された144キロバイトから49に縮小されました。比較のために、縮小が改善されたReactライブラリの重量は46 kbになりました。次に、いくつかの実例を示します。Angularコンポーネントがあるとします。ngFormとngModelがあります。 ngModelには、依存関係としてngFormがあります。
ここにNGCを追加します-メタデータを引き出してサポートするとき、これらのモジュールはTypeScriptに依存するため、これらはTypeScriptコンパイラにまたがる_nodeモジュールです。生成されたコードを見ると、非常に馴染みがあることがわかります。 user.nameがあります。値が変更された場合、ディレクティブ入力を更新します。前の値と比較して設定するだけです。これには良い面があります。テンプレートでuser.nameをwrongNameに変更するとします。テンプレートは、ユーザーではなくuser.nameを参照します。 wrongName。生成されたコードを見ると、TypeScriptはエラーをスローします。
これらの表現にはコンポーネントのタイプに基づいたタイプがあるためです。そして今、それらをコンパイルすると、TypeScriptを使用したという理由だけで、コードにエラーが見つかります。他に何も必要ありませんでした。うまくいきましたが、さらに良くなるよう努力しています。私たちの目標は、10 kb gzipのサイズを達成することです。プロトタイプの一部のサイズは25 kbであり、コンパイラーをさらに小さくする方法についていくつかのアイデアがあります。次に、ベースラインの開始点よりもさらに速くなりたいです。これは制限ではないことを認識しており、ベースラインよりもさらに速くなる可能性があります。変更はありません。変更を行い、異なるコードを生成します。唯一のこと-速度の違いのみが表示されます。結論として
パフォーマンス、パフォーマンスのさまざまな側面について話しました。入力について説明しました。分析とは何か、ASD(AST)を学びました。テンプレートを表示する方法と実装方法について説明しました。document.createElementは、Angularフレームワークに最適であることを学びました。プロパティと非表示クラスによる迅速なアクセスは、コードを最適化するための優れたツールであることを学びました。コード生成(評価が正しく行われた場合、つまり、評価の助けだけでなく、オフラインサポート、ASDの派生も)が素晴らしい強力なものであることを学びました。これは、隠しクラスを生成するのに役立ちます。静的(Ahead Of Time)および動的(Just In Time)コンパイル、および最適化できるものについて説明しました。そして最後に、良い例を見てみました。スライドに私のプレゼンテーションへのリンクがあります。ご清聴ありがとうございました。