Skip to content

Commit 41fa4cc

Browse files
committed
Added OVERVIEW doc
1 parent 1260e37 commit 41fa4cc

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

OVERVIEW.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Overview
2+
3+
## Serializing the tree
4+
5+
Every React commit that changes the tree in a way DevTools cares about results in an "_operations_" message being sent across the bridge. These messages are lightweight patches that describe the changes that were made. (We don't resend the full tree structure like in legacy DevTools.)
6+
7+
The payload for each message is a typed array. The first entry is a number identifying which renderer the update belongs to (for multi-root support). The rest of the array depends on the operations being made to the tree.
8+
9+
We only send the following bits of information: element type, id, parent id, owner id, name, and key. Additional information (e.g. props, state) requires a separate "_inspectElement_" message.
10+
11+
### Adding a root node
12+
13+
Adding a root to the tree requires sending 3 numbers:
14+
15+
1. add operation constant (`1`)
16+
1. fiber id
17+
1. element type constant (`8 === ElementTypeRoot`)
18+
19+
For example, adding a root fiber with an id of 1:
20+
```js
21+
[
22+
1, // add operation
23+
1, // fiber id
24+
8, // ElementTypeRoot
25+
]
26+
```
27+
28+
### Adding a leaf node
29+
30+
Adding a leaf node takes a variable number of numbers since we need to decode the name (and potentially the key):
31+
32+
1. add operation constant (`1`)
33+
1. fiber id
34+
1. element type constant (e.g. `1 === ElementTypeClass`)
35+
1. parent fiber id
36+
1. owner fiber id
37+
1. UTF encoded display name size
38+
* (followed by this number of encoded values)
39+
1. UTF encoded key size
40+
* (followed by this number of encoded values)
41+
42+
For example, adding a function component `<Foo>` with an id 2:
43+
```js
44+
[
45+
1, // add operation
46+
2, // fiber id
47+
2, // ElementTypeFunction
48+
1, // parent id
49+
0, // owner id
50+
3, // encoded display name size
51+
70, // "F"
52+
111, // "o"
53+
111, // "o"
54+
0, // encoded key (null)
55+
]
56+
```
57+
58+
### Removing a node
59+
60+
Removing a fiber from the tree (a root or a leaf) only requires sending 2 numbers:
61+
62+
1. remove operation constant (`2`)
63+
1. fiber id
64+
65+
For example, removing a root fiber with an id of 1:
66+
```js
67+
[
68+
2, // remove operation
69+
1, // fiber id
70+
]
71+
```
72+
73+
### Re-ordering children
74+
75+
1. re-order children constant (`3`)
76+
1. fiber id
77+
1. number of children
78+
* (followed by an ordered list of child fiber ids)
79+
80+
For example:
81+
```js
82+
[
83+
3, // re-order operation
84+
15, // fiber id
85+
2, // number of children
86+
35, // first chlid id
87+
21, // second chlid id
88+
]
89+
```
90+
91+
## Reconstructing the tree
92+
93+
The frontend stores its information about the tree in a map of id to objects with the following keys:
94+
95+
* id: `number`
96+
* parentID: `number`
97+
* children: `Array<number>`
98+
* type: `number` (constant)
99+
* displayName: `string | null`
100+
* key: `number | string | null`
101+
* ownerID: `number`
102+
* depth: `number` <sup>1</sup>
103+
* weight: `number` <sup>2</sup>
104+
105+
<sup>1</sup> The `weight` of an element is the number of elements (including itself) below it in the tree. We cache this property so that we can quickly determine the total number of Elements as well as to find the Nth element within that set. (This enables us to use windowing.) This value needs to be adjusted each time elements are added or removed from the tree, but we amortize this over time to avoid any big performance hits when rendering the tree.
106+
107+
The `depth` value determines how much padding/indentation to use for the element when rendering it in the Elements panel. (This preserves the appearance of a nested tree, even though the view is a flat list.)
108+
109+
### Finding the element at index N
110+
111+
The tree data structure lets us impose an order on elements and "quickly" find the Nth one using the `weight` attribute.
112+
113+
First we find which root contains the index:
114+
```js
115+
let rootID;
116+
let root;
117+
let rootWeight = 0;
118+
for (let i = 0; i < this._roots.length; i++) {
119+
rootID = this._roots[i];
120+
root = this._idToElement.get(rootID);
121+
if (root.children.length === 0) {
122+
continue;
123+
} else if (rootWeight + root.weight > index) {
124+
break;
125+
} else {
126+
rootWeight += root.weight;
127+
}
128+
}
129+
```
130+
131+
We skip the root itself because don't display them in the tree:
132+
```js
133+
const firstChildID = root.children[0];
134+
```
135+
136+
Then we traverse the tree to find the element:
137+
```js
138+
let currentElement = this._idToElement.get(firstChildID);
139+
let currentWeight = rootWeight;
140+
while (index !== currentWeight) {
141+
for (let i = 0; i < currentElement.children.length; i++) {
142+
const childID = currentElement.children[i];
143+
const child = this._idToElement.get(childID);
144+
const { weight } = child;
145+
if (index <= currentWeight + weight) {
146+
currentWeight++;
147+
currentElement = child;
148+
break;
149+
} else {
150+
currentWeight += weight;
151+
}
152+
}
153+
}
154+
```

0 commit comments

Comments
 (0)