From 5baba85e8ab585f4487ceeca1555b434a209a5bd Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 23 Jun 2020 16:17:54 +0200 Subject: [PATCH 1/4] build: add size test for tab group without lazy label or content --- goldens/size-test.yaml | 2 ++ .../size-test/material/tabs/BUILD.bazel | 13 ++++++++++ .../size-test/material/tabs/advanced.ts | 25 +++++++++++++++++++ integration/size-test/material/tabs/basic.ts | 23 +++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 integration/size-test/material/tabs/BUILD.bazel create mode 100644 integration/size-test/material/tabs/advanced.ts create mode 100644 integration/size-test/material/tabs/basic.ts diff --git a/goldens/size-test.yaml b/goldens/size-test.yaml index b596c9bc5e03..762fe6a88159 100644 --- a/goldens/size-test.yaml +++ b/goldens/size-test.yaml @@ -14,3 +14,5 @@ material/list/nav-list: 130473 material/menu/without-lazy-content: 210751 material/radio/without-group: 121448 material/select/without-optgroup: 256564 +material/tabs/advanced: 183478 +material/tabs/basic: 183332 diff --git a/integration/size-test/material/tabs/BUILD.bazel b/integration/size-test/material/tabs/BUILD.bazel new file mode 100644 index 000000000000..2138fad1bb66 --- /dev/null +++ b/integration/size-test/material/tabs/BUILD.bazel @@ -0,0 +1,13 @@ +load("//integration/size-test:index.bzl", "size_test") + +size_test( + name = "basic", + file = "basic.ts", + deps = ["//src/material/tabs"], +) + +size_test( + name = "advanced", + file = "advanced.ts", + deps = ["//src/material/tabs"], +) diff --git a/integration/size-test/material/tabs/advanced.ts b/integration/size-test/material/tabs/advanced.ts new file mode 100644 index 000000000000..deedf72e1f10 --- /dev/null +++ b/integration/size-test/material/tabs/advanced.ts @@ -0,0 +1,25 @@ +import {Component, NgModule} from '@angular/core'; +import {MatTabsModule} from '@angular/material/tabs'; + +/** + * Advanced component using `MatTabGroup` and `MatTab` in combination with + * lazy `MatTabContent` and `MatTabLabel`. + */ +@Component({ + template: ` + + + Label + Content + + + `, +}) +export class TestComponent {} + +@NgModule({ + imports: [MatTabsModule], + declarations: [TestComponent], + bootstrap: [TestComponent], +}) +export class AppModule {} diff --git a/integration/size-test/material/tabs/basic.ts b/integration/size-test/material/tabs/basic.ts new file mode 100644 index 000000000000..32ab52724394 --- /dev/null +++ b/integration/size-test/material/tabs/basic.ts @@ -0,0 +1,23 @@ +import {Component, NgModule} from '@angular/core'; +import {MatTabsModule} from '@angular/material/tabs'; + +/** + * Basic component using `MatTabGroup` and `MatTab`. Other parts of the tabs + * module such as lazy `MatTabContent` or `MatTabLabel` are not used and should + * be tree-shaken away. + */ +@Component({ + template: ` + + Content + + `, +}) +export class TestComponent {} + +@NgModule({ + imports: [MatTabsModule], + declarations: [TestComponent], + bootstrap: [TestComponent], +}) +export class AppModule {} From 7f34182ba56e5f15a59173f11cb06efbbf814ea7 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 23 Jun 2020 16:28:14 +0200 Subject: [PATCH 2/4] refactor: use lightweight token for tab-group label and content The Angular Material tab group currently always retains `MatTabContent` and `MatTabLabel`. These are optional features of the tabs module and should only be retained when needed. To achieve this, we use lightweight tokens instead of the actual implementations when querying for a lazy content or label. This signifies an improvement of ~500b in the common case, and an increase (due to the lightweight tokens being provided) in the advanced case of ~140b. Related to #19576. --- goldens/size-test.yaml | 4 ++-- src/material/tabs/tab-content.ts | 14 ++++++++++++-- src/material/tabs/tab-label.ts | 10 +++++++++- src/material/tabs/tab.ts | 10 ++++++---- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/goldens/size-test.yaml b/goldens/size-test.yaml index 762fe6a88159..160a5c8b7e19 100644 --- a/goldens/size-test.yaml +++ b/goldens/size-test.yaml @@ -14,5 +14,5 @@ material/list/nav-list: 130473 material/menu/without-lazy-content: 210751 material/radio/without-group: 121448 material/select/without-optgroup: 256564 -material/tabs/advanced: 183478 -material/tabs/basic: 183332 +material/tabs/advanced: 183627 +material/tabs/basic: 182824 diff --git a/src/material/tabs/tab-content.ts b/src/material/tabs/tab-content.ts index 369a97ff318d..bd348cf08f36 100644 --- a/src/material/tabs/tab-content.ts +++ b/src/material/tabs/tab-content.ts @@ -6,10 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, TemplateRef} from '@angular/core'; +import {Directive, InjectionToken, TemplateRef} from '@angular/core'; + +/** + * Injection token that can be used to reference instances of `MatTabContent`. It serves as + * alternative token to the actual `MatTabContent` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_TAB_CONTENT = new InjectionToken('MatTabContent'); /** Decorates the `ng-template` tags and reads out the template from it. */ -@Directive({selector: '[matTabContent]'}) +@Directive({ + selector: '[matTabContent]', + providers: [{provide: MAT_TAB_CONTENT, useExisting: MatTabContent}], +}) export class MatTabContent { constructor(public template: TemplateRef) { } } diff --git a/src/material/tabs/tab-label.ts b/src/material/tabs/tab-label.ts index 30172025e610..06f59b9c208f 100644 --- a/src/material/tabs/tab-label.ts +++ b/src/material/tabs/tab-label.ts @@ -6,11 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive} from '@angular/core'; +import {Directive, InjectionToken} from '@angular/core'; import {CdkPortal} from '@angular/cdk/portal'; +/** + * Injection token that can be used to reference instances of `MatTabLabel`. It serves as + * alternative token to the actual `MatTabLabel` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_TAB_LABEL = new InjectionToken('MatTabLabel'); + /** Used to flag tab labels for use with the portal directive */ @Directive({ selector: '[mat-tab-label], [matTabLabel]', + providers: [{provide: MAT_TAB_LABEL, useExisting: MatTabLabel}], }) export class MatTabLabel extends CdkPortal {} diff --git a/src/material/tabs/tab.ts b/src/material/tabs/tab.ts index 908bc4f8dc0b..27581b031579 100644 --- a/src/material/tabs/tab.ts +++ b/src/material/tabs/tab.ts @@ -27,8 +27,8 @@ import { } from '@angular/core'; import {CanDisable, CanDisableCtor, mixinDisabled} from '@angular/material/core'; import {Subject} from 'rxjs'; -import {MatTabContent} from './tab-content'; -import {MatTabLabel} from './tab-label'; +import {MAT_TAB_CONTENT} from './tab-content'; +import {MAT_TAB_LABEL, MatTabLabel} from './tab-label'; // Boilerplate for applying mixins to MatTab. @@ -53,8 +53,9 @@ export const MAT_TAB_GROUP = new InjectionToken('MAT_TAB_GROUP'); exportAs: 'matTab', }) export class MatTab extends _MatTabMixinBase implements OnInit, CanDisable, OnChanges, OnDestroy { + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. /** Content for the tab label given by ``. */ - @ContentChild(MatTabLabel) + @ContentChild(MAT_TAB_LABEL as any) get templateLabel(): MatTabLabel { return this._templateLabel; } set templateLabel(value: MatTabLabel) { // Only update the templateLabel via query if there is actually @@ -67,10 +68,11 @@ export class MatTab extends _MatTabMixinBase implements OnInit, CanDisable, OnCh } private _templateLabel: MatTabLabel; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. /** * Template provided in the tab content that will be used if present, used to enable lazy-loading */ - @ContentChild(MatTabContent, {read: TemplateRef, static: true}) + @ContentChild(MAT_TAB_CONTENT as any, {read: TemplateRef, static: true}) _explicitContent: TemplateRef; /** Template inside the MatTab view that contains an ``. */ From 44ffdc5acbfbee8bcc1f95c67b0c7791ac236690 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 23 Jun 2020 16:38:56 +0200 Subject: [PATCH 3/4] refactor: update select size-test golden to reflect latest changes The select size test has slightly shifted already due to the recent Angular update. We are updating the golden here so that we can see the lightweight token effect in the followed commit for select. --- goldens/size-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goldens/size-test.yaml b/goldens/size-test.yaml index 160a5c8b7e19..ee61033afc90 100644 --- a/goldens/size-test.yaml +++ b/goldens/size-test.yaml @@ -13,6 +13,6 @@ material/form-field/basic: 185008 material/list/nav-list: 130473 material/menu/without-lazy-content: 210751 material/radio/without-group: 121448 -material/select/without-optgroup: 256564 +material/select/without-optgroup: 256708 material/tabs/advanced: 183627 material/tabs/basic: 182824 From 1d70b9c7b941945271c34dda7c92446a2b3b32ce Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 23 Jun 2020 16:31:38 +0200 Subject: [PATCH 4/4] refactor: use lightweight token for custom select trigger The Angular Material always retains the `MatSelectTrigger` class. This is not ideal as this is an optional part of the select. The class itself is not large enough to contribute to any noticeable size impact, even with Ivy where factories/definitions are directly attached, but for the sake of completeness, and because custom triggers are assumed to be uncommon, a lightweight token is used for a small micro-optimization in the common case. Related to #19576. --- goldens/size-test.yaml | 2 +- integration/size-test/material/select/BUILD.bazel | 4 ++-- .../select/{without-optgroup.ts => basic.ts} | 3 ++- src/material/select/select.ts | 14 ++++++++++++-- tools/public_api_guard/material/select.d.ts | 2 ++ 5 files changed, 19 insertions(+), 6 deletions(-) rename integration/size-test/material/select/{without-optgroup.ts => basic.ts} (83%) diff --git a/goldens/size-test.yaml b/goldens/size-test.yaml index ee61033afc90..c6bff270058c 100644 --- a/goldens/size-test.yaml +++ b/goldens/size-test.yaml @@ -13,6 +13,6 @@ material/form-field/basic: 185008 material/list/nav-list: 130473 material/menu/without-lazy-content: 210751 material/radio/without-group: 121448 -material/select/without-optgroup: 256708 +material/select/basic: 256613 material/tabs/advanced: 183627 material/tabs/basic: 182824 diff --git a/integration/size-test/material/select/BUILD.bazel b/integration/size-test/material/select/BUILD.bazel index ed94e4468a95..e973d9d851cb 100644 --- a/integration/size-test/material/select/BUILD.bazel +++ b/integration/size-test/material/select/BUILD.bazel @@ -1,7 +1,7 @@ load("//integration/size-test:index.bzl", "size_test") size_test( - name = "without-optgroup", - file = "without-optgroup.ts", + name = "basic", + file = "basic.ts", deps = ["//src/material/select"], ) diff --git a/integration/size-test/material/select/without-optgroup.ts b/integration/size-test/material/select/basic.ts similarity index 83% rename from integration/size-test/material/select/without-optgroup.ts rename to integration/size-test/material/select/basic.ts index d26c4cadf2b0..405cb0486479 100644 --- a/integration/size-test/material/select/without-optgroup.ts +++ b/integration/size-test/material/select/basic.ts @@ -3,7 +3,8 @@ import {MatSelectModule} from '@angular/material/select'; /** * Basic component using `MatSelect` and `MatOption`. Other supported parts of the - * select like `MatOptgroup` are not used and should be tree-shaken away. + * select like `MatOptgroup` or `MatSelectTrigger` are not used and should be + * tree-shaken away. */ @Component({ template: ` diff --git a/src/material/select/select.ts b/src/material/select/select.ts index 9180ed54cf91..038dd634564c 100644 --- a/src/material/select/select.ts +++ b/src/material/select/select.ts @@ -198,11 +198,20 @@ const _MatSelectMixinBase: mixinDisableRipple(mixinTabIndex(mixinDisabled(mixinErrorState(MatSelectBase)))); + +/** + * Injection token that can be used to reference instances of `MatSelectTrigger`. It serves as + * alternative token to the actual `MatSelectTrigger` class which could cause unnecessary + * retention of the class and its directive metadata. + */ +export const MAT_SELECT_TRIGGER = new InjectionToken('MatSelectTrigger'); + /** * Allows the user to customize the trigger that is displayed when the select has a value. */ @Directive({ - selector: 'mat-select-trigger' + selector: 'mat-select-trigger', + providers: [{provide: MAT_SELECT_TRIGGER, useExisting: MatSelectTrigger}], }) export class MatSelectTrigger {} @@ -372,8 +381,9 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, /** Classes to be passed to the select panel. Supports the same syntax as `ngClass`. */ @Input() panelClass: string|string[]|Set|{[key: string]: any}; + // TODO: Remove cast once https://github.com/angular/angular/pull/37506 is available. /** User-supplied override of the trigger element. */ - @ContentChild(MatSelectTrigger) customTrigger: MatSelectTrigger; + @ContentChild(MAT_SELECT_TRIGGER as any) customTrigger: MatSelectTrigger; /** Placeholder to be shown if no value has been selected. */ @Input() diff --git a/tools/public_api_guard/material/select.d.ts b/tools/public_api_guard/material/select.d.ts index 6edab5eb0ee7..c160adfa62a5 100644 --- a/tools/public_api_guard/material/select.d.ts +++ b/tools/public_api_guard/material/select.d.ts @@ -10,6 +10,8 @@ export declare const MAT_SELECT_SCROLL_STRATEGY_PROVIDER: { export declare function MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay): () => ScrollStrategy; +export declare const MAT_SELECT_TRIGGER: InjectionToken; + export declare class MatSelect extends _MatSelectMixinBase implements AfterContentInit, OnChanges, OnDestroy, OnInit, DoCheck, ControlValueAccessor, CanDisable, HasTabIndex, MatFormFieldControl, CanUpdateErrorState, CanDisableRipple { _ariaDescribedby: string; readonly _closedStream: Observable;