|
1 | | -Chapter 7: Prod Deployment |
| 1 | +Chapter 7: Production Deployment |
2 | 2 | ========================== |
3 | 3 |
|
4 | | -WIP... |
| 4 | +# Running the Sample App |
| 5 | + |
| 6 | +Before running the app, make sure you run the code generators (see below for |
| 7 | +more info) like so: |
| 8 | + |
| 9 | +``` |
| 10 | +dart -c bin/generator.dart |
| 11 | +``` |
| 12 | + |
| 13 | +# Overview |
| 14 | + |
| 15 | +When deploying your app in production you need to make sure that: |
| 16 | + |
| 17 | +1. compiled ([dart2js][dart2js]) output is small |
| 18 | +1. application performs as well in JavaScript as it does in Dart VM |
| 19 | +1. application runs not only in Chrome, but other supported modern browsers |
| 20 | + |
| 21 | +AngularDart and di heavily rely on [dart:mirrors][dart-mirrors-api] APIs. |
| 22 | +Mirrors allow AngularDart to provide super fast developer friendly edit-refresh |
| 23 | +cycle, without needing to run slow compilers and code generators. |
| 24 | +However, mirrors come at a cost: |
| 25 | + |
| 26 | +1. use of mirrors disables very important optimizations performed by |
| 27 | + [dart2js][dart2js] compiler, such as tree-shaking, which allows removal |
| 28 | + of unused code from the output, resulting in very large JavaScript file. |
| 29 | +1. mirrors are much slower compared to static Dart code, which might not be |
| 30 | + an issue for smaller/medium applications, but in larger apps might become |
| 31 | + noticeable. Dart team is constantly working on improving performance of |
| 32 | + mirrors, so long-term it's not a problem, but in short-term it's something |
| 33 | + you might need to think about. |
| 34 | + |
| 35 | +Here we will provide some tip on these subjects. |
| 36 | + |
| 37 | +# Managing Compiled Code Size |
| 38 | + |
| 39 | +## Minification |
| 40 | + |
| 41 | +dart2js allows you to minify the resulting JavaScript, which: |
| 42 | + |
| 43 | +* removes unnecessary whitespace |
| 44 | +* shortens the class and field names |
| 45 | + |
| 46 | +Minification can reduce your resulting JavaScript by 2-3x. |
| 47 | + |
| 48 | +All you need to do, is to include ```--minify``` flag in dart2js command line. |
| 49 | + |
| 50 | +``` |
| 51 | +dart2js web/main.dart --minify -o web/main.dart.js |
| 52 | +``` |
| 53 | + |
| 54 | +## ```@MirrorsUsed``` |
| 55 | + |
| 56 | +To help manage the code size of applications that use mirrors, Dart provides |
| 57 | +[```@MirrorsUsed```][mirrors-used] annotation using which you can tell dart2js |
| 58 | +compiler which targets (classes, libraries, annotations, etc.) are being |
| 59 | +reflected on. This way dart2js can skip all the unused stuff thus radically |
| 60 | +reducing the output size. |
| 61 | + |
| 62 | +```@MirrorsUsed``` is often hard to get right as it really depends on how/if |
| 63 | +you use code generation (discussed later in "Optimizing Runtime Performance" |
| 64 | +chapter). Assuming you do use code generation (as we do in this chapter) your |
| 65 | +annotation could look something like this: |
| 66 | + |
| 67 | +``` |
| 68 | +@MirrorsUsed( |
| 69 | + targets: const [ |
| 70 | + 'angular.core', |
| 71 | + 'angular.core.dom', |
| 72 | + 'angular.core.parser', |
| 73 | + 'angular.routing', |
| 74 | + NodeTreeSanitizer |
| 75 | + ], |
| 76 | + metaTargets: const [ |
| 77 | + NgInjectableService, |
| 78 | + NgComponent, |
| 79 | + NgDirective, |
| 80 | + NgController, |
| 81 | + NgFilter, |
| 82 | + NgAttr, |
| 83 | + NgOneWay, |
| 84 | + NgOneWayOneTime, |
| 85 | + NgTwoWay, |
| 86 | + NgCallback |
| 87 | + ], |
| 88 | + override: '*' |
| 89 | +) |
| 90 | +import 'dart:mirrors'; |
| 91 | +``` |
| 92 | + |
| 93 | +Here you are essentually telling dart2js that your application reflects on |
| 94 | +```angular.core```, ```angular.core.dom```, ```angular.core.parser```, etc. |
| 95 | +libraries, as well as on ```NodeTreeSanitizer``` class, and annotations |
| 96 | +(metaTargets) like ```NgInjectableService```, ```NgComponent```, |
| 97 | +```NgDirective```, etc. |
| 98 | + |
| 99 | +### Debugging |
| 100 | + |
| 101 | +If it happens that you have misconfigured ```@MirrorsUsed```, you will likely |
| 102 | +be seeing errors like "Cannot find class for: Foo" or your |
| 103 | +directives/components/controllers will be ignored when running in JavaScript. |
| 104 | +Usually, the easiest fix is to just add that class (or the whole library) |
| 105 | +to ```@MirrorsUsed.targets```. |
| 106 | + |
| 107 | + |
| 108 | +# Optimizing Runtime Performance |
| 109 | + |
| 110 | +Currently there are two code generators: di and AngularDart Parser generators. |
| 111 | + |
| 112 | +## di Code Generator |
| 113 | + |
| 114 | +di.dart Injector uses dart:mirrors APIs for retrieving types of constructor |
| 115 | +parameters and invoking the constructor to create new instances. The generator |
| 116 | +generates static code for creating new instances and resolving dependencies. |
| 117 | + |
| 118 | +You can find an example of how to use the di generator in |
| 119 | +```bin/generator.dart``` file. |
| 120 | + |
| 121 | +## AngularDart Parser Generator |
| 122 | + |
| 123 | +AngularDart Parser Generator extracts all expressions from your application |
| 124 | +and then compiles them into Dart, so at runtime it doesn't have to parse those |
| 125 | +expressions and while invoking the expressions it uses pre-generated code to |
| 126 | +access fields and methods, so it doesn't have to use mirrors. |
| 127 | + |
| 128 | +You can find an example of how to use the parser generator in |
| 129 | +```bin/generator.dart``` file. |
| 130 | + |
| 131 | +## Code Generators and Development Mode |
| 132 | + |
| 133 | +You should not be using code generators during development, as they are slow |
| 134 | +and can significanly degrade productivity. Instead, during development it's |
| 135 | +better to use dynamic versions of di Injector and Parser and use generators |
| 136 | +only for testing and production. |
| 137 | + |
| 138 | +In ```lib/main.dart``` you can see ```initializer-prod.dart``` file being |
| 139 | +imported, which has ```initializer-dev.dart``` counterpart. Switching between |
| 140 | +those two file will allow you to switch between prod and dev modes. You will |
| 141 | +need to run the generator script before using the prod mode. |
| 142 | + |
| 143 | +It is highly recommended that you automate (via a script or a flag on the |
| 144 | +server) the prod/dev mode switching to minimize the chance of dev mode being |
| 145 | +released into production. |
| 146 | + |
| 147 | +# Cross-browser Support |
| 148 | + |
| 149 | +Angular components use [Shadow DOM][shadowdom101], but unfortunately it's not |
| 150 | +natively supported in all modern browsers, so you would need to use a |
| 151 | +[polyfill][shadow-dom-polyfill]. |
| 152 | + |
| 153 | +Make sure you include ```shadow_dom``` package in dependencies in pubspec.yaml. |
| 154 | + |
| 155 | +``` |
| 156 | +dependencies: |
| 157 | + shadow_dom: any |
| 158 | +``` |
| 159 | + |
| 160 | +And include the script tag: |
| 161 | + |
| 162 | +``` |
| 163 | +<script src="packages/shadow_dom/shadow_dom.debug.js"></script> |
| 164 | +``` |
| 165 | + |
| 166 | +or the debug version: |
| 167 | + |
| 168 | +``` |
| 169 | +<script src="packages/shadow_dom/shadow_dom.debug.js"></script> |
| 170 | +``` |
| 171 | + |
| 172 | +**NOTE:** using the polyfill has [some limitations][shadowdom-limitations], |
| 173 | +so make sure you are aware of those limitations before you start using it. |
| 174 | + |
| 175 | +[dart-mirrors-api]: https://api.dartlang.org/docs/channels/stable/latest/dart_mirrors.html |
| 176 | +[shadowdom101]: http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/ |
| 177 | +[shadowdom-limitations]: https://github.com/polymer/ShadowDOM#known-limitations |
| 178 | +[shadow-dom-polyfill]: http://pub.dartlang.org/packages/shadow_dom |
| 179 | +[dart2js]: https://www.dartlang.org/docs/dart-up-and-running/contents/ch04-tools-dart2js.html |
| 180 | +[mirrors-used]: https://api.dartlang.org/docs/channels/stable/latest/dart_mirrors/MirrorsUsed.html |
0 commit comments