angular-cn/TOOLS_DART.md

5.4 KiB

Developer Tools for Dart

Here you will find a collection of tools and tips for keeping your application perform well and contain fewer bugs.

Code size

Code needs to be downloaded, parsed and executed. Too much code could lead to slow application start-up time, especially on slow networks and low-end devices. The tools below will help you identify contributors to code size and keep them in check.

Finding contributors to code size

--dump-info

dart2js has an option --dump-info that outputs information about what happened during compilation. Enable this option in your transformer options like this:

transformers:
...
- $dart2js:
    commandLineOptions:
    - --dump-info

Use the visualizer to analyze the output or any of the command-line tools documented here.

ng2soyc.dart

ng2soyc is a utility for analyzing code size contributors in Angular 2 applications. It groups code size by library. It also assumes your library names follow "package.library.sub-library..." convention and gives code size breakdown at each level. To reduce noise in the output (for very large apps) it also provides an option to hide libraries that are too small, so you can focus on the biggest contributors.

Track unused reflection data

Call reflector.trackUsage() to cause it to track reflection information used by the application. Reflection information (ReflectionInfo) is a data structure that stores information about your application that Angular uses for locating DI factories, generated change detectors and other code related to a given type. After exercising your application, call reflector.listUnusedKeys() to get a list of types and functions whose reflection information was retained but was never used by the application.

Use code coverage to find dead code

When running in Dartium (or in Dart VM in general) you can request code coverage information from the VM. You can either use observatory, or download the coverage file and use your own tools to inspect it. Lines of code that are not covered are top candidates for dead code.

Keep in mind, however, that uncovered code is not sufficient evidence of dead code, only necessary evidence. It is perfectly possible that you simply didn't exercise your application in a way that triggers the execution of uncovered code. A common example is error handling code. Just because your testing never encountered an error does not mean the error won't happen in production. You therefore do not have to rush and remove all the catch blocks.

Reducing code size

Disable reflection

dart:mirrors allows discovering program metadata at runtime. However, this means that dart2js needs to retain that metadata and thus increase the size of resulting JS output. In practice, however, it is possible to extract most metadata necessary for your metaprogramming tasks statically using a transformer and package:analyzer, and act on it before compiling to JS.

Enable minification

Minification shortens all your longMethodNames into 2- or 3-letter long symbols. dart2js ensures that this kind of renaming is done safely, without breaking the functionality of your programs. You can enable it in pubspec.yaml under $dart2js transformer:

transformers:
...
- $dart2js:
    minify: true

Manually remove dead code

dart2js comes with dead code elimination out-of-the-box. However, it may not always be able to tell if a piece of code could be used. Consider the following example:

/// This function decides which serialization format to use
void setupSerializers() {
  if (server.doYouSupportProtocolBuffers()) {
    useProtobufSerializaers();
  } else {
    useJsonSerializaers();
  }
}

In this example the application asks the server what kind of serialization format it uses and dynamically chooses one or the other. dart2js could never tell whether the server responds with yes or no and so it must retain both kinds of serializers. However, you, as the developer of the application, may know in advance that your server supports protocol buffers and so you could remove that if block entirely and default to protocol buffers.

Code coverage (see above) is a good way to find dead code in your app.

Unsafe options

Dart also provides more aggressive optimization options. However, you have to be careful when using them and as of today the benefits aren't that clear. If your type annotations are inaccurate you may end up with non-Darty runtime behavior, including the classic "undefined is not a function" tautology, as well as the "keep on truckin'" behavior, e.g. null + 1 == 1 and {} + [] == 0.

--trust-type-annotations tells dart2js to trust that your type annotations are correct. So if you have a function foo(Bar bar) the compiler can omit the check that bar is truly Bar when calling methods on it.

--trust-primitives tells dart2js that primitive types, such as numbers and booleans are never null when performing arithmetic, and that your program does not run into range error when operating on lists, letting the compiler remove some of the error checking code.

These options are specified in pubspec.yaml.

Example:

transformers:
...
- $dart2js:
    commandLineOptions:
    - --trust-type-annotations
    - --trust-primitives