Skip to content

Commit

Permalink
perf(runtime): optimize dom write scheduling
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed Jul 4, 2019
1 parent 45e99f6 commit 8897c6f
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 26 deletions.
20 changes: 15 additions & 5 deletions src/client/client-load-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@ import * as d from '../declarations';
import { BUILD } from '@build-conditionals';
import { consoleError } from './client-log';

export const moduleCache = /*@__PURE__*/new Map<string, {[exportName: string]: d.ComponentConstructor}>();

export const loadModule = (cmpMeta: d.ComponentRuntimeMeta, hostRef: d.HostRef, hmrVersionId?: string): Promise<d.ComponentConstructor> => {
export const loadModule = (cmpMeta: d.ComponentRuntimeMeta, hostRef: d.HostRef, hmrVersionId?: string): Promise<d.ComponentConstructor> | d.ComponentConstructor => {
// loadModuleImport
const bundleId = (BUILD.mode && typeof cmpMeta.$lazyBundleIds$ !== 'string')
const exportName = cmpMeta.$tagName$.replace(/-/g, '_');
const bundleId = ((BUILD.mode && typeof cmpMeta.$lazyBundleIds$ !== 'string')
? cmpMeta.$lazyBundleIds$[hostRef.$modeName$]
: cmpMeta.$lazyBundleIds$;

: cmpMeta.$lazyBundleIds$) as string;
const module = !BUILD.hotModuleReplacement ? moduleCache.get(bundleId) : false;
if (module) {
return module[exportName];
}
return import(
/* webpackInclude: /\.entry\.js$/ */
/* webpackExclude: /\.system\.entry\.js$/ */
/* webpackMode: "lazy" */
`./${bundleId}.entry.js${BUILD.hotModuleReplacement && hmrVersionId ? '?s-hmr=' + hmrVersionId : ''}`
).then(importedModule => importedModule[cmpMeta.$tagName$.replace(/-/g, '_')], consoleError);
).then(importedModule => {
if (!BUILD.hotModuleReplacement) {
moduleCache.set(bundleId, importedModule);
}
return importedModule[exportName];
}, consoleError);
};
1 change: 0 additions & 1 deletion src/client/client-task-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const queueDomWrites: d.RafCallback[] = [];
const queueDomWritesLow: d.RafCallback[] = [];

const queueTask = (queue: d.RafCallback[]) => (cb: d.RafCallback) => {
// queue dom reads
queue.push(cb);

if (!queuePending) {
Expand Down
23 changes: 14 additions & 9 deletions src/runtime/initialize-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { fireConnectedCallback } from './connected-callback';
import { PROXY_FLAGS } from './runtime-constants';


export const initializeComponent = async (elm: d.HostElement, hostRef: d.HostRef, cmpMeta: d.ComponentRuntimeMeta, hmrVersionId?: string, Cstr?: d.ComponentConstructor) => {
export const initializeComponent = async (elm: d.HostElement, hostRef: d.HostRef, cmpMeta: d.ComponentRuntimeMeta, hmrVersionId?: string, Cstr?: any) => {
// initializeComponent
if ((BUILD.lazyLoad || BUILD.style) && (hostRef.$flags$ & HOST_FLAGS.hasInitializedComponent) === 0) {
// we haven't initialized this element yet
Expand All @@ -31,7 +31,11 @@ export const initializeComponent = async (elm: d.HostElement, hostRef: d.HostRef
// lazy loaded components
// request the component's implementation to be
// wired up with the host element
Cstr = await loadModule(cmpMeta, hostRef, hmrVersionId);
Cstr = loadModule(cmpMeta, hostRef, hmrVersionId);
if (Cstr.then) {
// Await creates a micro-task avoid if possible
Cstr = await Cstr;
}
if ((BUILD.isDev || BUILD.isDebug) && !Cstr) {
throw new Error(`Constructor for "${cmpMeta.$tagName$}#${hostRef.$modeName$}" was not found`);
}
Expand Down Expand Up @@ -88,18 +92,19 @@ export const initializeComponent = async (elm: d.HostElement, hostRef: d.HostRef

// we've successfully created a lazy instance
const ancestorComponent = hostRef.$ancestorComponent$;
const schedule = () => scheduleUpdate(elm, hostRef, cmpMeta, true);

if (BUILD.lifecycle && BUILD.lazyLoad && ancestorComponent && ancestorComponent['s-lr'] === false && ancestorComponent['s-rc']) {
// this is the intial load and this component it has an ancestor component
// but the ancestor component has NOT fired its will update lifecycle yet
// so let's just cool our jets and wait for the ancestor to continue first
ancestorComponent['s-rc'].push(() =>
// this will get fired off when the ancestor component
// finally gets around to rendering its lazy self
// fire off the initial update
initializeComponent(elm, hostRef, cmpMeta)
);

// this will get fired off when the ancestor component
// finally gets around to rendering its lazy self
// fire off the initial update
ancestorComponent['s-rc'].push(schedule);

} else {
scheduleUpdate(elm, hostRef, cmpMeta, true);
schedule();
}
};
30 changes: 19 additions & 11 deletions src/runtime/update-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,27 @@ import { HYDRATED_CLASS, PLATFORM_FLAGS } from './runtime-constants';
import { renderVdom } from './vdom/vdom-render';


export const safeCall = async (instance: any, method: string, arg?: any) => {
export const safeCall = (instance: any, method: string, arg?: any) => {
if (instance && instance[method]) {
try {
await instance[method](arg);
return instance[method](arg);
} catch (e) {
consoleError(e);
}
}
return undefined;
};

export const scheduleUpdate = async (elm: d.HostElement, hostRef: d.HostRef, cmpMeta: d.ComponentRuntimeMeta, isInitialLoad: boolean) => {
const then = (promise: Promise<any>, thenFn: () => any) => {
return promise && promise.then ? promise.then(thenFn) : thenFn();
};

export const scheduleUpdate = (elm: d.HostElement, hostRef: d.HostRef, cmpMeta: d.ComponentRuntimeMeta, isInitialLoad: boolean) => {
if (BUILD.taskQueue && BUILD.updatable) {
hostRef.$flags$ |= HOST_FLAGS.isQueuedForUpdate;
}
const instance = BUILD.lazyLoad ? hostRef.$lazyInstance$ : elm as any;
let promise: Promise<void>;
if (isInitialLoad) {
if (BUILD.hostListener) {
hostRef.$flags$ |= HOST_FLAGS.isListenReady;
Expand All @@ -32,31 +38,33 @@ export const scheduleUpdate = async (elm: d.HostElement, hostRef: d.HostRef, cmp
}
emitLifecycleEvent(elm, 'componentWillLoad');
if (BUILD.cmpWillLoad) {
await safeCall(instance, 'componentWillLoad');
promise = safeCall(instance, 'componentWillLoad');
}

} else {
emitLifecycleEvent(elm, 'componentWillUpdate');

if (BUILD.cmpWillUpdate) {
await safeCall(instance, 'componentWillUpdate');
promise = safeCall(instance, 'componentWillUpdate');
}
}

emitLifecycleEvent(elm, 'componentWillRender');
if (BUILD.cmpWillRender) {
await safeCall(instance, 'componentWillRender');
promise = then(promise, () => safeCall(instance, 'componentWillRender'));
}

// there is no ancestorc omponent or the ancestor component
// has already fired off its lifecycle update then
// fire off the initial update
if (BUILD.taskQueue) {
writeTask(() => updateComponent(elm, hostRef, cmpMeta, instance, isInitialLoad));
} else {
// syncronuously write DOM
updateComponent(elm, hostRef, cmpMeta, instance, isInitialLoad);
const update = () => updateComponent(elm, hostRef, cmpMeta, instance, isInitialLoad);
if (promise) {
return promise.then(BUILD.taskQueue
? () => writeTask(update)
: update
);
}
return update();
};

const updateComponent = (elm: d.RenderNode, hostRef: d.HostRef, cmpMeta: d.ComponentRuntimeMeta, instance: any, isInitialLoad: boolean) => {
Expand Down

0 comments on commit 8897c6f

Please sign in to comment.