fix(platform-server): remove styles added by ServerStylesHost on destruction (#38367)

When a ServerStylesHost instance is destroyed, all of the shared styles added to the DOM
head element by that instance should be removed.  Without this removal, over time a large
number of style rules will build up and cause extra memory pressure.  This brings the
ServerStylesHost in line with the DomStylesHost used by the platform browser, which
performs this same cleanup.

PR Close #38367
This commit is contained in:
Joey Perrott 2020-08-06 10:59:20 -07:00 committed by Andrew Kushnir
parent 6d8c73a4d6
commit e34c33cd46
2 changed files with 56 additions and 0 deletions

View File

@ -13,6 +13,7 @@ import {ɵSharedStylesHost as SharedStylesHost, ɵTRANSITION_ID} from '@angular/
@Injectable()
export class ServerStylesHost extends SharedStylesHost {
private head: any = null;
private _styleNodes = new Set<HTMLElement>();
constructor(
@Inject(DOCUMENT) private doc: any,
@ -29,9 +30,14 @@ export class ServerStylesHost extends SharedStylesHost {
el.setAttribute('ng-transition', this.transitionId);
}
this.head.appendChild(el);
this._styleNodes.add(el);
}
onStylesAdded(additions: Set<string>) {
additions.forEach(style => this._addStyle(style));
}
ngOnDestroy() {
this._styleNodes.forEach(styleNode => styleNode.remove());
}
}

View File

@ -0,0 +1,50 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ɵgetDOM as getDOM} from '@angular/common';
import {ServerStylesHost} from '@angular/platform-server/src/styles_host';
(function() {
if (getDOM().supportsDOMEvents()) return; // NODE only
describe('ServerStylesHost', () => {
let ssh: ServerStylesHost;
let documentHead: Element;
beforeEach(() => {
const doc = getDOM().createHtmlDocument();
ssh = new ServerStylesHost(doc, '');
documentHead = doc.head;
doc.querySelector('title')?.remove();
});
it('should add existing styles', () => {
ssh.addStyles(['a {};']);
expect(documentHead.innerHTML).toEqual('<style>a {};</style>');
});
it('should add new styles to hosts', () => {
ssh.addStyles(['a {};']);
expect(documentHead.innerHTML).toEqual('<style>a {};</style>');
});
it('should add styles only once to hosts', () => {
ssh.addStyles(['a {};']);
ssh.addStyles(['a {};']);
expect(documentHead.innerHTML).toEqual('<style>a {};</style>');
});
it('should remove style nodes on destroy', () => {
ssh.addStyles(['a {};']);
expect(documentHead.innerHTML).toEqual('<style>a {};</style>');
ssh.ngOnDestroy();
expect(documentHead.innerHTML).toEqual('');
});
});
})();