Browse Source

UI: Optimizations - AOT + JIT

pull/3911/head
Igor Kulikov 6 years ago
parent
commit
ddb3ee61e7
  1. 2
      ui-ngx/angular.json
  2. 29
      ui-ngx/extra-webpack.config.js
  3. 1
      ui-ngx/package.json
  4. 8
      ui-ngx/src/app/core/auth/auth.service.ts
  5. 59
      ui-ngx/src/app/core/services/dynamic-component-factory.service.ts
  6. 87
      ui-ngx/src/app/core/services/resources.service.ts
  7. 2
      ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.ts
  8. 2
      ui-ngx/src/app/modules/home/components/filter/filter-text.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts
  10. 3
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts
  11. 2
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  12. 5
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
  13. 4
      ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts
  14. 2
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
  15. 36
      ui-ngx/src/app/shared/components/json-form/json-form.component.ts
  16. 14
      ui-ngx/src/app/shared/components/led-light.component.ts

2
ui-ngx/angular.json

@ -22,6 +22,7 @@
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json", "tsConfig": "src/tsconfig.app.json",
"aot": true,
"assets": [ "assets": [
"src/thingsboard.ico", "src/thingsboard.ico",
"src/assets", "src/assets",
@ -161,6 +162,7 @@
"serve": { "serve": {
"builder": "@angular-builders/custom-webpack:dev-server", "builder": "@angular-builders/custom-webpack:dev-server",
"options": { "options": {
"aot": true,
"browserTarget": "thingsboard:build", "browserTarget": "thingsboard:build",
"proxyConfig": "proxy.conf.js" "proxyConfig": "proxy.conf.js"
}, },

29
ui-ngx/extra-webpack.config.js

@ -14,8 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
const CompressionPlugin = require("compression-webpack-plugin"); const CompressionPlugin = require("compression-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack"); const webpack = require("webpack");
const dirTree = require("directory-tree"); const dirTree = require("directory-tree");
const AngularCompilerPlugin = require('@ngtools/webpack');
var langs = []; var langs = [];
@ -25,12 +27,14 @@ dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => {
langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5)); langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5));
}); });
module.exports = { module.exports = (config, options) => {
plugins: [ config.plugins.push(
new webpack.DefinePlugin({ new webpack.DefinePlugin({
TB_VERSION: JSON.stringify(require("./package.json").version), TB_VERSION: JSON.stringify(require("./package.json").version),
SUPPORTED_LANGS: JSON.stringify(langs), SUPPORTED_LANGS: JSON.stringify(langs),
}), })
);
config.plugins.push(
new CompressionPlugin({ new CompressionPlugin({
filename: "[path][base].gz[query]", filename: "[path][base].gz[query]",
algorithm: "gzip", algorithm: "gzip",
@ -38,6 +42,21 @@ module.exports = {
threshold: 10240, threshold: 10240,
minRatio: 0.8, minRatio: 0.8,
deleteOriginalAssets: false, deleteOriginalAssets: false,
}), })
], );
if (config.mode === 'production') {
const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin);
const angularCompilerOptions = config.plugins[index]._options;
angularCompilerOptions.emitClassMetadata = true;
angularCompilerOptions.emitNgModuleScope = true;
config.plugins.splice(index, 1);
config.plugins.push(new AngularCompilerPlugin.AngularCompilerPlugin(angularCompilerOptions));
const terserPluginOptions = config.optimization.minimizer[1].options;
delete terserPluginOptions.terserOptions.compress.global_defs.ngJitMode;
terserPluginOptions.terserOptions.compress.side_effects = false;
config.optimization.minimizer.splice(1, 1);
config.optimization.minimizer.push(new TerserPlugin(terserPluginOptions));
}
return config;
}; };

1
ui-ngx/package.json

@ -100,6 +100,7 @@
"@angular/cli": "^10.1.5", "@angular/cli": "^10.1.5",
"@angular/compiler-cli": "^10.1.5", "@angular/compiler-cli": "^10.1.5",
"@angular/language-service": "^10.1.5", "@angular/language-service": "^10.1.5",
"@ngtools/webpack": "10.1.5",
"@types/canvas-gauges": "^2.1.2", "@types/canvas-gauges": "^2.1.2",
"@types/flot": "^0.0.31", "@types/flot": "^0.0.31",
"@types/jasmine": "^3.5.12", "@types/jasmine": "^3.5.12",

8
ui-ngx/src/app/core/auth/auth.service.ts

@ -446,8 +446,12 @@ export class AuthService {
const refreshTokenValid = AuthService.isTokenValid('refresh_token'); const refreshTokenValid = AuthService.isTokenValid('refresh_token');
this.setUserFromJwtToken(null, null, false); this.setUserFromJwtToken(null, null, false);
if (!refreshTokenValid) { if (!refreshTokenValid) {
this.refreshTokenSubject.error(new Error(this.translate.instant('access.refresh-token-expired'))); this.translate.get('access.refresh-token-expired').subscribe(
this.refreshTokenSubject = null; (translation) => {
this.refreshTokenSubject.error(new Error(translation));
this.refreshTokenSubject = null;
}
);
} else { } else {
const refreshTokenRequest = { const refreshTokenRequest = {
refreshToken refreshToken

59
ui-ngx/src/app/core/services/dynamic-component-factory.service.ts

@ -59,37 +59,40 @@ export class DynamicComponentFactoryService {
template: string, template: string,
modules?: Type<any>[]): Observable<ComponentFactory<T>> { modules?: Type<any>[]): Observable<ComponentFactory<T>> {
const dymamicComponentFactorySubject = new ReplaySubject<ComponentFactory<T>>(); const dymamicComponentFactorySubject = new ReplaySubject<ComponentFactory<T>>();
const comp = this.createDynamicComponent(componentType, template); import('@angular/compiler').then(
let moduleImports: Type<any>[] = [CommonModule]; () => {
if (modules) { const comp = this.createDynamicComponent(componentType, template);
moduleImports = [...moduleImports, ...modules]; let moduleImports: Type<any>[] = [CommonModule];
} if (modules) {
// noinspection AngularInvalidImportedOrDeclaredSymbol moduleImports = [...moduleImports, ...modules];
@NgModule({
declarations: [comp],
imports: moduleImports
})
class DynamicComponentInstanceModule extends DynamicComponentModule {}
try {
this.compiler.compileModuleAsync(DynamicComponentInstanceModule).then(
(module) => {
const moduleRef = module.create(this.injector);
const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp);
this.dynamicComponentModulesMap.set(factory, {
moduleRef,
moduleType: module.moduleType
});
dymamicComponentFactorySubject.next(factory);
dymamicComponentFactorySubject.complete();
} }
).catch( // noinspection AngularInvalidImportedOrDeclaredSymbol
(e) => { const dynamicComponentInstanceModule = NgModule({
declarations: [comp],
imports: moduleImports
})(class DynamicComponentInstanceModule extends DynamicComponentModule {});
try {
this.compiler.compileModuleAsync(dynamicComponentInstanceModule).then(
(module) => {
const moduleRef = module.create(this.injector);
const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp);
this.dynamicComponentModulesMap.set(factory, {
moduleRef,
moduleType: module.moduleType
});
dymamicComponentFactorySubject.next(factory);
dymamicComponentFactorySubject.complete();
}
).catch(
(e) => {
dymamicComponentFactorySubject.error(e);
}
);
} catch (e) {
dymamicComponentFactorySubject.error(e); dymamicComponentFactorySubject.error(e);
} }
); }
} catch (e) { );
dymamicComponentFactorySubject.error(e);
}
return dymamicComponentFactorySubject.asObservable(); return dymamicComponentFactorySubject.asObservable();
} }

87
ui-ngx/src/app/core/services/resources.service.ts

@ -78,28 +78,31 @@ export class ResourcesService {
(module) => { (module) => {
const modules = this.extractNgModules(module); const modules = this.extractNgModules(module);
if (modules.length) { if (modules.length) {
const tasks: Promise<ModuleWithComponentFactories<any>>[] = []; import('@angular/compiler').then(
for (const m of modules) { () => {
tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
} for (const m of modules) {
forkJoin(tasks).subscribe((compiled) => { tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
try {
const componentFactories: ComponentFactory<any>[] = [];
for (const c of compiled) {
c.ngModuleFactory.create(this.injector);
componentFactories.push(...c.componentFactories);
} }
this.loadedFactories[url].next(componentFactories); forkJoin(tasks).subscribe((compiled) => {
this.loadedFactories[url].complete(); try {
} catch (e) { const componentFactories: ComponentFactory<any>[] = [];
this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`)); for (const c of compiled) {
delete this.loadedFactories[url]; c.ngModuleFactory.create(this.injector);
} componentFactories.push(...c.componentFactories);
}, }
(e) => { this.loadedFactories[url].next(componentFactories);
this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`)); this.loadedFactories[url].complete();
delete this.loadedFactories[url]; } catch (e) {
}); this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));
delete this.loadedFactories[url];
}
},
(e) => {
this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
delete this.loadedFactories[url];
}); }
);
} else { } else {
this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`)); this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
delete this.loadedFactories[url]; delete this.loadedFactories[url];
@ -133,26 +136,30 @@ export class ResourcesService {
} catch (e) { } catch (e) {
} }
if (modules && modules.length) { if (modules && modules.length) {
const tasks: Promise<ModuleWithComponentFactories<any>>[] = []; import('@angular/compiler').then(
for (const m of modules) { () => {
tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
} for (const m of modules) {
forkJoin(tasks).subscribe((compiled) => { tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
try {
for (const c of compiled) {
c.ngModuleFactory.create(this.injector);
}
this.loadedModules[url].next(modules);
this.loadedModules[url].complete();
} catch (e) {
this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
delete this.loadedModules[url];
} }
}, forkJoin(tasks).subscribe((compiled) => {
(e) => { try {
this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`)); for (const c of compiled) {
delete this.loadedModules[url]; c.ngModuleFactory.create(this.injector);
}); }
this.loadedModules[url].next(modules);
this.loadedModules[url].complete();
} catch (e) {
this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
delete this.loadedModules[url];
}
},
(e) => {
this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
delete this.loadedModules[url];
});
}
);
} else { } else {
this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`)); this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`));
delete this.loadedModules[url]; delete this.loadedModules[url];

2
ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.ts

@ -77,7 +77,7 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
this.complexFilterPredicate = predicate; this.complexFilterPredicate = predicate;
} }
private openComplexFilterDialog() { public openComplexFilterDialog() {
this.dialog.open<ComplexFilterPredicateDialogComponent, ComplexFilterPredicateDialogData, this.dialog.open<ComplexFilterPredicateDialogComponent, ComplexFilterPredicateDialogData,
ComplexFilterPredicateInfo>(ComplexFilterPredicateDialogComponent, { ComplexFilterPredicateInfo>(ComplexFilterPredicateDialogComponent, {
disableClose: true, disableClose: true,

2
ui-ngx/src/app/modules/home/components/filter/filter-text.component.ts

@ -59,7 +59,7 @@ export class FilterTextComponent implements ControlValueAccessor, OnInit {
requiredClass = false; requiredClass = false;
private filterText: string; public filterText: string;
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };

2
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts

@ -38,7 +38,7 @@ export interface AlarmRuleConditionDialogData {
selector: 'tb-alarm-rule-condition-dialog', selector: 'tb-alarm-rule-condition-dialog',
templateUrl: './alarm-rule-condition-dialog.component.html', templateUrl: './alarm-rule-condition-dialog.component.html',
providers: [{provide: ErrorStateMatcher, useExisting: AlarmRuleConditionDialogComponent}], providers: [{provide: ErrorStateMatcher, useExisting: AlarmRuleConditionDialogComponent}],
styleUrls: ['/alarm-rule-condition-dialog.component.scss'] styleUrls: ['./alarm-rule-condition-dialog.component.scss']
}) })
export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRuleConditionDialogComponent, AlarmCondition> export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRuleConditionDialogComponent, AlarmCondition>
implements OnInit, ErrorStateMatcher { implements OnInit, ErrorStateMatcher {

3
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts

@ -15,7 +15,7 @@
/// ///
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { Inject, InjectionToken } from '@angular/core'; import { Directive, Inject, InjectionToken } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@ -30,6 +30,7 @@ export interface CustomDialogData {
[key: string]: any; [key: string]: any;
} }
@Directive()
export class CustomDialogComponent extends PageComponent { export class CustomDialogComponent extends PageComponent {
[key: string]: any; [key: string]: any;

2
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts

@ -154,7 +154,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private alarmsTitlePattern: string; private alarmsTitlePattern: string;
private displayDetails = true; private displayDetails = true;
private allowAcknowledgment = true; public allowAcknowledgment = true;
private allowClear = true; private allowClear = true;
private defaultPageSize = 10; private defaultPageSize = 10;

5
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts

@ -186,11 +186,16 @@ export type TripAnimationSettings = {
usePointAsAnchor: boolean; usePointAsAnchor: boolean;
normalizationStep: number; normalizationStep: number;
showPolygon: boolean; showPolygon: boolean;
showLabel: boolean;
showTooltip: boolean;
latKeyName: string; latKeyName: string;
lngKeyName: string; lngKeyName: string;
rotationAngle: number; rotationAngle: number;
label: string; label: string;
tooltipPattern: string; tooltipPattern: string;
tooltipColor: string;
tooltipOpacity: number;
tooltipFontColor: string;
useTooltipFunction: boolean; useTooltipFunction: boolean;
useLabelFunction: boolean; useLabelFunction: boolean;
pointAsAnchorFunction: GenericFunction; pointAsAnchorFunction: GenericFunction;

4
ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts

@ -102,11 +102,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
ctx: WidgetContext; ctx: WidgetContext;
private formResize$: ResizeObserver; private formResize$: ResizeObserver;
private settings: MultipleInputWidgetSettings; public settings: MultipleInputWidgetSettings;
private widgetConfig: WidgetConfig; private widgetConfig: WidgetConfig;
private subscription: IWidgetSubscription; private subscription: IWidgetSubscription;
private datasources: Array<Datasource>; private datasources: Array<Datasource>;
private sources: Array<MultipleInputWidgetSource> = []; public sources: Array<MultipleInputWidgetSource> = [];
isVerticalAlignment: boolean; isVerticalAlignment: boolean;
inputWidthSettings: string; inputWidthSettings: string;

2
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts

@ -126,7 +126,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
private defaultPageSize = 10; private defaultPageSize = 10;
private defaultSortOrder = '-0'; private defaultSortOrder = '-0';
private hideEmptyLines = false; private hideEmptyLines = false;
private showTimestamp = true; public showTimestamp = true;
private dateFormatFilter: string; private dateFormatFilter: string;
private searchAction: WidgetAction = { private searchAction: WidgetAction = {

36
ui-ngx/src/app/shared/components/json-form/json-form.component.ts

@ -36,12 +36,14 @@ import { JsonFormProps } from './react/json-form.models';
import inspector from 'schema-inspector'; import inspector from 'schema-inspector';
import * as tinycolor_ from 'tinycolor2'; import * as tinycolor_ from 'tinycolor2';
import { DialogService } from '@app/core/services/dialog.service'; import { DialogService } from '@app/core/services/dialog.service';
import * as React from 'react'; // import * as React from 'react';
import * as ReactDOM from 'react-dom'; // import * as ReactDOM from 'react-dom';
import ReactSchemaForm from './react/json-form-react'; // import ReactSchemaForm from './react/json-form-react';
import JsonFormUtils from './react/json-form-utils'; import JsonFormUtils from './react/json-form-utils';
import { JsonFormComponentData } from './json-form-component.models'; import { JsonFormComponentData } from './json-form-component.models';
import { GroupInfo } from '@shared/models/widget.models'; import { GroupInfo } from '@shared/models/widget.models';
import { Observable } from 'rxjs/internal/Observable';
import { forkJoin, from } from 'rxjs';
const tinycolor = tinycolor_; const tinycolor = tinycolor_;
@ -252,11 +254,35 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
if (destroy) { if (destroy) {
this.destroyReactSchemaForm(); this.destroyReactSchemaForm();
} }
ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement);
// import ReactSchemaForm from './react/json-form-react';
const reactSchemaFormObservables: Observable<any>[] = [];
reactSchemaFormObservables.push(from(import('react')));
reactSchemaFormObservables.push(from(import('react-dom')));
reactSchemaFormObservables.push(from(import('./react/json-form-react')));
forkJoin(reactSchemaFormObservables).subscribe(
(modules) => {
const react = modules[0];
const reactDom = modules[1];
const jsonFormReact = modules[2].default;
reactDom.render(react.createElement(jsonFormReact, this.formProps), this.reactRootElmRef.nativeElement);
}
);
/* import('./react/json-form-react').then(
(mod) => {
ReactDOM.render(React.createElement(mod.default, this.formProps), this.reactRootElmRef.nativeElement);
}
);*/
// ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement);
} }
private destroyReactSchemaForm() { private destroyReactSchemaForm() {
ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement); import('react-dom').then(
(reactDom) => {
reactDom.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
}
);
// ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
} }
private validateModel(): boolean { private validateModel(): boolean {

14
ui-ngx/src/app/shared/components/led-light.component.ts

@ -16,7 +16,7 @@
import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import Raphael, { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael'; import { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael';
import * as tinycolor_ from 'tinycolor2'; import * as tinycolor_ from 'tinycolor2';
const tinycolor = tinycolor_; const tinycolor = tinycolor_;
@ -90,10 +90,14 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
if (this.paper) { if (this.paper) {
this.paper.remove(); this.paper.remove();
} }
this.paper = Raphael($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize); import('raphael').then(
const center = this.canvasSize / 2; (raphael) => {
this.circleElement = this.paper.circle(center, center, this.radius); this.paper = raphael.default($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize);
this.draw(); const center = this.canvasSize / 2;
this.circleElement = this.paper.circle(center, center, this.radius);
this.draw();
}
);
} }
private draw() { private draw() {

Loading…
Cancel
Save