[REF] move component function to app, improve some code

This commit is contained in:
Géry Debongnie
2022-06-08 11:09:39 +02:00
committed by Sam Degueldre
parent 51538c2fea
commit 0e6059467f
53 changed files with 2522 additions and 1975 deletions
+17 -6
View File
@@ -264,9 +264,7 @@ export class CodeGenerator {
tKeyExpr: null,
});
// define blocks and utility functions
let mainCode = [
` let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;`,
];
let mainCode = [` let { text, createBlock, list, multi, html, toggler, comment } = bdom;`];
if (this.helpers.size) {
mainCode.push(`let { ${[...this.helpers].join(", ")} } = helpers;`);
}
@@ -1178,8 +1176,15 @@ export class CodeGenerator {
if (ctx.tKeyExpr) {
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
}
const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`;
let blockExpr = `component(${blockArgs})`;
let id = generateId("comp");
this.staticDefs.push({
id,
expr: `app.createComponent(${
ast.isDynamic ? null : expr
}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps})`,
});
let blockExpr = `${id}(${propString}, ${keyArg}, node, ctx, ${ast.isDynamic ? expr : null})`;
if (ast.isDynamic) {
blockExpr = `toggler(${expr}, ${blockExpr})`;
}
@@ -1270,8 +1275,14 @@ export class CodeGenerator {
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx)`);
}
let id = generateId("comp");
this.staticDefs.push({
id,
expr: `app.createComponent(null, false, true, false)`,
});
const target = compileExpr(ast.target);
const blockString = `component(Portal, {target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx)`;
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`;
if (block) {
this.insertAnchor(block);
}
+67 -5
View File
@@ -1,11 +1,12 @@
import { Component, ComponentConstructor } from "./component";
import { Component, ComponentConstructor, Props } from "./component";
import { ComponentNode } from "./component_node";
import { MountOptions } from "./fibers";
import { Scheduler } from "./scheduler";
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { nodeErrorHandlers } from "./error_handling";
import { validateTarget } from "./utils";
import { Fiber, MountOptions } from "./fibers";
import { Scheduler } from "./scheduler";
import { STATUS } from "./status";
import { validateProps } from "./template_helpers";
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { validateTarget } from "./utils";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
@@ -112,6 +113,67 @@ export class App<
this.root.destroy();
}
}
createComponent<P extends Props>(
name: string | null,
isStatic: boolean,
hasSlotsProp: boolean,
hasDynamicPropList: boolean
) {
const isDynamic = !isStatic;
function _arePropsDifferent(props1: Props, props2: Props): boolean {
for (let k in props1) {
if (props1[k] !== props2[k]) {
return true;
}
}
return hasDynamicPropList && Object.keys(props1).length !== Object.keys(props2).length;
}
const arePropsDifferent = hasSlotsProp ? () => true : _arePropsDifferent;
return (props: P, key: string, ctx: ComponentNode, parent: any, C: any) => {
let children = ctx.children;
let node: any = children[key];
if (node && node.status === STATUS.DESTROYED) {
node = undefined;
}
if (isDynamic && node && node.component.constructor !== C) {
node = undefined;
}
const parentFiber = ctx.fiber!;
if (node) {
let shouldRender = node.forceNextRender;
if (shouldRender) {
node.forceNextRender = false;
} else {
const currentProps = node.props;
shouldRender = parentFiber.deep || arePropsDifferent(currentProps, props);
}
if (shouldRender) {
node.updateAndRender(props, parentFiber);
}
} else {
// new component
if (isStatic) {
C = parent.constructor.components[name as any];
if (!C) {
throw new Error(`Cannot find the definition of component "${name}"`);
} else if (!(C.prototype instanceof Component)) {
throw new Error(
`"${name}" is not a Component. It must inherit from the Component class`
);
}
}
node = new ComponentNode(C, props, this, ctx, key);
children[key] = node;
node.initiateRender(new Fiber(node, parentFiber));
}
parentFiber.childrenMap[key] = node;
return node;
};
}
}
export async function mount<
+1 -1
View File
@@ -5,7 +5,7 @@ import type { ComponentNode } from "./component_node";
// Component Class
// -----------------------------------------------------------------------------
type Props = { [key: string]: any };
export type Props = { [key: string]: any };
interface StaticComponentProperties {
template: string;
+10 -77
View File
@@ -1,19 +1,18 @@
import type { App, Env } from "./app";
import { BDom, VNode } from "./blockdom";
import { Component, ComponentConstructor, Props } from "./component";
import { fibersInError, handleError } from "./error_handling";
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
import {
clearReactivesForCallback,
getSubscriptions,
NonReactive,
Reactive,
reactive,
toRaw,
TARGET,
} from "./reactivity";
import { batched, Callback } from "./utils";
import { Component, ComponentConstructor } from "./component";
import { fibersInError, handleError } from "./error_handling";
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
import { STATUS } from "./status";
import { batched, Callback } from "./utils";
let currentNode: ComponentNode | null = null;
@@ -31,14 +30,12 @@ export function useComponent(): Component {
/**
* Apply default props (only top level).
*/
function applyDefaultProps<P extends object>(props: P, defaultProps: Partial<P>): P {
const result = Object.assign({} as any, props);
function applyDefaultProps<P extends object>(props: P, defaultProps: Partial<P>) {
for (let propName in defaultProps) {
if (props[propName] === undefined) {
result[propName] = defaultProps[propName];
(props as any)[propName] = defaultProps[propName];
}
}
return result;
}
// -----------------------------------------------------------------------------
// Integration with reactivity system (useState)
@@ -67,72 +64,6 @@ export function useState<T extends object>(state: T): Reactive<T> | NonReactive<
return reactive(state, render);
}
// -----------------------------------------------------------------------------
// component function (used in compiled template code)
// -----------------------------------------------------------------------------
type Props = { [key: string]: any };
function arePropsDifferent(props1: Props, props2: Props): boolean {
for (let k in props1) {
const prop1 = props1[k] && typeof props1[k] === "object" ? toRaw(props1[k]) : props1[k];
const prop2 = props2[k] && typeof props2[k] === "object" ? toRaw(props2[k]) : props2[k];
if (prop1 !== prop2) {
return true;
}
}
return Object.keys(props1).length !== Object.keys(props2).length;
}
export function component<P extends Props>(
name: string | ComponentConstructor<P>,
props: P,
key: string,
ctx: ComponentNode,
parent: any
): ComponentNode<P> {
let node: any = ctx.children[key];
let isDynamic = typeof name !== "string";
if (node && node.status === STATUS.DESTROYED) {
node = undefined;
}
if (isDynamic && node && node.component.constructor !== name) {
node = undefined;
}
const parentFiber = ctx.fiber!;
if (node) {
let shouldRender = node.forceNextRender;
if (shouldRender) {
node.forceNextRender = false;
} else {
const currentProps = node.props;
shouldRender = parentFiber.deep || arePropsDifferent(currentProps, props);
}
if (shouldRender) {
node.updateAndRender(props, parentFiber);
}
} else {
// new component
let C;
if (isDynamic) {
C = name;
} else {
C = parent.constructor.components[name as any];
if (!C) {
throw new Error(`Cannot find the definition of component "${name}"`);
} else if (!(C.prototype instanceof Component)) {
throw new Error(`"${name}" is not a Component. It must inherit from the Component class`);
}
}
node = new ComponentNode(C, props, ctx.app, ctx, key);
ctx.children[key] = node;
node.initiateRender(new Fiber(node, parentFiber));
}
parentFiber.childrenMap[key] = node;
return node;
}
// -----------------------------------------------------------------------------
// Component VNode class
// -----------------------------------------------------------------------------
@@ -179,8 +110,9 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
this.parentKey = parentKey;
this.level = parent ? parent.level + 1 : 0;
const defaultProps = C.defaultProps;
props = Object.assign({}, props);
if (defaultProps) {
props = applyDefaultProps(props, defaultProps);
applyDefaultProps(props, defaultProps);
}
const env = (parent && parent.childEnv) || app.env;
this.childEnv = env;
@@ -297,13 +229,14 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
async updateAndRender(props: P, parentFiber: Fiber) {
const rawProps = props;
props = Object.assign({}, props);
// update
const fiber = makeChildFiber(this, parentFiber);
this.fiber = fiber;
const component = this.component;
const defaultProps = (component.constructor as any).defaultProps;
if (defaultProps) {
props = applyDefaultProps(props, defaultProps);
applyDefaultProps(props, defaultProps);
}
currentNode = this;
+3 -3
View File
@@ -1,10 +1,10 @@
import { createBlock, html, list, multi, text, toggler, comment } from "./blockdom";
import { compile, Template, TemplateFunction } from "../compiler";
import { comment, createBlock, html, list, multi, text, toggler } from "./blockdom";
import { getCurrent } from "./component_node";
import { Portal, portalTemplate } from "./portal";
import { component, getCurrent } from "./component_node";
import { helpers } from "./template_helpers";
const bdom = { text, createBlock, list, multi, html, toggler, component, comment };
const bdom = { text, createBlock, list, multi, html, toggler, comment };
function parseXML(xml: string): Document {
const parser = new DOMParser();