私のプログラミング備忘録

spring, docker, aws etc.

angular.ioのaot-compilerを読む

はじめに

index.html

  • AOTとroolupは時間がかかるので、開発時はJIT、本番ではAOTに分ける必要があります。src/index.htmlをaot配下にコピーして、aot/index.htmlに対して変更を加えていきます。
  • バンドルのロードにSystemJSのようなモジュールローダーを必要としないので、aot/index.htmlから不要部分を削除します。
    <script src="node_modules/systemjs/dist/system.src.js"></script>

    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
  • 代わりにバンドルファイルをロードするのでbodyタグの閉じタグの後にscriptタグを挿入します。
<script src="dist/build.js"></script>

main.ts

  • AOTとJITは大部分は同じように起動しますが、bootstrapが大きく異なります。JITではAppModuleによるbootstrapではなく、tsconfig-aotに沿ってコンパイルされる時に出力されるNgFactoryファイル群によってbootstrapされます。
  • main.tsと同階層にmain-aot.tsを作成し、NgFactoryをimportします。
import { platformBrowser }    from '@angular/platform-browser';
import { AppModuleNgFactory } from '../aot/src/app/app.module.ngfactory';

platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

typescript configuration

  • AOTではTree Shaking(のRollup)でimport/exportをトレースするのでes2015(es6)でトランスパイルされる必要があるのに対して、JITではcommonJSでトランスパイルされるのが好ましいので、configファイルは明示的に分けるべきです。
  • tsconfig-aot.jsonとして、以下のように準備します。
{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [ "es2015", "dom" ],
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true,
    "typeRoots": [
      "./node_modules/@types/"
    ]
  },

  "files": [
    "src/app/app.module.ts",
    "src/main-aot.ts"
  ],

  "angularCompilerOptions": {
   "genDir": "aot",
   "skipMetadataEmit" : true
 }
}

tree shaking

  • tree shakingの1つとしてRollUpが実行されます。
  • configファイルとしてrollup-config.jsを以下のように設定し、ルート配下に配置します。
// import rollup      from 'rollup'
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs    from 'rollup-plugin-commonjs';
import uglify      from 'rollup-plugin-uglify'

export default {
  entry: 'src/main-aot.js',
  dest: 'aot/dist/build.js', // output a single application bundle
  sourceMap: true,
  sourceMapFile: 'aot/dist/build.js.map',
  format: 'iife',
  onwarn: function(warning) {
    // Skip certain warnings

    // should intercept ... but doesn't in some rollup versions
    if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }

    // console.warn everything else
    console.warn( warning.message );
  },
  plugins: [
      nodeResolve({jsnext: true, module: true}),
      commonjs({
        include: 'node_modules/rxjs/**',
      }),
      uglify()
  ]
}

Running the application

パッケージのインストー

  • platform-serverがなければ入れる。
npm install @angular/compiler-cli @angular/platform-server --save
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev

copy-dist-files

  • AOT配布ファイルをaot/配下にコピーするスクリプトを作成します。
var fs = require('fs');
var resources = [
  'node_modules/core-js/client/shim.min.js',
  'node_modules/zone.js/dist/zone.min.js',
  'src/styles.css'
];
resources.map(function(f) {
  var path = f.split('/');
  var t = 'aot/' + path[path.length-1];
  fs.createReadStream(f).pipe(fs.createWriteStream(t));
});
  • pakcage.jsonのcopy-dist-filesを実行します。
node copy-dist-files

起動

  • AOTコンパイル、Rollup、lite-serverによる起動を行います。
npm run build:aot && npm run serve:aot

上記のコマンド内を1つずつ見ていきます。

AOTコンパイル
  • typescriptコンパイラ(tsc)の代わりに、@angular/compiler-cliで提供されるngcコンパイラで実行します
    • -pオプションは設定ファイルの指定
    • 設定ファイルのgendirで指定したフォルダにhoge.ngfactory.tsができる
      • JITではインメモリに展開されるけど、AOTでは物理ファイルとして明示的に作成される
node_modules/.bin/ngc -p tsconfig-aot.json
Rollup実行
node_modules/.bin/rollup -c rollup-config.js
lite-server
  • bs-config.aot.jsonにある通り8080ポートで起動されます。
lite-server -c bs-config.aot.json

tree shaking補足

  • tree shakingはAOTコンパイラによってセットされたステージ上のファイルを全検索して不要なコードを除きます。これによってアプリケーションサイズが小さくなります。

Rollup

  • tree shakingのプラグイン
  • import/exportのトレースによるアプリケーションの静的解析ユーティリティ
  • 再記になるがtsconfig-aot.jsonでmoduleにes2015を指定したのは、requireではなくimport/exportが使われている必要があるから
  • Rollupユーティリティをインストー
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev
  • rollup-config.js(rollupの設定ファイル)をルート配下に作成します。内容はとりあえずサンプル通り何ですが、1行目のコードは不要なのでコメントアウトする。
// import rollup      from 'rollup'

これをしないと、以下の警告が出る

'default' is imported from external module 'rollup' but never used

RxJS
  • tree shakingのプラグイン
  • Rollupはes2015でコーディングされている前提で動作するけど、公開されている依存ライブラリの多くはcommonjsで実装されている。そういったライブラリの変換を行うためのプラグイン
Minification
  • tree shakingのプラグイン
  • gzipに圧縮することで、アプリケーションサイズが小さくできる