У меня есть приложение Angular 9 (v9.0.6), корректно работающее с Universal (SSR), и я выполнял последний набор тестов перед переходом на PROD, когда я заметил, что приложение зависает со 100% потреблением ЦП. Я проанализировал ошибку и оказался проблемой, когда приложение Angular загружает напрямую маршрут, который представляет собой ленивый загружаемый модуль, который выполняет HTTP-вызов.
Если я загружаю angular через Home (или другой маршрут без HTTP - но даже домашний, другой ленивый загруженный модуль имеет тот же компонент, который выполняет HTTP-вызов), все работает нормально. Я могу без проблем перемещаться по всем маршрутам, а приложение работает как шарм. Если, однако, я перейду, скажем, на www.mywebsite / lazy-loaded-module прямо на новую вкладку, я считаю, что в процессе начальной загрузки есть что-то, что препятствует правильной регистрации всех регистраций или, по крайней мере, HTTPClientModule и тогда я получаю отказ.
Начнем с того, что HTTPClientModule
зарегистрирован только один раз в AppModule
. Я получаю следующую ошибку:
(node:17624) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
ЦП переходит на 100%, и приложение зависает. Опять же, этого не произойдет, если я перейду к www.my-website.com
, а затем перейду через приложение к /my-lazy-loaded-module
Тогда я попробовал что-то еще: включая (я знаю, что не должен) HTTPClientModule внутри моего ленивого загруженного модуля, и он работал, как и проблема с процессором, исчезла, но у меня появилась другая ошибка:
ReferenceError: XMLHttpRequest is not defined
at BrowserXhr.build
Это влияет на контент, который я бы извлекал с помощью SSR и рендерил его, потому что его нет. В качестве обходного пути, который мог бы это сделать, но я хотел бы знать, как с этим бороться. Версия, которую я выполняю node
: v10.16.2
ОБНОВЛЕНИЕ
Для большей наглядности выполняемый мной HTTP-вызов - это просто стандартный вызов:
public getNext = (page: number, pageSize: number): Promise<IEventsPaged> => {
return this.http.get<IEventsPaged>(`${environment.apiBaseUrl}/events?page=${page}&pageSize=${pageSize})
.toPromise()
.then(r => r)
.catch((error: Response | any) => {
return Promise.reject(error);
});
}
ОБНОВЛЕНИЕ 2
Я обновился до Angular 9.1.1, в котором исправлено предупреждение о буфере. Приложение по-прежнему зависает, и это происходит только на одном модуле. Другие модули также без проблем выполняют HTTP-вызов (тот же стандартный get, но с другой службой).
** ОБНОВЛЕНИЕ 3 **
Я нашел основную причину проблемы. В конце концов, это не имело ничего общего с HTTP. Произошло то, что на странице, о которой я говорю, у меня очень простая угловая анимация. Это треугольник, который становится больше или меньше, применяя бесконечную анимацию с помощью стандартной угловой анимации. Чтобы добиться бесконечного эффекта, я подключался к событию animation.done
, чтобы изменить состояние на большое или маленькое. Что ж, если вы сильно обновите страницу на том маршруте, где размещена эта анимация, вы попадете в бесконечный цикл, подобный этому:
{ element:
HTMLDivElement {
parentNode:
HTMLUnknownElement {
parentNode: [HTMLDivElement],
_previousSibling: [HTMLDivElement],
_nextSibling: [HTMLImageElement],
_index: undefined,
_childNodes: null,
_firstChild: [Circular],
nodeType: 1,
ownerDocument: [Object],
localName: 'app-bottom-angle',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName: [Object],
_attrsByLName: [Object],
_attrKeys: [Array],
__ngContext__: [LComponentView_CalendarIntroductionComponent],
_classList: [DOMTokenList],
_nid: 79 },
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: null,
nodeType: 1,
ownerDocument:
{ parentNode: null,
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: [Object],
nodeType: 9,
isHTML: true,
_address: 'http://localhost:54818/en/calendar',
readyState: 'loading',
implementation: [Object],
ownerDocument: null,
_contentType: 'text/html',
doctype: [Object],
documentElement: [HTMLHtmlElement],
_templateDocCache: null,
_nodeIterators: null,
_nid: 1,
_nextnid: 152,
_nodes: [Array],
byId: [Object],
modclock: 23,
_scripting_enabled: true,
defaultView: [Object],
_lastModTime: 1 },
localName: 'div',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName:
[Object: null prototype] { '_ngcontent-sc33': [Object], class: [Object], style: [Object] },
_attrsByLName:
[Object: null prototype] {
'|_ngcontent-sc33': [Object],
'|class': [Object],
'|style': [Object] },
_attrKeys: [ '|_ngcontent-sc33', '|class', '|style' ],
_classList:
DOMTokenList {
'0': 'position-absolute',
'1': 'z-index-plus-1',
'2': 'bottom-0',
'3': 'right-0',
'4': 'left-0',
'5': 'mb-4',
'6': 'ng-tns-c33-1',
'7': 'ng-trigger',
'8': 'ng-trigger-scale',
'9': undefined,
'10': undefined,
_getString: [Function],
_setString: [Function],
_length: 9 },
__ngContext__:
LComponentView_BottomAngleComponent [
[HTMLUnknownElement],
[TView],
211,
[LComponentView_CalendarIntroductionComponent],
null,
null,
[TNode$1],
[LCleanup],
[BottomAngleComponent],
[Object],
[AnimationRendererFactory],
[AnimationRenderer],
null,
null,
null,
[LComponentView_CalendarIntroductionComponent],
[Circular],
null,
0,
[Circular],
'big' ],
_nid: 80,
_style:
{ _element: [Circular],
_parsedStyles: [Object],
_lastParsedText: 'transform: scale(1); transform-style: preserve-3d;',
_names: [Array] } },
triggerName: 'scale',
fromState: 'small',
toState: 'big',
phaseName: 'done',
totalTime: 1200,
disabled: false,
_data: 1006 }
{ element:
HTMLDivElement {
parentNode:
HTMLUnknownElement {
parentNode: [HTMLDivElement],
_previousSibling: [HTMLDivElement],
_nextSibling: [HTMLImageElement],
_index: undefined,
_childNodes: null,
_firstChild: [Circular],
nodeType: 1,
ownerDocument: [Object],
localName: 'app-bottom-angle',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName: [Object],
_attrsByLName: [Object],
_attrKeys: [Array],
__ngContext__: [LComponentView_CalendarIntroductionComponent],
_classList: [DOMTokenList],
_nid: 79 },
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: null,
nodeType: 1,
ownerDocument:
{ parentNode: null,
_previousSibling: [Circular],
_nextSibling: [Circular],
_index: undefined,
_childNodes: null,
_firstChild: [Object],
nodeType: 9,
isHTML: true,
_address: 'http://localhost:54818/en/calendar',
readyState: 'loading',
implementation: [Object],
ownerDocument: null,
_contentType: 'text/html',
doctype: [Object],
documentElement: [HTMLHtmlElement],
_templateDocCache: null,
_nodeIterators: null,
_nid: 1,
_nextnid: 152,
_nodes: [Array],
byId: [Object],
modclock: 23,
_scripting_enabled: true,
defaultView: [Object],
_lastModTime: 1 },
localName: 'div',
namespaceURI: 'http://www.w3.org/1999/xhtml',
prefix: null,
_tagName: undefined,
_attrsByQName:
[Object: null prototype] { '_ngcontent-sc33': [Object], class: [Object], style: [Object] },
_attrsByLName:
[Object: null prototype] {
'|_ngcontent-sc33': [Object],
'|class': [Object],
'|style': [Object] },
_attrKeys: [ '|_ngcontent-sc33', '|class', '|style' ],
_classList:
DOMTokenList {
'0': 'position-absolute',
'1': 'z-index-plus-1',
'2': 'bottom-0',
'3': 'right-0',
'4': 'left-0',
'5': 'mb-4',
'6': 'ng-tns-c33-1',
'7': 'ng-trigger',
'8': 'ng-trigger-scale',
'9': undefined,
'10': undefined,
_getString: [Function],
_setString: [Function],
_length: 9 },
__ngContext__:
LComponentView_BottomAngleComponent [
[HTMLUnknownElement],
[TView],
211,
[LComponentView_CalendarIntroductionComponent],
null,
null,
[TNode$1],
[LCleanup],
[BottomAngleComponent],
[Object],
[AnimationRendererFactory],
[AnimationRenderer],
null,
null,
null,
[LComponentView_CalendarIntroductionComponent],
[Circular],
null,
0,
[Circular],
'small' ],
_nid: 80,
_style:
{ _element: [Circular],
_parsedStyles: [Object],
_lastParsedText: 'transform: scale(1.2); transform-style: preserve-3d;',
_names: [Array] } },
triggerName: 'scale',
fromState: 'big',
toState: 'small',
phaseName: 'done',
totalTime: 1200,
disabled: false,
_data: 1007 }
И ваше приложение зависает. Я не знаю, есть ли лучший способ достижения бесконечных анимаций с помощью Angular-анимаций и должен ли разработчик «знать», что SSR будет уязвим с ними. Думаю, если вы думаете, что они имеют дело с элементами HTML, вы можете возразить, что все они должны использовать внутри себя isPlatformBrowser
.
APP_INITIALIZER
? - person David   schedule 13.04.2020