mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-19 05:02:40 +08:00
Fix performance of Angular rerender
This commit is contained in:
parent
8cfc2c2007
commit
e651a3bdbc
@ -18,18 +18,28 @@ function setPaneKnobs(timestamp = +new Date()) {
|
||||
channel.emit(SET, { knobs: knobStore.getAll(), timestamp });
|
||||
}
|
||||
|
||||
// Increased performance by reducing the number of times a component is rendered during knob changes
|
||||
const debouncedOnKnobChanged = debounce(() => {
|
||||
const resetAndForceUpdate = () => {
|
||||
knobStore.markAllUnused();
|
||||
forceReRender();
|
||||
}, COMPONENT_FORCE_RENDER_DEBOUNCE_DELAY_MS);
|
||||
};
|
||||
|
||||
// Increase performance by reducing how frequently the story is recreated during knob changes
|
||||
const debouncedResetAndForceUpdate = debounce(
|
||||
resetAndForceUpdate,
|
||||
COMPONENT_FORCE_RENDER_DEBOUNCE_DELAY_MS
|
||||
);
|
||||
|
||||
function knobChanged(change) {
|
||||
const { name } = change;
|
||||
const { value } = change; // Update the related knob and it's value.
|
||||
const knobOptions = knobStore.get(name);
|
||||
knobOptions.value = value;
|
||||
debouncedOnKnobChanged();
|
||||
|
||||
if (!manager.options.disableDebounce) {
|
||||
debouncedResetAndForceUpdate();
|
||||
} else {
|
||||
resetAndForceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
function knobClicked(clicked) {
|
||||
|
@ -51,6 +51,7 @@
|
||||
"@angular/platform-browser-dynamic": ">=6.0.0",
|
||||
"autoprefixer": "^8.1.0",
|
||||
"babel-loader": "^7.0.0 || ^8.0.0",
|
||||
"rxjs": "^6.0.0",
|
||||
"zone.js": "^0.8.29"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@ -16,6 +16,8 @@ import {
|
||||
} from '@angular/core';
|
||||
import { STORY } from '../app.token';
|
||||
import { NgStory, ICollection } from '../types';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'storybook-dynamic-app-root',
|
||||
@ -24,22 +26,33 @@ import { NgStory, ICollection } from '../types';
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('target', { read: ViewContainerRef })
|
||||
target: ViewContainerRef;
|
||||
constructor(private cfr: ComponentFactoryResolver, @Inject(STORY) private data: NgStory) {}
|
||||
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private cfr: ComponentFactoryResolver,
|
||||
@Inject(STORY) private data: Observable<NgStory>
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.putInMyHtml();
|
||||
this.data.pipe(first()).subscribe((data: NgStory) => {
|
||||
this.target.clear();
|
||||
const compFactory = this.cfr.resolveComponentFactory(data.component);
|
||||
const ref = this.target.createComponent(compFactory);
|
||||
const instance = ref.instance;
|
||||
|
||||
this.subscription = this.data.subscribe(newData => {
|
||||
this.setProps(instance, newData);
|
||||
ref.changeDetectorRef.detectChanges();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.target.clear();
|
||||
}
|
||||
|
||||
private putInMyHtml(): void {
|
||||
this.target.clear();
|
||||
const compFactory = this.cfr.resolveComponentFactory(this.data.component);
|
||||
const instance = this.target.createComponent(compFactory).instance;
|
||||
|
||||
this.setProps(instance, this.data);
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,7 @@ import { BrowserModule } from '@angular/platform-browser';
|
||||
import { AppComponent } from './components/app.component';
|
||||
import { STORY } from './app.token';
|
||||
import { NgModuleMetadata, IStoryFn, NgStory } from './types';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
|
||||
let platform: any = null;
|
||||
let promises: Array<Promise<NgModuleRef<any>>> = [];
|
||||
@ -14,6 +15,8 @@ const componentClass = class DynamicComponent {};
|
||||
|
||||
type DynamicComponentType = typeof componentClass;
|
||||
|
||||
const storyData = new ReplaySubject(1);
|
||||
|
||||
const getModule = (
|
||||
declarations: Array<Type<any> | any[]>,
|
||||
entryComponents: Array<Type<any> | any[]>,
|
||||
@ -21,10 +24,12 @@ const getModule = (
|
||||
data: NgStory,
|
||||
moduleMetadata: NgModuleMetadata
|
||||
) => {
|
||||
storyData.next(data);
|
||||
|
||||
const moduleMeta = {
|
||||
declarations: [...declarations, ...(moduleMetadata.declarations || [])],
|
||||
imports: [BrowserModule, FormsModule, ...(moduleMetadata.imports || [])],
|
||||
providers: [{ provide: STORY, useValue: { ...data } }, ...(moduleMetadata.providers || [])],
|
||||
providers: [{ provide: STORY, useValue: storyData }, ...(moduleMetadata.providers || [])],
|
||||
entryComponents: [...entryComponents, ...(moduleMetadata.entryComponents || [])],
|
||||
schemas: [...(moduleMetadata.schemas || [])],
|
||||
bootstrap: [...bootstrap],
|
||||
@ -86,6 +91,10 @@ const draw = (newModule: DynamicComponentType): void => {
|
||||
}
|
||||
};
|
||||
|
||||
export const renderNgApp = (storyFn: IStoryFn) => {
|
||||
draw(initModule(storyFn));
|
||||
export const renderNgApp = (storyFn: IStoryFn, forced: boolean) => {
|
||||
if (!forced) {
|
||||
draw(initModule(storyFn));
|
||||
} else {
|
||||
storyData.next(storyFn());
|
||||
}
|
||||
};
|
||||
|
4
app/angular/src/client/preview/render.js
vendored
4
app/angular/src/client/preview/render.js
vendored
@ -1,6 +1,6 @@
|
||||
import { renderNgApp } from './angular/helpers';
|
||||
|
||||
export default function render({ storyFn, showMain }) {
|
||||
export default function render({ storyFn, showMain, forceRender }) {
|
||||
showMain();
|
||||
renderNgApp(storyFn);
|
||||
renderNgApp(storyFn, forceRender);
|
||||
}
|
||||
|
@ -18,6 +18,11 @@ import { SimpleKnobsComponent } from './knobs.component';
|
||||
import { AllKnobsComponent } from './all-knobs.component';
|
||||
|
||||
storiesOf('Addon|Knobs', module)
|
||||
.addParameters({
|
||||
knobs: {
|
||||
disableDebounce: true,
|
||||
},
|
||||
})
|
||||
.addDecorator(withKnobs)
|
||||
.add('Simple', () => {
|
||||
const name = text('name', 'John Doe');
|
||||
|
@ -10,6 +10,8 @@
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"target": "es5",
|
||||
"typeRoots": ["../../node_modules/@types", "node_modules/@types"],
|
||||
"lib": ["es2017", "dom"]
|
||||
|
@ -18,7 +18,6 @@
|
||||
"comment-format": [true, "check-space"],
|
||||
"curly": true,
|
||||
"forin": true,
|
||||
"import-blacklist": [true, "rxjs"],
|
||||
"interface-over-type-literal": true,
|
||||
"label-position": true,
|
||||
"member-access": false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user