1 of 49

What if…?

Wednesday, 28th of July @ trivago

2 of 49

Jason Miller

Web DevRel @Google

Marvin Hagemeister

Consultant @AWESOME! Software

3 of 49

Fast 3kB framework and close to the DOM

4 of 49

How can we reimagine what

Preact can be?

5 of 49

What even is the next thing?

6 of 49

Long-Term vision

Short-Term thinking

7 of 49

Positive

prototyping

culture

No internal/external pressure

Ok to fail → learnings

Allow ideas to grow

Async collaboration

Explore alternative implementations

8 of 49

consequences

of a change?

What are the

change

system

9 of 49

used modern JS features?

What if we...

10 of 49

setState(update, callback) {

var s = this._state;

if (!s) {

s = this._state = assign({}, this.state);

}

assign(s, update);

if (callback) {

this._callbacks.push(callback);

}

enqueueRender(this);

}

setState(update, callback) {

let s = this._state;

if (!s) {

s = this._state = Object.assign({}, this.state);

}

Object.assign(s, update);

if (callback) {

this._callbacks.push(callback);

}

enqueueRender(this);

}

11 of 49

Example: JSX

transpiles to:

source code:

function Link({ href, children }) {

return <a href={href}>link: {children}</a>;

}

function Link({ href, children }) {

return h('a', { href }, 'link: ', children);

}

also known as createElement() or React.createElement()

12 of 49

function createElement(type, props, children) {

if (arguments.length > 3) {

children = [children]

for (let i=3; i<arguments.length, i++) {

children.push(i);

}

}

if (children != null) {

props.children = children;

}

return { type, props };

}

createElement(

'a',

{ href },

'link: ',

children

)

13 of 49

with modern JS

function createElement(type, props, ...children) {

if (children.length !== 0) {

props.children = children;

}

return { type, props };

}

14 of 49

with modern JS

function createElement(type, props, ...children) {

if (children.length === 1) {

props.children = children[0];

}

else if (children.length > 1) {

props.children = children;

}

return { type, props };

}

always allocates an Array

15 of 49

back to ES5

function createElement(type, props, children) {

if (arguments.length > 3) {

children = Array.prototype.slice.call(arguments, 2);

}

if (children != null) {

props.children = children;

}

return { type, props };

}

only for�2+ children

16 of 49

use modern JS

What did we learn?

remove workarounds

17 of 49

Drop workarounds

for IE11

18 of 49

removed recursion?

What if we...

19 of 49

Recursion

function diff(node, next) {

if (node.type != next.type) {

return replace(node, next);

}

for (child of children) {

let nextChild = /* find match */;

diff(child, nextChild);

}

}

repeat to walk down the tree

20 of 49

Recursion

Loop

diff(oldVNode, newVNode)

diff(oldVNode, newVNode)

diff(oldVNode, newVNode)

diff(oldVNode, newVNode)

// ...

const pending = [...];

let item;

while (item = pending.pop()) {

diff(item.old, item.new);

}

21 of 49

Recursion

Loop

22 of 49

only use a switch-statement?

What if we...

23 of 49

const operations = [];

let op, node;

while ({ op, node } = operations.pop()) {

switch (op) {

case MOUNT:

/* code for mounting `node` */���

break;

case UNMOUNT: /*...*/ break;

case PATCH: /*...*/ break;

}

}

24 of 49

const operations = [];

let op, node;

while ({ op, node } = operations.pop()) {

switch (op) {

case MOUNT:

/* code for mounting `node` */

for (let node of node.children) {

operations.push({ op: MOUNT, node });

}

break;

case UNMOUNT: /*...*/ break;

case PATCH: /*...*/ break;

}

}

25 of 49

Sequence of commands

insert

patch

set prop

swap

remove

(op-queue, stack machine)

26 of 49

know if this is any better?

Errr...how do we

27 of 49

28 of 49

29 of 49

remove all deopts?

What if we...

30 of 49

31 of 49

32 of 49

Your optimization insight is

only as good

as the benchmark it is obtained from

33 of 49

removed class components?

What if we...

34 of 49

Preact X: Component normalization

// Instantiate class component

if (newType.prototype.render) {

c = new newType(newProps);

} else {

// Convert function component into a class component

c = new Component(newProps);

c.constructor = newType;

c.render = doRender;

}

35 of 49

Same code path saves bytes

State and Hooks

render state (error, suspend, ...)

view hierarchy

Reasons:

36 of 49

Planning for evolution

Function Components

2018

Hooks

2020

Observables?

??

2015

createClass

Class Components

2016

Vue’s Composition API

Generator Components

Non-JS Components

??

37 of 49

→ component instance

38 of 49

→ backing node

→ component instance

39 of 49

40 of 49

41 of 49

split diffing from rendering

Realization:

42 of 49

img

img

div

a

#text

#text

DOM Layer

Virtual-DOM Layer

#text

div

a

#text

walk the DOM for position & order

Preact 10

43 of 49

img

#text

div

a

#text

img

div

a

#text

#text

DOM Layer

Virtual-DOM Layer

use virtual tree for position & order

Preact 11

44 of 49

combine approaches?

What if we...

45 of 49

Backing Tree

Operation Queue

Remove Legacy

Next-gen Renderer

46 of 49

47 of 49

Faster algorithms

Custom renderers + components

Better animation scheduling

Deeper tooling + debugging

Evolutionary milestones:

48 of 49

split diff�+ render

(attempt #1)

Reality is less linear

Trivago Sponsoring

Server Components

AOT SSR

Devtools Hook Names

split diff + render

Extract

components

Op Queue

Backing nodes

~0 deopts

split diff�+ render

(attempt #2)

today

Observable components

3 Tries

49 of 49

Q&A

Thanks!