diff --git a/src/bindings/angular/providers/ionic-core.ts b/src/bindings/angular/providers/ionic-core.ts index 4d84fc9867..130f6e2e26 100644 --- a/src/bindings/angular/providers/ionic-core.ts +++ b/src/bindings/angular/providers/ionic-core.ts @@ -60,58 +60,108 @@ export function setupCore(config: Config, plt: Platform, domCtrl: DomController, function getQueueController(win: any, zone: NgZone) { - const hostScheduleDefer: Function = win['requestIdleCallback']; - const callbacks: Function[] = []; - let pending = false; + function now() { + return win.performance.now(); + } - function doWork(deadlineObj: any) { - // let's see if we've got time to take care of things - while (deadlineObj.timeRemaining() > 1 && callbacks.length > 0) { - // do some work while within the allowed time - // shift the array and fire off the callbacks from the beginning - // once we run out of time or callbacks we'll stop - callbacks.shift()(); + const highPromise = Promise.resolve(); + + const highCallbacks: Function[] = []; + const mediumCallbacks: Function[] = []; + const lowCallbacks: Function[] = []; + + let resolvePending = false; + let ricPending = false; + + + function doHighPriority() { + // holy geez we need to get this stuff done and fast + // all high priority callbacks should be fired off immediately + while (highCallbacks.length > 0) { + highCallbacks.shift()(); + } + resolvePending = false; + } + + + function doWork() { + const start = now(); + + // always run all of the high priority work if there is any + doHighPriority(); + + while (mediumCallbacks.length > 0 && (now() - start < 40)) { + mediumCallbacks.shift()(); + } + + if (mediumCallbacks.length === 0) { + // we successfully drained the medium queue or the medium queue is empty + // so now let's drain the low queue with our remaining time + while (lowCallbacks.length > 0 && (now() - start < 40)) { + lowCallbacks.shift()(); + } } // check to see if we still have work to do - if (pending = (callbacks.length > 0)) { + if (ricPending = (mediumCallbacks.length > 0 || lowCallbacks.length > 0)) { // everyone just settle down now // we already don't have time to do anything in this callback // let's throw the next one in a requestAnimationFrame // so we can just simmer down for a bit zone.runOutsideAngular(() => { - requestAnimationFrame(flush); + win.requestAnimationFrame(flush); }); } } function flush() { - // always force a bunch of callbacks to run, but still have + // always run all of the high priority work if there is any + doHighPriority(); + + // always force a bunch of medium callbacks to run, but still have // a throttle on how many can run in a certain time - const start = performance.now(); - while (callbacks.length > 0 && (performance.now() - start < 4)) { - callbacks.shift()(); + const start = now(); + while (mediumCallbacks.length > 0 && (now() - start < 4)) { + mediumCallbacks.shift()(); } - if (pending = (callbacks.length > 0)) { + if (ricPending = (mediumCallbacks.length > 0 || lowCallbacks.length > 0)) { // still more to do yet, but we've run out of time - // let's let thing cool off and try again after a raf + // let's let this thing cool off and try again in the next ric zone.runOutsideAngular(() => { - hostScheduleDefer(doWork); + win.requestAnimationFrame(doWork); }); } } - function add(cb: Function) { - // add the work to the end of the callbacks - callbacks.push(cb); + function add(cb: Function, priority?: number) { + if (priority === 3) { + // uses Promise.resolve() for next tick + highCallbacks.push(cb); - if (!pending) { - // not already pending work to do, so let's tee it up - pending = true; - zone.runOutsideAngular(() => { - hostScheduleDefer(doWork); - }); + if (!resolvePending) { + // not already pending work to do, so let's tee it up + resolvePending = true; + highPromise.then(doHighPriority); + } + + } else { + if (priority === 1) { + lowCallbacks.push(cb); + + } else { + // defaults to medium priority + // uses requestIdleCallback + mediumCallbacks.push(cb); + } + + if (!ricPending) { + // not already pending work to do, so let's tee it up + ricPending = true; + zone.runOutsideAngular(() => { + win.requestAnimationFrame(doWork); + }); + } } } diff --git a/src/platform/dom-controller.ts b/src/platform/dom-controller.ts index 8d5675ce27..bbfd0def5f 100644 --- a/src/platform/dom-controller.ts +++ b/src/platform/dom-controller.ts @@ -102,6 +102,14 @@ export class DomController { } } + now() { + return this.plt.win().performance.now(); + } + + raf(cb: any) { + return this.plt.raf(cb); + } + private _queue() { const self = this; if (!self.q) { diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index e4a87746c0..313245d5bb 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -260,6 +260,7 @@ export interface DomControllerApi { read: DomControllerCallback; write: DomControllerCallback; raf: DomControllerCallback; + now(): number; } export interface RafCallback { @@ -285,15 +286,20 @@ export interface LoadComponentData { [modeCode: string]: string; }; + /** + * slot meta + */ + [2]: number; + /** * props */ - [2]: any[]; + [3]: any[]; /** * bundle priority */ - [3]: LoadPriority; + [4]: LoadPriority; } @@ -413,7 +419,7 @@ export interface ComponentWatchersData { export interface ModulesImporterFn { - (importer: any, h: Function, Ionic: Ionic): void; + (importer: any, h: Function, t: Function, Ionic: Ionic): void; } @@ -527,8 +533,7 @@ export interface ComponentMeta { modesMeta?: {[modeCode: string]: ModeMeta}; isShadowMeta?: boolean; hostMeta?: HostMeta; - hasSlotsMeta?: boolean; - namedSlotsMeta?: string[]; + slotMeta?: number; componentModuleMeta?: any; priorityMeta?: LoadPriority; } @@ -618,7 +623,7 @@ export interface HostElement extends HTMLElement { $instance?: Component; // private methods - _render: (isInitialRender: boolean) => void; + _render: (isUpdateRender?: boolean) => void; _initLoad: () => void; _queueUpdate: () => void; @@ -638,25 +643,23 @@ export interface HostElement extends HTMLElement { export interface RendererApi { - (oldVnode: VNode | Element, vnode: VNode, hostContentNodes?: HostContentNodes, hydrating?: boolean): VNode; + (oldVNode: VNode | Element, newVNode: VNode, isUpdate?: boolean, hostContentNodes?: HostContentNodes): VNode; } export interface DomApi { $head: HTMLHeadElement; $body: HTMLElement; - $isElement(node: any): boolean; - $isText(node: any): boolean; - $isComment(node: any): boolean; + $nodeType(node: any): number; $createEvent(): CustomEvent; $createElement(tagName: K): HTMLElementTagNameMap[K]; $createElement(tagName: string): HTMLElement; $createElementNS(namespace: string, tagName: string): any; $createTextNode(text: string): Text; - $createComment(text: string): Comment; $insertBefore(parentNode: Node, newNode: Node, referenceNode: Node): void; $removeChild(node: Node, child: Node): void; $appendChild(node: Node, child: Node): void; + $childNodes(node: Node): NodeList; $parentNode(node: Node): Node; $nextSibling(node: Node): Node; $tagName(elm: any): string; @@ -683,32 +686,80 @@ export interface Hyperscript { } -export interface VNode { - sel?: string | undefined; - vdata?: VNodeData | undefined; - vchildren?: Array | undefined; - elm?: Node | undefined | HostElement; - vtext?: string | undefined; - vkey?: Key; -} - - export interface HostContentNodes { defaultSlot?: Node[]; namedSlots?: {[slotName: string]: Node[]}; } + export type CssClassObject = { [className: string]: boolean }; + + +export interface VNode { + // using v prefixes largely so closure has no issue property renaming + vtag: string; + vtext: string; + vchildren: VNode[]; + vprops: any; + vattrs: any; + vclass: CssClassObject; + vstyle: any; + vlisteners: any; + vkey: Key; + elm: Element|Node; + vnamespace: any; + assignedListener: any; + skipDataOnUpdate: boolean; + skipChildrenOnUpdate: boolean; +} + export interface VNodeData { props?: any; attrs?: any; class?: CssClassObject; style?: any; - dataset?: any; on?: any; - vkey?: Key; - vns?: string; // for SVGs + key?: Key; + ns?: any; // for SVGs +} + +/** + * used by production compiler + */ +export interface VNodeProdData { + /** + * props + */ + p?: any; + /** + * attrs + */ + a?: any; + /** + * css classes + */ + c?: CssClassObject; + /** + * styles + */ + s?: any; + /** + * on (event listeners) + */ + o?: any; + /** + * key + */ + k?: Key; + /** + * namespace + */ + n?: any; + /** + * check once + */ + x?: number; } @@ -719,7 +770,7 @@ export interface PlatformApi { loadBundle: (bundleId: string, priority: LoadPriority, cb: Function) => void; render?: RendererApi; config: ConfigApi; - collectHostContent: (elm: HostElement, validNamedSlots: string[]) => void; + collectHostContent: (elm: HostElement, slotMeta: number) => void; queue: QueueApi; css?: {[cmpModeId: string]: string}; isServer?: boolean;