<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Angular Japan User Group</title>
    <description>The latest articles on DEV Community by Angular Japan User Group (@angular-jp).</description>
    <link>https://dev.to/angular-jp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1413%2Feea71483-34ec-4822-bbdd-9622a8cea253.png</url>
      <title>DEV Community: Angular Japan User Group</title>
      <link>https://dev.to/angular-jp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/angular-jp"/>
    <language>en</language>
    <item>
      <title>Angular: Lightweight Injection Tokenという新しいテクニック</title>
      <dc:creator>Suguru Inatomi</dc:creator>
      <pubDate>Wed, 29 Jul 2020 04:57:17 +0000</pubDate>
      <link>https://dev.to/angular-jp/angular-lightweight-injection-token-4fn2</link>
      <guid>https://dev.to/angular-jp/angular-lightweight-injection-token-4fn2</guid>
      <description>&lt;p&gt;最近Angularチームが発見し、Angularライブラリの実装におけるパターンとして普及させようとしているのが、 &lt;strong&gt;Lightweight Injection Token&lt;/strong&gt; というテクニックだ。これはこれまで不可能だった &lt;strong&gt;コンポーネント(ディレクティブ)のTree-Shaking&lt;/strong&gt; を可能にする。本稿ではこの新しいテクニックの概要、そして生まれた経緯や深く知るための参考リンクをまとめる。&lt;/p&gt;

&lt;p&gt;なお、Lightweight Injection Tokenについては公式ドキュメントでも解説される予定であるため、そちらを参照すればいい部分は省略する。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://next.angular.io/guide/lightweight-injection-tokens" rel="noopener noreferrer"&gt;Angular - Optimizing client app size with lightweight injection tokens&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lightweight Injection Tokenの概要
&lt;/h2&gt;

&lt;p&gt;ひとことでいえば、「オプショナルな機能に関連するInjection Tokenとして代替の軽量トークンを使う」ということである。AngularのDIを深く理解していればこれだけでピンと来るかもしれないが、具体例から概要をつかもう。&lt;/p&gt;

&lt;p&gt;あるAngularライブラリが、次のような使い方ができる &lt;code&gt;&amp;lt;lib-card&amp;gt;&lt;/code&gt; コンポーネントを提供している。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;lib-card&amp;gt;&lt;/span&gt;
  Hello World!
&lt;span class="nt"&gt;&amp;lt;/lib-card&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;このコンポーネントは、Contentとして &lt;code&gt;&amp;lt;lib-card-header&amp;gt;&lt;/code&gt; コンポーネントを配置すると、カードのヘッダーとして取り扱う &lt;strong&gt;オプショナル&lt;/strong&gt; な機能があることをイメージしよう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;lib-card&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;lib-card-header&amp;gt;&lt;/span&gt;Greeting Card&lt;span class="nt"&gt;&amp;lt;/lib-card-header&amp;gt;&lt;/span&gt;
  Hello World!
&lt;span class="nt"&gt;&amp;lt;/lib-card&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ライブラリ側はこのような使い方ができるコンポーネントを実装するとおおよそ次のようになるだろう。 &lt;code&gt;@ContentChild()&lt;/code&gt; を使って &lt;code&gt;CardHeaderComponent&lt;/code&gt; の参照を得る。ただしこのヘッダーを置くかどうかはユーザー次第なので、 &lt;code&gt;CardHeaderComponent|null&lt;/code&gt; という形でnullを許容することになる。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib-card-header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardHeaderComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ContentChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CardHeaderComponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CardHeaderComponent&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ここで問題になるのが、 &lt;code&gt;CardComponent&lt;/code&gt; から &lt;code&gt;CardHeaderComponent&lt;/code&gt; への参照の持ち方である。 &lt;code&gt;@ContentChild(CardHeaderComponent)&lt;/code&gt; と &lt;code&gt;header: CardHeaderComponent|null&lt;/code&gt; の2箇所で参照を持っているが、この2つは性質が異なる。&lt;/p&gt;

&lt;p&gt;後者の &lt;code&gt;header: CardHeaderComponent|null&lt;/code&gt; は、 &lt;strong&gt;型&lt;/strong&gt; としての参照である。この参照はTypeScriptのコンパイル時型チェックにのみ用いられ、コンパイル後のJavaScriptには残らないため問題にならない。&lt;/p&gt;

&lt;p&gt;問題は前者の &lt;code&gt;@ContentChild(CardHeaderComponent)&lt;/code&gt; だ。これは &lt;strong&gt;値&lt;/strong&gt; としての参照であり、 &lt;code&gt;CardHeaderComponent&lt;/code&gt; というクラスオブジェクトそのものを参照している。それが直接 &lt;code&gt;@ContentChild()&lt;/code&gt; デコレーターに渡されているのだから、 &lt;strong&gt;ユーザーがヘッダーを使おうが使わまいが、この参照は実行時に残る&lt;/strong&gt; 。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@ViewChild()&lt;/code&gt; や &lt;code&gt;@ContentChild()&lt;/code&gt; の走査条件として使われるコンポーネント/ディレクティブのクラス参照はどうしてもTree-Shakingできず、これがAngularライブラリを利用したときの &lt;strong&gt;バンドルサイズの肥大化の原因&lt;/strong&gt; となる。&lt;/p&gt;

&lt;p&gt;これを解決するためのアプローチが、Lightweight Injection Tokenだ。上記の例で &lt;code&gt;@ContentChild()&lt;/code&gt; デコレーターに渡していたクラスを、次のように軽量なオブジェクトを利用したInjection Tokenに置き換える。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lightweight Injection Token&lt;/span&gt;
&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardHeaderToken&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib-card-header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CardHeaderToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useExisting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CardHeaderComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardHeaderComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CardHeaderToken&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CardComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ContentChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CardHeaderToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CardHeaderToken&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;まず &lt;code&gt;CardHeaderToken&lt;/code&gt; 抽象クラスを作成し、 &lt;code&gt;CardHeaderComponent&lt;/code&gt; をその具象クラスとする。そしてコンポーネントプロバイダーで &lt;code&gt;CardHeaderToken&lt;/code&gt; に対して自身のクラスオブジェクトを提供する。 &lt;code&gt;CardComponent&lt;/code&gt; ではトークンを &lt;code&gt;@ContentChild()&lt;/code&gt;デコレーターの走査条件とする。&lt;/p&gt;

&lt;p&gt;これにより、 &lt;code&gt;CardComponent&lt;/code&gt; から直接の &lt;code&gt;CardHeaderComponent&lt;/code&gt; への参照はなくなり、ライブラリのユーザーが &lt;code&gt;&amp;lt;lib-card-header&amp;gt;&lt;/code&gt; コンポーネントを呼び出したときだけ &lt;code&gt;CardHeaderToken&lt;/code&gt; に対して &lt;code&gt;CardHeaderComponent&lt;/code&gt; クラスのインスタンスが提供されることになる。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@ContentChild()&lt;/code&gt; や &lt;code&gt;@ViewChild()&lt;/code&gt; の引数としてDIトークンを渡せるようになるのがバージョン 10.1.0からなので、このアプローチが取れるのは &lt;strong&gt;バージョン 10.1.0以降&lt;/strong&gt; になる（ &lt;code&gt;as any&lt;/code&gt; で突破する手法はあるが）。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/angular/commit/97dc85ba5e4eb6cfa741908a04cfccb1459cec9b" rel="noopener noreferrer"&gt;feat(core): support injection token as predicate in queries (#37506) · angular/angular@97dc85b&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  なぜ今なのか、これまでの経緯
&lt;/h2&gt;

&lt;p&gt;この問題は昔からずっと存在したが、実はバージョン8まではそれほど重大な問題ではなかった。なぜかというとバージョン8以前、つまりIvy以前 (ViewEngine, VE) はAOTコンパイルによってテンプレートコンパイルされた結果の生成コードが、もとのコンポーネントとは別のクラス実体をもっていたからだ。&lt;/p&gt;

&lt;p&gt;ViewEngineでは &lt;code&gt;CardComponent&lt;/code&gt; クラスのデコレーターとそのメタデータをもとに &lt;code&gt;CardComponentNgFactory&lt;/code&gt; クラスが生成される。そして、JavaScriptとしてコードサイズが大きいのはほとんどの場合NgFactory側である。&lt;/p&gt;

&lt;p&gt;つまり上記の例でいえば、 たとえ &lt;code&gt;CardComponentNgFactory&lt;/code&gt; クラスが &lt;code&gt;CardHeaderComponent&lt;/code&gt; への参照を持っていたとしても、&lt;code&gt;CardHeaderComponent&lt;/code&gt; そのものが大きくないために問題にならなかったのだ。サイズが大きいのは &lt;code&gt;CardHeaderComponenNgFactory&lt;/code&gt; のほうで、NgFactoryは テンプレート中で &lt;code&gt;&amp;lt;lib-card-header&amp;gt;&lt;/code&gt; を使わない限り参照されないため、不完全ではあるがTree-ShakingできていたのがViewEngine方式だった。&lt;/p&gt;

&lt;p&gt;バージョン9からデフォルトになったIvy方式のAOTコンパイルは、生成コードを &lt;strong&gt;もとのクラスの静的フィールドとして合成する&lt;/strong&gt; 。よって AOTコンパイルすると &lt;code&gt;CardHeaderComponent&lt;/code&gt; そのもののサイズが大きくなり、 &lt;code&gt;CardComponent&lt;/code&gt; に巻き込まれて一緒にバンドルされるサイズが顕著に大きくなる。いままで行なわれていた生成コードのTree-ShakingがIvyによりなくなってしまった。&lt;/p&gt;

&lt;p&gt;つまり、Lightweight Injection TokenはViewEngine時代には顕在化していなかったがIvyによってクリティカルになった問題を解決するために編み出された、 &lt;strong&gt;Ivy時代のAngualrライブラリ実装パターン&lt;/strong&gt; である。&lt;/p&gt;

&lt;p&gt;もっともポピュラーなAngularのコンポーネントライブラリであるAngular Materialではバージョン9リリース時からバンドルサイズの増加が報告されており、その解消の過程でAngularチームが辿り着いた答えである。現在Angular ComponentsチームはAngular Materialの各コンポーネントをLightweight Injection Tokenパターンに置き換える作業を進めている。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/components/issues/19610" rel="noopener noreferrer"&gt;Increased initial main.js bundle size in v9 - mainly due to @angular/material packages · Issue #19610 · angular/components&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/components/issues/19576" rel="noopener noreferrer"&gt;Use light-weight injection pattern for optimized tree-shaking/bundle size · Issue #19576 · angular/components&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  コンポーネント以外のLightweight Injection Token
&lt;/h2&gt;

&lt;p&gt;ところで、 &lt;code&gt;@ContentChild()&lt;/code&gt; などの走査条件でなくとも、通常のDIの中でもオプショナルなものについてはLightweight Injection Tokenパターンを使うべきである。 &lt;code&gt;@Optional()&lt;/code&gt; を使っていてもそのトークンの参照は残るためTree-Shakingはできない。コンストラクタDIでは型注釈部分にしか参照がないためコンパイルすれば消えそうに見えるが、コンストラクタ引数の型注釈はAOTコンパイル時に自動的に &lt;code&gt;@Inject()&lt;/code&gt; デコレーターに変換されるため、実体参照をもつのである。つまりこれも &lt;code&gt;@ContentChild()&lt;/code&gt; と全く同じ構造であり、同じ問題をもちうる。ライブラリ作者であればオプショナルなプロバイダーのトークンは可能な限り軽量にしておくべきだろう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;srv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OptionalService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Same&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OptionalService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;srv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OptionalService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ちなみにコンポーネントのLightweight Injection Tokenとして &lt;code&gt;InjectionToken&lt;/code&gt; オブジェクトを使うこともできるはずだ。公式ドキュメントでは抽象クラスの例が紹介されているが、どちらが定着するかは今後のコミュニティでの受け入れられ方次第だろう。ただ、トークンの抽象クラスとコンポーネントクラスを継承関係にするとそのままコンポーネントのAPI定義として利用もできるため、おそらくは抽象クラスのほうが便利な場面は多そうだ。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CardHeaderToken&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CardHeaderComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CardHeaderComponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://angular.io/guide/dependency-injection-providers#non-class-dependencies" rel="noopener noreferrer"&gt;https://angular.io/guide/dependency-injection-providers#non-class-dependencies&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  参考リンク
&lt;/h2&gt;

&lt;p&gt;以下に参考リンクをまとめる。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Misko HeveryによるDesign Doc &lt;a href="https://hackmd.io/@mhevery/SyqDjUlrU" rel="noopener noreferrer"&gt;https://hackmd.io/@mhevery/SyqDjUlrU&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;公式ドキュメントへの追加PR &lt;a href="https://github.com/angular/angular/pull/36144" rel="noopener noreferrer"&gt;https://github.com/angular/angular/pull/36144&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular MaterialのIssue &lt;a href="https://github.com/angular/components/issues/19576" rel="noopener noreferrer"&gt;https://github.com/angular/components/issues/19576&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>japanese</category>
    </item>
    <item>
      <title>Event Coalescing to improve performance</title>
      <dc:creator>JiaLiPassion</dc:creator>
      <pubDate>Thu, 16 Jan 2020 01:18:04 +0000</pubDate>
      <link>https://dev.to/angular-jp/event-coalescing-to-improve-performance-1ca6</link>
      <guid>https://dev.to/angular-jp/event-coalescing-to-improve-performance-1ca6</guid>
      <description>&lt;h1&gt;
  
  
  Event Bubbling の性能の問題
&lt;/h1&gt;

&lt;p&gt;下記のAngularアプリケーションでのHTMLテンプレートコードを見ましょう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"doSomething()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"doAnotherThing()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Button&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture"&gt;Event Bubbling&lt;/a&gt; のため、DivのなかのButtonをクリックしたら、両方のEvent ListenerがTriggerされます。この２つのEvent Listenerが全部Change Detectionを実行します。実際はこのようなケースでChange Detectionを一回だけ実行したいです。特にこのようなケースがAngular MaterialとかUI ライブラリで結構普通のケースですので、性能改善したいです。&lt;/p&gt;

&lt;h1&gt;
  
  
  実行順番
&lt;/h1&gt;

&lt;p&gt;まず上記のケースでEvent ListenerとChange Detectionの実行順番を見ましょう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// event listener for parent div&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event listener for parent div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;doAnotherThing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// event listener for inner button&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event listener for inner button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;そして、Zone.jsがEvent ListenerをMonkey patchされましたので、下記のような感じのコードになりました。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;realHandler&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt; &lt;span class="c1"&gt;// doSomething あるいはdoAnotherThingのDelegate&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;applicationRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;そしたら、Buttonをクリックするとき、実行の順番が下記のようになりました。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event listener for inner button
applicationRef.tick
event listener for parent div
applicationRef.tick
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;実際ほしいのは&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event listener for inner button
event listener for parent div
applicationRef.tick
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;です。&lt;/p&gt;

&lt;h2&gt;
  
  
  解決方法
&lt;/h2&gt;

&lt;p&gt;Change Detectionを同期ではなく、非同期で実行することです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;isChangeDetectionScheduled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;realHandler&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt; &lt;span class="c1"&gt;// doSomething あるいはdoAnotherThingのDelegate&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isChangeDetectionScheduled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;isChangeDetectionScheduled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;isChangeDetectionScheduled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;applicationRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;のような感じのコードでChange DetectionをrequestAnimationFrameのSchedulerで実行させ、そしてもしScheduleされたTaskがあったら、スキップして、なかったら、Scheduleするということです。&lt;/p&gt;

&lt;h2&gt;
  
  
  設定方法
&lt;/h2&gt;

&lt;p&gt;bootstrapModuleのとき、かきのOptionを設定することができます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;platformBrowserDynamic&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;bootstrapModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;ngZoneEventCoalescing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  副作用
&lt;/h2&gt;

&lt;p&gt;このオプションをTrueにしたら、もともと同期のChange Detectionが非同期になりました、普通のアプリケーションには影響がないですが、Google 内部でのEdge Caseで同期のChange Detectionが求めるテストケースがあるらしくて、それ以外が特に既存のアプリケーションには影響ないはずです。&lt;br&gt;
この機能がすでに最新バージョンで使えますので、なにか問題が発見されたら、ぜひAngular RepoにIssueを提出ください。&lt;/p&gt;

&lt;p&gt;以上です。&lt;/p&gt;

&lt;p&gt;どうもありがとうございました！&lt;/p&gt;

</description>
      <category>zonejs</category>
      <category>angular</category>
      <category>eventlistener</category>
      <category>performance</category>
    </item>
    <item>
      <title>なぜentryComponentsは非推奨になるのか</title>
      <dc:creator>Suguru Inatomi</dc:creator>
      <pubDate>Mon, 04 Nov 2019 03:45:08 +0000</pubDate>
      <link>https://dev.to/angular-jp/entrycomponents-53mo</link>
      <guid>https://dev.to/angular-jp/entrycomponents-53mo</guid>
      <description>&lt;p&gt;この記事では、Angular v9.0にて非推奨となる &lt;code&gt;entryComponents&lt;/code&gt; 機能が、なぜ非推奨になるのかについてできるだけ簡単に解説します。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://next.angular.io/guide/deprecations#entrycomponents-and-analyze_for_entry_components-no-longer-required" rel="noopener noreferrer"&gt;Angular - Deprecated APIs and Features&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  はじめに
&lt;/h2&gt;

&lt;p&gt;解説を始める前に、重要な点をあらかじめ書き記しておきます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;もしIvyをオプトアウトする場合は、 &lt;code&gt;entryComponents&lt;/code&gt; は引き続き必要です。決して削除しないでください。&lt;/li&gt;
&lt;li&gt;いままで &lt;code&gt;entryComponents&lt;/code&gt; 機能を使ったことがない方が新たになにか覚える必要はありません。興味がなければ過去のものとして無視してください。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;entryComponents&lt;/code&gt; とは何なのか
&lt;/h2&gt;

&lt;p&gt;v9.0 で非推奨となる &lt;code&gt;entryComponents&lt;/code&gt; とは何だったのかということをまずは振り返りましょう。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;entryComponents&lt;/code&gt; は多くの場合、 &lt;strong&gt;動的なコンポーネント&lt;/strong&gt; を実現するために利用されます。動的なコンポーネントとは、AngularのテンプレートHTML内に登場せず、コードの実行によって生成されるコンポーネントです。テンプレートHTMLを静的に検査しても宣言が見つからないことから &lt;strong&gt;動的&lt;/strong&gt; と呼ばれます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://angular.jp/guide/dynamic-component-loader" rel="noopener noreferrer"&gt;Angular - 動的コンポーネントローダー&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;もっとも代表的なユースケースはダイアログやモーダルのようなケースです。コンポーネントクラスの処理が実行されることで動的にコンポーネントが表示されます。このようなコンポーネントはテンプレートHTML内に宣言されません。&lt;/p&gt;

&lt;p&gt;たとえばAngular CDKの&lt;a href="https://material.angular.io/cdk/overlay/overview" rel="noopener noreferrer"&gt;Overlay API&lt;/a&gt;を使ってコンポーネントをオーバーレイ上に表示するには次のようなコードを書きます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="nf"&gt;openModal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overlayRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modalPortal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ComponentPortal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyModalComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modalPortal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;このとき、動的に表示したい &lt;code&gt;MyModalComponent&lt;/code&gt; は、 それが宣言される &lt;code&gt;NgModule&lt;/code&gt; の &lt;code&gt;entryComponents&lt;/code&gt; 配列に追加される必要があります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyModalComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;entryComponents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MyModalComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  なぜ &lt;code&gt;entryComponents&lt;/code&gt; が必要なのか
&lt;/h2&gt;

&lt;p&gt;Angularに慣れている人にとっては、もはや当たり前のように「モーダルを実装するときは &lt;code&gt;entryComponents&lt;/code&gt; 」というルーチンになってしまっているかもしれませんが、そもそもなぜこれが必要なのでしょうか。その理由は、Angular v8までのAoTテンプレートコンパイラと、そのテンプレートコンパイラが生成する実行コードに理由があります。&lt;/p&gt;

&lt;p&gt;ここで以降の説明の簡単のため、v8以前のAoTコンパイラを &lt;strong&gt;ViewEngine&lt;/strong&gt; (VE) コンパイラと呼びます。&lt;/p&gt;

&lt;h3&gt;
  
  
  コンポーネントの生成とComponentFactory
&lt;/h3&gt;

&lt;p&gt;動的コンポーネントの生成には &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; というAPIを使います。このAPIはコンポーネントクラスから、そのコンポーネントに対してAoTコンパイラが生成した &lt;strong&gt;ComponentFactory&lt;/strong&gt; オブジェクトを返すものです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cfr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFactoryResolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;componentFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cfr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolveComponentFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SomeComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;先ほど紹介したCDKのOverlayやPortalの機能も、この &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; を利用しています。そして、 &lt;code&gt;entryComponents&lt;/code&gt; に追加されたコンポーネントだけがこの &lt;code&gt;resolveComponentFactory&lt;/code&gt; メソッドの引数に使えます。もし追加されていなければ次のようなエラーが表示されます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzy7c5q5wkm1myo9uw6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzy7c5q5wkm1myo9uw6i.png" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;つまり、 &lt;code&gt;entryComponents&lt;/code&gt; とは、「あるコンポーネントのComponentFactoryを解決可能にする」ための機能であると言えます。ではなぜ &lt;code&gt;entryComponents&lt;/code&gt; に追加されていないコンポーネントのComponentFactoryは解決できないのでしょうか。すべてのコンポーネントは等価ではないのでしょうか？&lt;/p&gt;

&lt;h3&gt;
  
  
  ViewEngineはTree-shakableなComponentFactoryを生成する
&lt;/h3&gt;

&lt;p&gt;その答えは半分YESです。ViewEngineのAoTコンパイラは &lt;code&gt;NgModule.declarations&lt;/code&gt; 配列に指定されたすべてのコンポーネントのComponentFactoryを生成しています。しかし、それが &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; から解決可能になっていないのです。&lt;/p&gt;

&lt;p&gt;この様子は実際にAoTコンパイルの結果を見るとはっきりとわかります。Angular CLIのプロジェクトであれば、 &lt;code&gt;ngc -p ./tsconfig.app.json&lt;/code&gt; とコマンドを実行すれば &lt;code&gt;tsc-out&lt;/code&gt; ディレクトリにAoTコンパイル結果が出力されます。その中には、すべてのコンポーネントに対して &lt;code&gt;./some.component.ngfactory.js&lt;/code&gt; のような ComponentFactoryの生成コードを見ることができます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhggy68ozlu4b3je6f3bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhggy68ozlu4b3je6f3bx.png" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;これでViewEngineではどのコンポーネントにもComponentFactoryは存在していることがわかります。しかし、これらのComponentFactoryは &lt;strong&gt;どこからも参照されていません&lt;/strong&gt;。つまりAngular CLI（の内部で使われているwebpack）のビルドでは、不要なコードとしてバンドルに含められないのです。これが、&lt;code&gt;ComponentFactoryResolver&lt;/code&gt; によってComponentFactoryを解決できないコンポーネントがある理由です。バンドルサイズ削減のために、不要なコードを含めない仕組みになっているのです。&lt;/p&gt;

&lt;h3&gt;
  
  
  ComponentとComponentFactoryの分断
&lt;/h3&gt;

&lt;p&gt;しかしこれはおかしい話です。ソースコード中で &lt;code&gt;SomeComponent&lt;/code&gt; を参照しているのだからそのComponentFactoryは必要なコードとしてバンドルに含められるべきです。&lt;/p&gt;

&lt;p&gt;ここがViewEngineの限界です。ViewEngineのAoTコンパイラはComponentFactoryの生成コードを元のコンポーネントクラスとは別のファイルに出力します。つまり、 &lt;code&gt;some.component.ts&lt;/code&gt; に対して &lt;code&gt;some.component.js&lt;/code&gt; と &lt;code&gt;some.component.ngfactory.js&lt;/code&gt; を出力します。したがって、アプリケーションで &lt;code&gt;SomeComponent&lt;/code&gt; への参照があったとしても、 &lt;code&gt;SomeComponent&lt;/code&gt; のComponentFactoryには一切参照が届かないのです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gz0xare75w0zjqhgn5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gz0xare75w0zjqhgn5a.png" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;entryComponents&lt;/code&gt; は &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; をセットアップする
&lt;/h3&gt;

&lt;p&gt;ここでようやく &lt;code&gt;entryComponents&lt;/code&gt; の出番です。 &lt;code&gt;NgModule&lt;/code&gt; の &lt;code&gt;entryComponents&lt;/code&gt; に追加されたコンポーネントのComponentFactoryは、AoTコンパイラが特別に解釈して &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; で解決できるように参照を作ります。その様子は AoTコンパイル後の &lt;code&gt;app.module.ngfactory.js&lt;/code&gt; で見ることができます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifakkkladd5jao4d55p2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifakkkladd5jao4d55p2.png" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AoTコンパイルの生成コードをはじめて見る方は驚くかもしれませんが、今回注目すべき点は2ヶ所。インポート文と &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; のプロバイダ宣言です。見ての通り、 &lt;code&gt;AppModuleNgFactory&lt;/code&gt; から参照されているのは &lt;code&gt;app.component.ngfactory&lt;/code&gt; だけです。そして、 &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; の近くにある配列には &lt;code&gt;AppComponentNgFactory&lt;/code&gt; だけがセットされています。&lt;/p&gt;

&lt;p&gt;それでは、 &lt;code&gt;SomeComponent&lt;/code&gt; を &lt;code&gt;entryComponents&lt;/code&gt; 配列に追加してもう一度AoTコンパイルしてみましょう。 &lt;code&gt;app.module.ngfactory.js&lt;/code&gt; に変化があるはずです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dvc83l4slmw12dsg1y7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dvc83l4slmw12dsg1y7.png" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;新たに &lt;code&gt;some.component.ngfactory&lt;/code&gt; への参照が追加され、&lt;code&gt;ComponentFactoryResolver&lt;/code&gt; の近くにある配列に &lt;code&gt;SomeComponentNgFactory&lt;/code&gt; が追加されています。実はこの配列こそが &lt;code&gt;ComponentFactoryResolver&lt;/code&gt;が解決できるコンポーネントのリストです。&lt;/p&gt;

&lt;p&gt;つまり、&lt;code&gt;entryComponents&lt;/code&gt; によって &lt;code&gt;NgModule&lt;/code&gt; のコンパイル結果に影響を与えることで、動的に利用したいコンポーネントのComponentFactoryがTree-shakingされないように、&lt;code&gt;ComponentFactoryResolver&lt;/code&gt; から解決可能な参照を保持することができるのです。&lt;/p&gt;

&lt;h2&gt;
  
  
  なぜ &lt;code&gt;entryComponents&lt;/code&gt; が非推奨になるのか
&lt;/h2&gt;

&lt;p&gt;ViewEngineにおいて &lt;code&gt;entryComponents&lt;/code&gt; がなぜ必要だったかを簡単に説明しましたが、なぜv9からは非推奨となるのでしょうか。それはViewEngineに変わるAngularの新しい &lt;strong&gt;Ivy&lt;/strong&gt;コンパイラがViewEngineの抱える問題を根本から解決したからです。&lt;/p&gt;

&lt;h3&gt;
  
  
  Ivyは同一ファイルにコード生成する
&lt;/h3&gt;

&lt;p&gt;IvyのAoTコンパイラは元のコンポーネントファイルと同じファイル、しかも同じクラスの静的フィールドとしてコード生成します。実際にAoTコンパイル結果を見てみましょう。v9では次のような生成コードになります。IvyではAoTコンパイルによって追加される独自のファイルは一切ありません。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhm6hnxhszetbopt1xdtc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhm6hnxhszetbopt1xdtc.png" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;some.component.js&lt;/code&gt; は次のようになっています。3行目にあるのは元の &lt;code&gt;SomeComponent&lt;/code&gt; から &lt;code&gt;@Component&lt;/code&gt; デコレーターが除去されたクラスです。そしてデコレーターの中に定義されていたセレクターやテンプレートなどのメタデータが、 9行目以降のAoTコンパイラによる生成コードに変換されています。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tt7v5gv7r59w9t7xwy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tt7v5gv7r59w9t7xwy4.png" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ここで重要なことは、 &lt;code&gt;SomeComponent&lt;/code&gt; のAoTコンパイル後コードが、 &lt;code&gt;SomeComponent&lt;/code&gt; クラスと密に結合していることです。これにより、 &lt;code&gt;SomeComponent&lt;/code&gt; を参照すれば自動的に &lt;code&gt;SomeComponent&lt;/code&gt; のコンポーネント生成に必要なすべての情報を解決できます。&lt;/p&gt;

&lt;p&gt;つまり、 &lt;code&gt;app.module.js&lt;/code&gt; で &lt;code&gt;some.component.js&lt;/code&gt; をインポートしているだけで、 &lt;code&gt;SomeComponent&lt;/code&gt; のComponentFactoryは解決可能になるのです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes9mle5ytngo7llgdyr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes9mle5ytngo7llgdyr8.png" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;これが、 Angular v9でIvyによって &lt;code&gt;entryComponents&lt;/code&gt; が非推奨になる理由です。 &lt;code&gt;entryComponents&lt;/code&gt; の代替となる新たな方法に変わるのではなく、そもそも根本的に&lt;strong&gt;動的コンポーネントと静的コンポーネントを区別する必要がなくなる&lt;/strong&gt;のです。&lt;/p&gt;

&lt;h3&gt;
  
  
  Tree-shakingの問題は？
&lt;/h3&gt;

&lt;p&gt;ここまで読んだ方はもしかすると &lt;code&gt;entryComponents&lt;/code&gt; がなくなることで、ViewEngineと比べてバンドルサイズが増えるのではないかと疑っているかもしれません。確かに、コンポーネントの生成コードだけを考えると、ViewEngineと比べてTree-shaking可能な領域は減っています。しかしIvyではその他のいくつもの改善によってトータルではほとんどのユースケースでバンドルサイズが削減されます。&lt;/p&gt;

&lt;p&gt;もっとも大きな改善は、Angularのテンプレート機能がTree-shakableになることです。詳細は割愛しますが、 &lt;code&gt;[prop]="someValue"&lt;/code&gt; や &lt;code&gt;(eventName)="onEvent($event)"&lt;/code&gt; など、すべてのテンプレートの機能が個別にTree-shakingされます。アプリケーションで一度も使わなかったテンプレート機能はバンドルに含まれません。&lt;/p&gt;

&lt;p&gt;また、コンポーネントと生成コードが同一ファイルになることでクラス定義やimport/exportのオーバーヘッドもなくなり、より少ないコードだけを生成すればよくなりました。また、ViewEngineではコンポーネントが子コンポーネントになる場合とホストコンポーネントになる場合で別の生成関数を定義していましたが、Ivyではひとつの生成関数に統合されるので、これによっても生成コードのサイズは減っています。&lt;/p&gt;

&lt;p&gt;トレードオフはありつつも、Ivyでは差分コンパイルのスピード、バンドルサイズの削減、内部アーキテクチャの単純化などの複合的な視点で、Ivyのアーキテクチャを選択しています。&lt;/p&gt;

&lt;h3&gt;
  
  
  動的コンポーネントを超えた遅延コンポーネントへ
&lt;/h3&gt;

&lt;p&gt;IvyのAoTコンパイラは同一クラスの静的フィールドにComponentFactoryを生成すると説明しました。この変更による恩恵は &lt;code&gt;entryComponents&lt;/code&gt; が不要になるだけではありません。ひとつのコンポーネントに関するコードが1ファイルに含まれることで、Dynamic Importによるコンポーネントの遅延読み込みも将来的には可能になります。&lt;/p&gt;

&lt;p&gt;つまり、次のように動的な &lt;code&gt;import()&lt;/code&gt; 文で取得した &lt;code&gt;SomeComponent&lt;/code&gt; クラスでも &lt;code&gt;ComponentFactoryResolver&lt;/code&gt; で解決できるということです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cfr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFactoryResolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./some/some.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cfr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolveComponentFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SomeComponent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someCompFactory&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someCompFactory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IvyではすべてのコンポーネントのComponentFactoryがバンドルに含められると説明しましたが、それはテンプレートHTMLやTypeScriptコードの中で &lt;strong&gt;静的に&lt;/strong&gt; 参照されている場合だけです。もし &lt;code&gt;SomeComponent&lt;/code&gt; がこの Dynamic Import以外でまったく参照されていなければ、 Angular CLIは &lt;code&gt;SomeComponent&lt;/code&gt; そのものを別バンドルに分離し、遅延読み込み可能にします。モーダル用のコンポーネントであれば、初期読込されるJavaScriptにはコンポーネントを含めず、モーダルを表示するイベントが発生したときに初めて遅延読み込みすればいいわけです。&lt;/p&gt;

&lt;p&gt;このようにViewEngineからIvyにアーキテクチャ変更したことによって、いままでは覚えるしかなかった「Angularではできない」や「Angularではこのようにする」といった慣例的な制約がいくつも取り払われていきます。そして不要になった（陳腐化した）APIは非推奨となっていきます。&lt;/p&gt;

&lt;p&gt;非推奨化は必ずしも代替APIへの置き換えを意味するわけではないということを覚えておきましょう。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;v8までのViewEngineでは &lt;code&gt;entryComponents&lt;/code&gt; が無ければComponentFactoryの解決ができなかった&lt;/li&gt;
&lt;li&gt;Ivyではすべてのコンポーネントが常にComponentFactoryを保持しているため、いつでもどのコンポーネントも動的に利用できるようになる&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entryComponents&lt;/code&gt; の非推奨化は代替APIへの置き換えではなく、そもそも動的コンポーネントと静的コンポーネントの区別が不要になったということである&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ivyについての詳しい話は、 AngularConnect 2019での次のセッションをおすすめします。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=anphffaCZrQ&amp;amp;list=PLAw7NFdKKYpE-f-yMhP2WVmvTH2kBs00s" rel="noopener noreferrer"&gt;Deep Dive into the Angular Compiler | Alex Rickabaugh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=S0o-4yc2n-8&amp;amp;list=PLAw7NFdKKYpE-f-yMhP2WVmvTH2kBs00s&amp;amp;index=26" rel="noopener noreferrer"&gt;How Angular works | Kara Erickson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=EqSRpkMRyY4&amp;amp;list=PLAw7NFdKKYpE-f-yMhP2WVmvTH2kBs00s&amp;amp;index=10" rel="noopener noreferrer"&gt;How we make Angular fast | Miško Hevery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=nQ8oJ1rpwIc&amp;amp;list=PLAw7NFdKKYpE-f-yMhP2WVmvTH2kBs00s&amp;amp;index=7" rel="noopener noreferrer"&gt;The secrets behind Angular’s lightning speed | Max Koretskyi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>ivy</category>
    </item>
  </channel>
</rss>
