While doing investigation in a context of #40997, I came across a few areas in Forms module that can be refactored to provide better tree-shaking.
IMPORTANT: I’m creating this ticket primarily to capture some ideas/proposals. The numbers that are used in the ticket are for a particular use-case only, the real result might be different based on a particular scenario.
The use-case that I’ve looked at was an
<input> with an
ngModel on it:
<input type="text" [(ngModel)="title">.
Currently (with Angular 11.2.2) the Forms package size for such use-case is 41.81kb. I’ve built an app using command from #40997 (comment), so it’s not a fully optimized version (with all optimizations it’s 30.11kb). While doing the investigation I prototyped some of the changes to get a sense of potential savings we can get from improved tree-shaking.
- [PR #41146] Built-in Control Value Accessors are not tree-shakable due to this code:
This can be refactored to be tree-shakable by adding a special field onto these classes (for ex
ɵbuiltinCVA) and checking if this field exists in the
isBuiltInAccessor function instead of referring to these classes directly.
Performing this refactoring allows to drop 10.12kb (~25%% reduction), so the Forms package reduced to 31.69kb for that use-case.
- [PR #41189] Extract some logic from the Validators class and use them directly in the Forms code.
Currently the Validators class contains some helper functions (such as
Validators.nullValidator) that are used across the code of the Forms package. We should consider extracting these functions, using them directly in the code (to avoid references to Validators class) and use these helpers in the Validators class too (to avoid any breaking changes).
This optimization allows to save 3.36kb (~8%% reduction), so the Forms package reduced to 28.33kb for that use-case.
- Avoid direct references to
FormGroupin the _find function.
_find function is used in the
AbstractControl, so once the
FormControl is present in the code (which is used under the hood in the
NgModel directive), the
FormGroup become non-tree-shakable as well (even if they are not used anywhere else). Possible refactoring it to do something similar to what I mentioned in the 1st section (regarding Built-in CVAs).
That can give another 7kb of savings (or 16.7%% reduction), so the Forms package size is 21.3kb.
- [PR #41126] I also noticed that the
RadioControlRegistryclass is non-tree-shakable (as it’s directly referenced in the
ReactiveFormsModule). We can consider making it a tree-shakable provider (by using
providedIn: FormsModule), but that might potentially be a breaking change.
RadioControlRegistry tree-shakable saves a bit less than 1kb, so the final Forms package size in my experiment was 20.5kb, which is half the size of the original package (41.81kb).
Note: in real scenarios savings would be much less than in my example, since most of the Forms code would actually be used (such as validators, FormGroup and FormArray, different types of inputs -> more CVAs), but improved tree-shaking should still provide benefits.