mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf7730a34a | |||
| a968bb7ab5 | |||
| 4cd1df46b3 |
@@ -116,10 +116,9 @@ export function component<P extends object>(
|
|||||||
}
|
}
|
||||||
node = new ComponentNode(C, props, ctx.app, ctx);
|
node = new ComponentNode(C, props, ctx.app, ctx);
|
||||||
ctx.children[key] = node;
|
ctx.children[key] = node;
|
||||||
node.initiateRender(new Fiber(node, parentFiber));
|
|
||||||
}
|
|
||||||
parentFiber.childrenMap[key] = node;
|
|
||||||
|
|
||||||
|
node.initiateRender(makeChildFiber(node, parentFiber));
|
||||||
|
}
|
||||||
parentFiber.root!.reachedChildren.add(node);
|
parentFiber.root!.reachedChildren.add(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -327,7 +326,6 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
|
|||||||
bdom.mount(parent, anchor);
|
bdom.mount(parent, anchor);
|
||||||
this.status = STATUS.MOUNTED;
|
this.status = STATUS.MOUNTED;
|
||||||
this.fiber!.appliedToDom = true;
|
this.fiber!.appliedToDom = true;
|
||||||
this.children = this.fiber!.childrenMap;
|
|
||||||
this.fiber = null;
|
this.fiber = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +334,7 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
|
|||||||
}
|
}
|
||||||
|
|
||||||
patch() {
|
patch() {
|
||||||
|
debugger
|
||||||
if (this.fiber && this.fiber.parent) {
|
if (this.fiber && this.fiber.parent) {
|
||||||
// we only patch here renderings coming from above. renderings initiated
|
// we only patch here renderings coming from above. renderings initiated
|
||||||
// by the component will be patched independently in the appropriate
|
// by the component will be patched independently in the appropriate
|
||||||
@@ -345,8 +344,10 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
|
|||||||
}
|
}
|
||||||
_patch() {
|
_patch() {
|
||||||
const hasChildren = Object.keys(this.children).length > 0;
|
const hasChildren = Object.keys(this.children).length > 0;
|
||||||
this.children = this.fiber!.childrenMap;
|
|
||||||
this.bdom!.patch(this!.fiber!.bdom!, hasChildren);
|
this.bdom!.patch(this!.fiber!.bdom!, hasChildren);
|
||||||
|
if (hasChildren) {
|
||||||
|
this.cleanOutdatedChildren();
|
||||||
|
}
|
||||||
this.fiber!.appliedToDom = true;
|
this.fiber!.appliedToDom = true;
|
||||||
this.fiber = null;
|
this.fiber = null;
|
||||||
}
|
}
|
||||||
@@ -359,6 +360,20 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
|
|||||||
this.bdom!.remove();
|
this.bdom!.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanOutdatedChildren() {
|
||||||
|
const children = this.children;
|
||||||
|
for (const key in children) {
|
||||||
|
const node = children[key];
|
||||||
|
const status = node.status;
|
||||||
|
if (status !== STATUS.MOUNTED) {
|
||||||
|
delete children[key];
|
||||||
|
if (status !== STATUS.DESTROYED) {
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Some debug helpers
|
// Some debug helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+70
-37
@@ -1,6 +1,6 @@
|
|||||||
import { BDom, mount } from "../blockdom";
|
import { BDom, mount } from "../blockdom";
|
||||||
import type { ComponentNode } from "./component_node";
|
import type { ComponentNode } from "./component_node";
|
||||||
import { fibersInError, handleError } from "./error_handling";
|
import { handleError } from "./error_handling";
|
||||||
import { STATUS } from "./status";
|
import { STATUS } from "./status";
|
||||||
|
|
||||||
export function makeChildFiber(node: ComponentNode, parent: Fiber): Fiber {
|
export function makeChildFiber(node: ComponentNode, parent: Fiber): Fiber {
|
||||||
@@ -9,28 +9,72 @@ export function makeChildFiber(node: ComponentNode, parent: Fiber): Fiber {
|
|||||||
cancelFibers(current.children);
|
cancelFibers(current.children);
|
||||||
current.root = null;
|
current.root = null;
|
||||||
}
|
}
|
||||||
return new Fiber(node, parent);
|
let fiber = new Fiber(node);
|
||||||
|
fiber.deep = parent.deep;
|
||||||
|
const root = parent.root!;
|
||||||
|
fiber.root = root;
|
||||||
|
parent.children.push(fiber);
|
||||||
|
fiber.parent = parent;
|
||||||
|
root.setCounter(root.counter + 1);
|
||||||
|
return fiber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeRootFiber(node: ComponentNode): Fiber {
|
export function makeRootFiber(node: ComponentNode): Fiber {
|
||||||
let current = node.fiber;
|
let current = node.fiber;
|
||||||
if (current) {
|
if (current) {
|
||||||
let root = current.root!;
|
debugger;
|
||||||
root.setCounter(root.counter + 1 - cancelFibers(current.children));
|
let parent = current.parent;
|
||||||
current.children = [];
|
let n = cancelFibers(current.children);
|
||||||
current.childrenMap = {};
|
current.root = null;
|
||||||
current.bdom = null;
|
// current.bdom = null;
|
||||||
if (current === root) {
|
if (parent) {
|
||||||
root.reachedChildren = new WeakSet();
|
let fiber = new Fiber(node);
|
||||||
|
fiber.deep = parent.deep;
|
||||||
|
const root = parent.root!;
|
||||||
|
fiber.root = root;
|
||||||
|
let index = parent.children.indexOf(current);
|
||||||
|
parent.children[index] = fiber;
|
||||||
|
// parent.children.push(fiber);
|
||||||
|
fiber.parent = parent;
|
||||||
|
// node.fiber =
|
||||||
|
root.setCounter(root.counter + 1 - n);
|
||||||
|
if (node.willPatch.length) {
|
||||||
|
let index = root.willPatch.indexOf(current);
|
||||||
|
if (index >= 0) {
|
||||||
|
root.willPatch[index] = fiber;
|
||||||
}
|
}
|
||||||
if (fibersInError.has(current)) {
|
|
||||||
fibersInError.delete(current);
|
|
||||||
fibersInError.delete(root);
|
|
||||||
current.appliedToDom = false;
|
|
||||||
}
|
}
|
||||||
return current;
|
if (node.patched.length) {
|
||||||
|
let index = root.patched.indexOf(current);
|
||||||
|
if (index >= 0) {
|
||||||
|
root.patched[index] = fiber;
|
||||||
}
|
}
|
||||||
const fiber = new RootFiber(node, null);
|
}
|
||||||
|
|
||||||
|
return fiber;
|
||||||
|
}
|
||||||
|
// let parent = current.parent;
|
||||||
|
// let fiber = new Fiber(node);
|
||||||
|
// return fiber;
|
||||||
|
|
||||||
|
// cancelFibers(current.children);
|
||||||
|
// current.root = null;
|
||||||
|
// let root = current.root!;
|
||||||
|
// root.setCounter(root.counter + 1 - cancelFibers(current.children));
|
||||||
|
// current.children = [];
|
||||||
|
// current.bdom = null;
|
||||||
|
// if (current === root) {
|
||||||
|
// root.reachedChildren = new WeakSet();
|
||||||
|
// }
|
||||||
|
// if (fibersInError.has(current)) {
|
||||||
|
// fibersInError.delete(current);
|
||||||
|
// fibersInError.delete(root);
|
||||||
|
// current.appliedToDom = false;
|
||||||
|
// }
|
||||||
|
// return current;
|
||||||
|
}
|
||||||
|
const fiber = new RootFiber(node);
|
||||||
|
fiber.root = fiber;
|
||||||
if (node.willPatch.length) {
|
if (node.willPatch.length) {
|
||||||
fiber.willPatch.push(fiber);
|
fiber.willPatch.push(fiber);
|
||||||
}
|
}
|
||||||
@@ -46,11 +90,7 @@ export function makeRootFiber(node: ComponentNode): Fiber {
|
|||||||
function cancelFibers(fibers: Fiber[]): number {
|
function cancelFibers(fibers: Fiber[]): number {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
for (let fiber of fibers) {
|
for (let fiber of fibers) {
|
||||||
let node = fiber.node;
|
fiber.node.fiber = null;
|
||||||
if (node.status === STATUS.NEW) {
|
|
||||||
node.destroy();
|
|
||||||
}
|
|
||||||
node.fiber = null;
|
|
||||||
if (fiber.bdom) {
|
if (fiber.bdom) {
|
||||||
// if fiber has been rendered, this means that the component props have
|
// if fiber has been rendered, this means that the component props have
|
||||||
// been updated. however, this fiber will not be patched to the dom, so
|
// been updated. however, this fiber will not be patched to the dom, so
|
||||||
@@ -58,7 +98,7 @@ function cancelFibers(fibers: Fiber[]): number {
|
|||||||
// the same props, and skip the render completely. With the next line,
|
// the same props, and skip the render completely. With the next line,
|
||||||
// we kindly request the component code to force a render, so it works as
|
// we kindly request the component code to force a render, so it works as
|
||||||
// expected.
|
// expected.
|
||||||
node.forceNextRender = true;
|
fiber.node.forceNextRender = true;
|
||||||
} else {
|
} else {
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
@@ -67,28 +107,20 @@ function cancelFibers(fibers: Fiber[]): number {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(window as any).fibers = [];
|
||||||
|
|
||||||
export class Fiber {
|
export class Fiber {
|
||||||
node: ComponentNode;
|
node: ComponentNode;
|
||||||
bdom: BDom | null = null;
|
bdom: BDom | null = null;
|
||||||
root: RootFiber | null; // A Fiber that has been replaced by another has no root
|
root: RootFiber | null = null; // A Fiber that has been replaced by another has no root
|
||||||
parent: Fiber | null;
|
parent: Fiber | null = null;
|
||||||
children: Fiber[] = [];
|
children: Fiber[] = [];
|
||||||
appliedToDom = false;
|
appliedToDom = false;
|
||||||
deep: boolean = false;
|
deep: boolean = false;
|
||||||
childrenMap: ComponentNode["children"] = {};
|
|
||||||
|
|
||||||
constructor(node: ComponentNode, parent: Fiber | null) {
|
constructor(node: ComponentNode) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.parent = parent;
|
(window as any).fibers.push(this);
|
||||||
if (parent) {
|
|
||||||
this.deep = parent.deep;
|
|
||||||
const root = parent.root!;
|
|
||||||
root.setCounter(root.counter + 1);
|
|
||||||
this.root = root;
|
|
||||||
parent.children.push(this);
|
|
||||||
} else {
|
|
||||||
this.root = this as any;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -198,6 +230,7 @@ export class RootFiber extends Fiber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCounter(newValue: number) {
|
setCounter(newValue: number) {
|
||||||
|
debugger;
|
||||||
this.counter = newValue;
|
this.counter = newValue;
|
||||||
if (newValue === 0) {
|
if (newValue === 0) {
|
||||||
this.node.app.scheduler.flush();
|
this.node.app.scheduler.flush();
|
||||||
@@ -216,15 +249,15 @@ export class MountFiber extends RootFiber {
|
|||||||
position: Position;
|
position: Position;
|
||||||
|
|
||||||
constructor(node: ComponentNode, target: HTMLElement, options: MountOptions = {}) {
|
constructor(node: ComponentNode, target: HTMLElement, options: MountOptions = {}) {
|
||||||
super(node, null);
|
super(node);
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
this.root = this;
|
||||||
this.position = options.position || "last-child";
|
this.position = options.position || "last-child";
|
||||||
}
|
}
|
||||||
complete() {
|
complete() {
|
||||||
let current: Fiber | undefined = this;
|
let current: Fiber | undefined = this;
|
||||||
try {
|
try {
|
||||||
const node = this.node;
|
const node = this.node;
|
||||||
node.children = this.childrenMap;
|
|
||||||
(node.app.constructor as any).validateTarget(this.target);
|
(node.app.constructor as any).validateTarget(this.target);
|
||||||
if (node.bdom) {
|
if (node.bdom) {
|
||||||
// this is a complicated situation: if we mount a fiber with an existing
|
// this is a complicated situation: if we mount a fiber with an existing
|
||||||
|
|||||||
@@ -114,8 +114,8 @@ test("destroying/recreating a subwidget with different props (if start is not ov
|
|||||||
expect(n).toBe(2);
|
expect(n).toBe(2);
|
||||||
|
|
||||||
expect([
|
expect([
|
||||||
"Child:willDestroy",
|
|
||||||
"W:willRender",
|
"W:willRender",
|
||||||
|
"Child:willDestroy",
|
||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"W:rendered",
|
"W:rendered",
|
||||||
@@ -244,8 +244,8 @@ test("creating two async components, scenario 1", async () => {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("");
|
expect(fixture.innerHTML).toBe("");
|
||||||
expect([
|
expect([
|
||||||
"ChildA:willDestroy",
|
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
|
"ChildA:willDestroy",
|
||||||
"ChildA:setup",
|
"ChildA:setup",
|
||||||
"ChildA:willStart",
|
"ChildA:willStart",
|
||||||
"ChildB:setup",
|
"ChildB:setup",
|
||||||
@@ -696,8 +696,8 @@ test("rendering component again in next microtick", async () => {
|
|||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
"Child:willDestroy",
|
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
|
"Child:willDestroy",
|
||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
@@ -870,6 +870,7 @@ test("concurrent renderings scenario 2", async () => {
|
|||||||
"ComponentB:rendered",
|
"ComponentB:rendered",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
|
|
||||||
|
debugger;
|
||||||
stateB.fromB = "c";
|
stateB.fromB = "c";
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div>1<p><span>1b</span></p></div>");
|
expect(fixture.innerHTML).toBe("<div>1<p><span>1b</span></p></div>");
|
||||||
@@ -881,7 +882,7 @@ test("concurrent renderings scenario 2", async () => {
|
|||||||
|
|
||||||
defs[1].resolve(); // resolve rendering initiated in B
|
defs[1].resolve(); // resolve rendering initiated in B
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div>2<p><span>2c</span></p></div>");
|
debugger
|
||||||
expect([
|
expect([
|
||||||
"ComponentC:willRender",
|
"ComponentC:willRender",
|
||||||
"ComponentC:rendered",
|
"ComponentC:rendered",
|
||||||
@@ -892,6 +893,7 @@ test("concurrent renderings scenario 2", async () => {
|
|||||||
"ComponentB:patched",
|
"ComponentB:patched",
|
||||||
"ComponentA:patched",
|
"ComponentA:patched",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>2<p><span>2c</span></p></div>");
|
||||||
|
|
||||||
defs[0].resolve(); // resolve rendering initiated in A
|
defs[0].resolve(); // resolve rendering initiated in A
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@@ -1725,7 +1727,6 @@ test("concurrent renderings scenario 10", async () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><p></p></div>");
|
expect(fixture.innerHTML).toBe("<div><p></p></div>");
|
||||||
expect([
|
expect([
|
||||||
"ComponentA:willRender",
|
"ComponentA:willRender",
|
||||||
"ComponentC:willDestroy",
|
|
||||||
"ComponentB:willUpdateProps",
|
"ComponentB:willUpdateProps",
|
||||||
"ComponentA:rendered",
|
"ComponentA:rendered",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
@@ -1736,6 +1737,7 @@ test("concurrent renderings scenario 10", async () => {
|
|||||||
expect(rendered).toBe(1);
|
expect(rendered).toBe(1);
|
||||||
expect([
|
expect([
|
||||||
"ComponentB:willRender",
|
"ComponentB:willRender",
|
||||||
|
"ComponentC:willDestroy",
|
||||||
"ComponentC:setup",
|
"ComponentC:setup",
|
||||||
"ComponentC:willStart",
|
"ComponentC:willStart",
|
||||||
"ComponentB:rendered",
|
"ComponentB:rendered",
|
||||||
@@ -2275,11 +2277,11 @@ test("concurrent renderings scenario 16", async () => {
|
|||||||
"D:setup",
|
"D:setup",
|
||||||
"D:willStart",
|
"D:willStart",
|
||||||
"C:rendered",
|
"C:rendered",
|
||||||
"D:willDestroy",
|
|
||||||
"B:willRender",
|
"B:willRender",
|
||||||
"C:willUpdateProps",
|
"C:willUpdateProps",
|
||||||
"B:rendered",
|
"B:rendered",
|
||||||
"C:willRender",
|
"C:willRender",
|
||||||
|
"D:willDestroy",
|
||||||
"D:setup",
|
"D:setup",
|
||||||
"D:willStart",
|
"D:willStart",
|
||||||
"C:rendered",
|
"C:rendered",
|
||||||
@@ -2990,13 +2992,13 @@ test("t-key on dom node having a component", async () => {
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect([
|
expect([
|
||||||
"Child (2):willDestroy",
|
|
||||||
"Child (3):setup",
|
"Child (3):setup",
|
||||||
"Child (3):willStart",
|
"Child (3):willStart",
|
||||||
"Child (3):willRender",
|
"Child (3):willRender",
|
||||||
"Child (3):rendered",
|
"Child (3):rendered",
|
||||||
"Child (1):willUnmount",
|
"Child (1):willUnmount",
|
||||||
"Child (1):willDestroy",
|
"Child (1):willDestroy",
|
||||||
|
"Child (2):willDestroy",
|
||||||
"Child (3):mounted",
|
"Child (3):mounted",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
});
|
});
|
||||||
@@ -3048,13 +3050,13 @@ test("t-key on dynamic async component (toggler is never patched)", async () =>
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect([
|
expect([
|
||||||
"Child (2):willDestroy",
|
|
||||||
"Child (3):setup",
|
"Child (3):setup",
|
||||||
"Child (3):willStart",
|
"Child (3):willStart",
|
||||||
"Child (3):willRender",
|
"Child (3):willRender",
|
||||||
"Child (3):rendered",
|
"Child (3):rendered",
|
||||||
"Child (1):willUnmount",
|
"Child (1):willUnmount",
|
||||||
"Child (1):willDestroy",
|
"Child (1):willDestroy",
|
||||||
|
"Child (2):willDestroy",
|
||||||
"Child (3):mounted",
|
"Child (3):mounted",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
});
|
});
|
||||||
@@ -3107,13 +3109,13 @@ test("t-foreach with dynamic async component", async () => {
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect([
|
expect([
|
||||||
"Child (2):willDestroy",
|
|
||||||
"Child (3):setup",
|
"Child (3):setup",
|
||||||
"Child (3):willStart",
|
"Child (3):willStart",
|
||||||
"Child (3):willRender",
|
"Child (3):willRender",
|
||||||
"Child (3):rendered",
|
"Child (3):rendered",
|
||||||
"Child (1):willUnmount",
|
"Child (1):willUnmount",
|
||||||
"Child (1):willDestroy",
|
"Child (1):willDestroy",
|
||||||
|
"Child (2):willDestroy",
|
||||||
"Child (3):mounted",
|
"Child (3):mounted",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user