From 8269ae14199f7faae5b6b0aa2145b3d0d80d0e61 Mon Sep 17 00:00:00 2001 From: harttle Date: Fri, 27 Mar 2020 17:56:02 +0800 Subject: [PATCH] perf: reutilization of RendererCompiler --- src/index.ts | 2 +- src/target-js/compilers/anode-compiler.ts | 9 ++- src/target-js/compilers/element-compiler.ts | 6 +- src/target-js/compilers/renderer-compiler.ts | 59 +++++++++---------- src/target-js/index.ts | 11 ++-- src/utils/emitter.ts | 5 +- .../compilers/renderer-compiler.spec.ts | 4 +- 7 files changed, 46 insertions(+), 50 deletions(-) diff --git a/src/index.ts b/src/index.ts index b4ea7b45..68919cf3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ export { SanProject } from './models/san-project' export { Compiler } from './models/compiler' export { ComponentInfo } from './models/component-info' export { ComponentTree } from './models/component-tree' -export { isComponentLoader, COMPONENT_RESERVED_MEMBERS } from './models/component' +export { COMPONENT_RESERVED_MEMBERS } from './models/component' export { CompiledComponent } from './models/compiled-component' let defaultProject: SanProject diff --git a/src/target-js/compilers/anode-compiler.ts b/src/target-js/compilers/anode-compiler.ts index 5026c0f8..66d1d59d 100644 --- a/src/target-js/compilers/anode-compiler.ts +++ b/src/target-js/compilers/anode-compiler.ts @@ -1,5 +1,4 @@ import { stringLiteralize, expr } from './expr-compiler' -import { isComponentLoader } from '../../models/component' import { ComponentTree } from '../../models/component-tree' import { JSEmitter } from '../emitters/emitter' import { ANode, ExprStringNode, AIfNode, AForNode, ASlotNode, ATemplateNode, ATextNode } from 'san' @@ -16,8 +15,8 @@ export class ANodeCompiler { private ssrIndex = 0 constructor ( - private componentInfo: ComponentInfo, - private componentTree: ComponentTree, + private owner: ComponentInfo, + private root: ComponentTree, private elementSourceCompiler: ElementCompiler, public emitter: JSEmitter ) { @@ -30,9 +29,9 @@ export class ANodeCompiler { if (TypeGuards.isASlotNode(aNode)) return this.compileSlot(aNode) if (TypeGuards.isATemplateNode(aNode)) return this.compileTemplate(aNode) - const ComponentClass = this.componentInfo.getChildComponentClass(aNode) + const ComponentClass = this.owner.getChildComponentClass(aNode) if (ComponentClass) { - const info = this.componentTree.addComponentClass(ComponentClass) + const info = this.root.addComponentClass(ComponentClass) return info ? this.compileComponent(aNode, info) : undefined } return this.compileElement(aNode) diff --git a/src/target-js/compilers/element-compiler.ts b/src/target-js/compilers/element-compiler.ts index 7ce57503..0bfbef03 100644 --- a/src/target-js/compilers/element-compiler.ts +++ b/src/target-js/compilers/element-compiler.ts @@ -14,12 +14,12 @@ import { ComponentTree } from '../../models/component-tree' export class ElementCompiler { private aNodeCompiler: ANodeCompiler constructor ( - componentInfo: ComponentInfo, - componentTree: ComponentTree, + owner: ComponentInfo, + root: ComponentTree, private noTemplateOutput: boolean, public emitter: JSEmitter = new JSEmitter() ) { - this.aNodeCompiler = new ANodeCompiler(componentInfo, componentTree, this, emitter) + this.aNodeCompiler = new ANodeCompiler(owner, root, this, emitter) } /** diff --git a/src/target-js/compilers/renderer-compiler.ts b/src/target-js/compilers/renderer-compiler.ts index 6299a855..350b7b3b 100644 --- a/src/target-js/compilers/renderer-compiler.ts +++ b/src/target-js/compilers/renderer-compiler.ts @@ -49,38 +49,27 @@ export interface CompileContext { * Each ComponentClass is compiled to a render function */ export class RendererCompiler { - private elementCompiler: ElementCompiler - constructor ( - private componentInfo: ComponentInfo, - noTemplateOutput: boolean, - componentTree: ComponentTree, + private noTemplateOutput: boolean, + private componentTree: ComponentTree, public emitter = new JSEmitter() - ) { - this.emitter = emitter - this.elementCompiler = new ElementCompiler( - componentInfo, - componentTree, - noTemplateOutput, - emitter - ) - this.componentInfo = componentInfo - } + ) {} - public compileComponentSource () { + public compileComponentSource (componentInfo: ComponentInfo) { this.emitter.writeAnonymousFunction(RENDERER_ARGS, () => { - this.compileComponentRendererBody() + this.compileComponentRendererBody(componentInfo) }) return this.emitter.fullText() } - public compileComponentRenderer () { - const body = this.compileComponentRendererBody() + public compileComponentRenderer (componentInfo: ComponentInfo) { + this.emitter.clear() + const body = this.compileComponentRendererBody(componentInfo) return new Function(...RENDERER_ARGS, body) // eslint-disable-line no-new-func } - public compileComponentPrototypeSource () { - const component = this.componentInfo.component + public compileComponentPrototypeSource (componentInfo: ComponentInfo) { + const component = componentInfo.component const ComponentProto = component.constructor.prototype const builtinKeys = ['components', '_cmptReady', 'aNode', 'constructor'] const { emitter } = this @@ -125,7 +114,7 @@ export class RendererCompiler { // filters emitter.writeLine('filters: {') emitter.writeIndentedLines(map( - this.componentInfo.filters, + componentInfo.filters, (fn, key) => `${key}: ${functionString(fn)}` ).join(', ')) emitter.writeLine('},') @@ -133,7 +122,7 @@ export class RendererCompiler { // computed obj emitter.writeLine('computed: {') emitter.writeIndentedLines(map( - this.componentInfo.computed, + componentInfo.computed, (fn, key) => `${key}: ${functionString(fn)}` ).join(', ')) emitter.writeLine('},') @@ -142,14 +131,14 @@ export class RendererCompiler { emitter.writeLine('tagName: "' + component.aNode.tagName + '"') } - public compileComponentRendererBody () { + public compileComponentRendererBody (componentInfo: ComponentInfo) { const { emitter } = this - const component = this.componentInfo.component + const component = componentInfo.component emitter.writeLine('var _ = sanssrRuntime._;') emitter.writeLine('var SanData = sanssrRuntime.SanData;') emitter.writeLine('var html = "";') - this.genComponentContextCode() + this.genComponentContextCode(componentInfo) // init data const defaultData = (component.initData && component.initData()) || {} @@ -176,14 +165,20 @@ export class RendererCompiler { emitter.writeLine('if (' + expr(ifDirective.value) + ') {') } - this.elementCompiler.tagStart(component.aNode, 'tagName') + const elementCompiler = new ElementCompiler( + componentInfo, + this.componentTree, + this.noTemplateOutput, + emitter + ) + elementCompiler.tagStart(component.aNode, 'tagName') emitter.writeIf('!noDataOutput', () => { emitter.writeDataComment() }) - this.elementCompiler.inner(component.aNode) - this.elementCompiler.tagEnd(component.aNode, 'tagName') + elementCompiler.inner(component.aNode) + elementCompiler.tagEnd(component.aNode, 'tagName') if (ifDirective) { emitter.writeLine('}') @@ -196,17 +191,17 @@ export class RendererCompiler { /** * 生成组件 renderer 时 ctx 对象构建的代码 */ - private genComponentContextCode () { + private genComponentContextCode (componentInfo: ComponentInfo) { const { emitter } = this emitter.writeBlock('var ctx =', () => { - emitter.writeLine(`instance: _.createFromPrototype(sanssrRuntime.prototype${this.componentInfo.cid}),`) + emitter.writeLine(`instance: _.createFromPrototype(sanssrRuntime.prototype${componentInfo.cid}),`) emitter.writeLine('sourceSlots: sourceSlots,') emitter.writeLine('data: data,') emitter.writeLine('owner: parentCtx,') // computedNames emitter.nextLine('computedNames: [') - emitter.write(Object.keys(this.componentInfo.computed).map(x => `'${x}'`).join(', ')) + emitter.write(Object.keys(componentInfo.computed).map(x => `'${x}'`).join(', ')) emitter.feedLine('],') emitter.writeLine('slotRenderers: {}') diff --git a/src/target-js/index.ts b/src/target-js/index.ts index 5f9d89b6..a686cd3c 100644 --- a/src/target-js/index.ts +++ b/src/target-js/index.ts @@ -44,15 +44,14 @@ export default class ToJSCompiler implements Compiler { function emitFunctionBody () { emitRuntime(emitter, 'sanssrRuntime') + const cc = new RendererCompiler(noTemplateOutput, sanApp.componentTree, emitter) for (const info of sanApp.componentTree.preOrder()) { const { cid } = info - const cc = new RendererCompiler(info, noTemplateOutput, sanApp.componentTree, emitter) - emitter.writeBlock(`sanssrRuntime.prototype${cid} =`, () => { - cc.compileComponentPrototypeSource() + cc.compileComponentPrototypeSource(info) }) emitter.nextLine(`sanssrRuntime.renderer${cid} = `) - cc.compileComponentSource() + cc.compileComponentSource(info) } const funcName = 'sanssrRuntime.renderer' + sanApp.componentTree.root.cid emitter.writeLine(`return ${funcName}(data, noDataOutput, sanssrRuntime)`) @@ -65,12 +64,12 @@ export default class ToJSCompiler implements Compiler { noTemplateOutput = false }: ToJSCompileOptions = {}): Renderer { const sanssrRuntime = { _, SanData } + const cc = new RendererCompiler(noTemplateOutput, sanApp.componentTree) for (const info of sanApp.componentTree.preOrder()) { const { cid } = info - const cc = new RendererCompiler(info, noTemplateOutput, sanApp.componentTree) sanssrRuntime[`prototype${cid}`] = info.component - sanssrRuntime[`renderer${cid}`] = cc.compileComponentRenderer() + sanssrRuntime[`renderer${cid}`] = cc.compileComponentRenderer(info) } const entryComponentId = sanApp.componentTree.root.cid return (data: any, noDataOutput: boolean = false) => { diff --git a/src/utils/emitter.ts b/src/utils/emitter.ts index c5449f2d..0e931f82 100644 --- a/src/utils/emitter.ts +++ b/src/utils/emitter.ts @@ -6,7 +6,10 @@ export abstract class Emitter { constructor (shiftWidth = 4) { this.shiftWidth = shiftWidth } - + public clear () { + this.code = '' + this.indentLevel = 0 + } public abstract write (str: string): void public fullText () { return this.code diff --git a/test/unit/target-js/compilers/renderer-compiler.spec.ts b/test/unit/target-js/compilers/renderer-compiler.spec.ts index 7e7764d6..405d4658 100644 --- a/test/unit/target-js/compilers/renderer-compiler.spec.ts +++ b/test/unit/target-js/compilers/renderer-compiler.spec.ts @@ -13,8 +13,8 @@ describe('target-js/compilers/renderer-compiler', () => { component: new ComponentClass(), componentClass: ComponentClass } as any) - const compiler = new RendererCompiler(info, false, {} as any) - compiler.compileComponentPrototypeSource() + const compiler = new RendererCompiler(false, {} as any) + compiler.compileComponentPrototypeSource(info) expect(compiler.emitter.fullText()).toEqual(`foo: [ 1, x => x