mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
The `getBaseHref` method is called several times per request and currently queries through the entire document. We can speed it up by taking advantage of the fact that the `<base>` can only be a direct child of the `<head>` and is usually defined towards the beginning. Below are some benchmarks for a "Hello world" app before and after this change. ### Before: ``` Running 60s test @ http://localhost:4202 100 connections with 10 pipelining factor ┌─────────┬────────┬────────┬────────┬────────┬───────────┬──────────┬─────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼────────┼────────┼────────┼────────┼───────────┼──────────┼─────────┤ │ Latency │ 568 ms │ 853 ms │ 901 ms │ 904 ms │ 866.58 ms │ 437.6 ms │ 9915 ms │ └─────────┴────────┴────────┴────────┴────────┴───────────┴──────────┴─────────┘ ┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤ │ Req/Sec │ 490 │ 826 │ 1,006 │ 1,643 │ 1,129.3 │ 234.69 │ 490 │ ├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤ │ Bytes/Sec │ 10.4 MB │ 17.4 MB │ 21.3 MB │ 34.7 MB │ 23.9 MB │ 4.96 MB │ 10.3 MB │ └───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘ Req/Bytes counts sampled once per second. # of samples: 60 69k requests in 60.04s, 1.43 GB read 90 errors (90 timeouts) ``` ### After ``` Running 60s test @ http://localhost:4202 100 connections with 10 pipelining factor ┌─────────┬────────┬────────┬────────┬─────────┬───────────┬───────────┬─────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼────────┼────────┼────────┼─────────┼───────────┼───────────┼─────────┤ │ Latency │ 471 ms │ 831 ms │ 889 ms │ 1668 ms │ 835.91 ms │ 467.89 ms │ 9720 ms │ └─────────┴────────┴────────┴────────┴─────────┴───────────┴───────────┴─────────┘ ┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼────────┼─────────┤ │ Req/Sec │ 390 │ 860 │ 1,145 │ 1,572 │ 1,156.77 │ 222.65 │ 390 │ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼────────┼─────────┤ │ Bytes/Sec │ 8.24 MB │ 18.2 MB │ 24.2 MB │ 33.2 MB │ 24.4 MB │ 4.7 MB │ 8.24 MB │ └───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴────────┴─────────┘ Req/Bytes counts sampled once per second. # of samples: 60 71k requests in 60.03s, 1.47 GB read 140 errors (140 timeouts) ``` PR Close #61392
97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
/**
|
|
* @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.dev/license
|
|
*/
|
|
|
|
import {
|
|
ɵparseCookieValue as parseCookieValue,
|
|
ɵsetRootDomAdapter as setRootDomAdapter,
|
|
ɵDomAdapter as DomAdapter,
|
|
} from '@angular/common';
|
|
|
|
/**
|
|
* A `DomAdapter` powered by full browser DOM APIs.
|
|
*
|
|
* @security Tread carefully! Interacting with the DOM directly is dangerous and
|
|
* can introduce XSS risks.
|
|
*/
|
|
export class BrowserDomAdapter extends DomAdapter {
|
|
override readonly supportsDOMEvents: boolean = true;
|
|
|
|
static makeCurrent() {
|
|
setRootDomAdapter(new BrowserDomAdapter());
|
|
}
|
|
|
|
override onAndCancel(el: Node, evt: any, listener: any, options: any): Function {
|
|
el.addEventListener(evt, listener, options);
|
|
return () => {
|
|
el.removeEventListener(evt, listener, options);
|
|
};
|
|
}
|
|
override dispatchEvent(el: Node, evt: any) {
|
|
el.dispatchEvent(evt);
|
|
}
|
|
override remove(node: Node): void {
|
|
(node as Element | Text | Comment).remove();
|
|
}
|
|
override createElement(tagName: string, doc?: Document): HTMLElement {
|
|
doc = doc || this.getDefaultDocument();
|
|
return doc.createElement(tagName);
|
|
}
|
|
override createHtmlDocument(): Document {
|
|
return document.implementation.createHTMLDocument('fakeTitle');
|
|
}
|
|
override getDefaultDocument(): Document {
|
|
return document;
|
|
}
|
|
|
|
override isElementNode(node: Node): boolean {
|
|
return node.nodeType === Node.ELEMENT_NODE;
|
|
}
|
|
|
|
override isShadowRoot(node: any): boolean {
|
|
return node instanceof DocumentFragment;
|
|
}
|
|
|
|
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
|
|
override getGlobalEventTarget(doc: Document, target: string): EventTarget | null {
|
|
if (target === 'window') {
|
|
return window;
|
|
}
|
|
if (target === 'document') {
|
|
return doc;
|
|
}
|
|
if (target === 'body') {
|
|
return doc.body;
|
|
}
|
|
return null;
|
|
}
|
|
override getBaseHref(doc: Document): string | null {
|
|
const href = getBaseElementHref();
|
|
return href == null ? null : relativePath(href);
|
|
}
|
|
override resetBaseElement(): void {
|
|
baseElement = null;
|
|
}
|
|
override getUserAgent(): string {
|
|
return window.navigator.userAgent;
|
|
}
|
|
override getCookie(name: string): string | null {
|
|
return parseCookieValue(document.cookie, name);
|
|
}
|
|
}
|
|
|
|
let baseElement: HTMLElement | null = null;
|
|
function getBaseElementHref(): string | null {
|
|
baseElement = baseElement || document.head.querySelector('base');
|
|
return baseElement ? baseElement.getAttribute('href') : null;
|
|
}
|
|
|
|
function relativePath(url: string): string {
|
|
// The base URL doesn't really matter, we just need it so relative paths have something
|
|
// to resolve against. In the browser `HTMLBaseElement.href` is always absolute.
|
|
return new URL(url, document.baseURI).pathname;
|
|
}
|