perf(core): allow checkNoChanges mode to be tree-shaken in production (#45913)

This commit guards all logic that exists for the `checkNoChanges` mode
with `ngDevMode` checks such that the logic can be tree-shaken.

PR Close #45913
This commit is contained in:
JoostK
2021-12-09 01:11:30 +01:00
committed by Andrew Kushnir
parent def1f0fb93
commit 75b3d0f843
13 changed files with 33 additions and 102 deletions
@@ -104,7 +104,7 @@ export abstract class ChangeDetectorRef {
* Checks the change detector and its children, and throws if any changes are detected.
*
* Use in development mode to verify that running change detection doesn't introduce
* other changes.
* other changes. Calling it in production mode is a noop.
*/
abstract checkNoChanges(): void;
@@ -37,7 +37,8 @@ import {getLView, getSelectedIndex, getTView, isInCheckNoChangesMode, setSelecte
*/
export function ɵɵadvance(delta: number): void {
ngDevMode && assertGreaterThan(delta, 0, 'Can only advance forward');
selectIndexInternal(getTView(), getLView(), getSelectedIndex() + delta, isInCheckNoChangesMode());
selectIndexInternal(
getTView(), getLView(), getSelectedIndex() + delta, !!ngDevMode && isInCheckNoChangesMode());
}
export function selectIndexInternal(
@@ -384,7 +384,7 @@ export function refreshView<T>(
enterView(lView);
// Check no changes mode is a dev only mode used to verify that bindings have not changed
// since they were assigned. We do not want to execute lifecycle hooks in that mode.
const isInCheckNoChangesPass = isInCheckNoChangesMode();
const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
try {
resetPreOrderHookFlags(lView);
@@ -505,10 +505,14 @@ export function refreshView<T>(
export function renderComponentOrTemplate<T>(
tView: TView, lView: LView, templateFn: ComponentTemplate<{}>|null, context: T) {
const rendererFactory = lView[RENDERER_FACTORY];
const normalExecutionPath = !isInCheckNoChangesMode();
// Check no changes mode is a dev only mode used to verify that bindings have not changed
// since they were assigned. We do not want to invoke renderer factory functions in that mode
// to avoid any possible side-effects.
const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
const creationModeIsActive = isCreationMode(lView);
try {
if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
if (!checkNoChangesMode && !creationModeIsActive && rendererFactory.begin) {
rendererFactory.begin();
}
if (creationModeIsActive) {
@@ -516,7 +520,7 @@ export function renderComponentOrTemplate<T>(
}
refreshView(tView, lView, templateFn, context);
} finally {
if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
if (!checkNoChangesMode && !creationModeIsActive && rendererFactory.end) {
rendererFactory.end();
}
}
@@ -531,7 +535,7 @@ function executeTemplate<T>(
if (isUpdatePhase && lView.length > HEADER_OFFSET) {
// When we're updating, inherently select 0 so we don't
// have to generate that instruction for most update blocks.
selectIndexInternal(tView, lView, HEADER_OFFSET, isInCheckNoChangesMode());
selectIndexInternal(tView, lView, HEADER_OFFSET, !!ngDevMode && isInCheckNoChangesMode());
}
const preHookType =
+15 -15
View File
@@ -7,7 +7,7 @@
*/
import {InjectFlags} from '../di/interface/injector';
import {assertDefined, assertEqual, assertGreaterThanOrEqual, assertLessThan, assertNotEqual} from '../util/assert';
import {assertDefined, assertEqual, assertGreaterThanOrEqual, assertLessThan, assertNotEqual, throwError} from '../util/assert';
import {assertLViewOrUndefined, assertTNodeForLView, assertTNodeForTView} from './assert';
import {DirectiveDef} from './interfaces/definition';
@@ -172,24 +172,23 @@ interface InstructionState {
* ```
*/
bindingsEnabled: boolean;
/**
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
*
* Necessary to support ChangeDetectorRef.checkNoChanges().
*
* checkNoChanges Runs only in devmode=true and verifies that no unintended changes exist in
* the change detector or its children.
*/
isInCheckNoChangesMode: boolean;
}
const instructionState: InstructionState = {
lFrame: createLFrame(null),
bindingsEnabled: true,
isInCheckNoChangesMode: false,
};
/**
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
*
* Necessary to support ChangeDetectorRef.checkNoChanges().
*
* The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended
* changes exist in the change detector or its children.
*/
let _isInCheckNoChangesMode = false;
/**
* Returns true if the instruction state stack is empty.
*
@@ -353,12 +352,13 @@ export function getContextLView(): LView {
}
export function isInCheckNoChangesMode(): boolean {
// TODO(misko): remove this from the LView since it is ngDevMode=true mode only.
return instructionState.isInCheckNoChangesMode;
!ngDevMode && throwError('Must never be called in production mode');
return _isInCheckNoChangesMode;
}
export function setIsInCheckNoChangesMode(mode: boolean): void {
instructionState.isInCheckNoChangesMode = mode;
!ngDevMode && throwError('Must never be called in production mode');
_isInCheckNoChangesMode = mode;
}
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
+6 -2
View File
@@ -281,7 +281,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
* introduce other changes.
*/
checkNoChanges(): void {
checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context as unknown as {});
if (ngDevMode) {
checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context as unknown as {});
}
}
attachToViewContainerRef() {
@@ -318,7 +320,9 @@ export class RootViewRef<T> extends ViewRef<T> {
}
override checkNoChanges(): void {
checkNoChangesInRootView(this._view);
if (ngDevMode) {
checkNoChangesInRootView(this._view);
}
}
override get context(): T {
@@ -725,12 +725,6 @@
{
"name": "detachMovedView"
},
{
"name": "detectChangesInRootView"
},
{
"name": "detectChangesInternal"
},
{
"name": "diPublicInInjector"
},
@@ -1022,9 +1016,6 @@
{
"name": "isImportedNgModuleProviders"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isInlineTemplate"
},
@@ -1313,9 +1304,6 @@
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsInCheckNoChangesMode"
},
{
"name": "setSelectedIndex"
},
@@ -236,9 +236,6 @@
{
"name": "isCurrentTNodeParent"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isInlineTemplate"
},
@@ -746,12 +746,6 @@
{
"name": "detachView"
},
{
"name": "detectChangesInRootView"
},
{
"name": "detectChangesInternal"
},
{
"name": "diPublicInInjector"
},
@@ -1127,9 +1121,6 @@
{
"name": "isImportedNgModuleProviders"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isInlineTemplate"
},
@@ -1463,9 +1454,6 @@
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsInCheckNoChangesMode"
},
{
"name": "setSelectedIndex"
},
@@ -719,12 +719,6 @@
{
"name": "detachView"
},
{
"name": "detectChangesInRootView"
},
{
"name": "detectChangesInternal"
},
{
"name": "diPublicInInjector"
},
@@ -1088,9 +1082,6 @@
{
"name": "isImportedNgModuleProviders"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isInlineTemplate"
},
@@ -1442,9 +1433,6 @@
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsInCheckNoChangesMode"
},
{
"name": "setSelectedIndex"
},
@@ -167,9 +167,6 @@
{
"name": "invertObject"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isLView"
},
@@ -1022,12 +1022,6 @@
{
"name": "detachView"
},
{
"name": "detectChangesInRootView"
},
{
"name": "detectChangesInternal"
},
{
"name": "diPublicInInjector"
},
@@ -1466,9 +1460,6 @@
{
"name": "isImportedNgModuleProviders"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isInlineTemplate"
},
@@ -1826,9 +1817,6 @@
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsInCheckNoChangesMode"
},
{
"name": "setRouterState"
},
@@ -461,12 +461,6 @@
{
"name": "detachMovedView"
},
{
"name": "detectChangesInRootView"
},
{
"name": "detectChangesInternal"
},
{
"name": "domRendererFactory3"
},
@@ -668,9 +662,6 @@
{
"name": "isImportedNgModuleProviders"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isLContainer"
},
@@ -848,9 +839,6 @@
{
"name": "setInjectImplementation"
},
{
"name": "setIsInCheckNoChangesMode"
},
{
"name": "setSelectedIndex"
},
@@ -299,12 +299,6 @@
{
"name": "detachView"
},
{
"name": "detectChangesInRootView"
},
{
"name": "detectChangesInternal"
},
{
"name": "diPublicInInjector"
},
@@ -551,9 +545,6 @@
{
"name": "isDirectiveHost"
},
{
"name": "isInCheckNoChangesMode"
},
{
"name": "isInlineTemplate"
},
@@ -758,9 +749,6 @@
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsInCheckNoChangesMode"
},
{
"name": "setSelectedIndex"
},