snabbdom / snabbdom

A virtual DOM library with focus on simplicity, modularity, powerful features and performance.
MIT License
11.91k stars 1.11k forks source link

Lower-level patch API #231

Open aronallen opened 7 years ago

aronallen commented 7 years ago

Hi,

As some may know I am experimenting with running Cycle in WebWorkers. It works pretty well, but the main bottleneck is the postMessage API. Right now I am transmitting something i call vDOMSON, representing the entire component vTree, this is then mapped recursively to snabbdom/h which in turn is passed to the patch function.

To reduce I/O I thought it could be neat to have patch in two parts, so instead of transmitting the entire vDOMSON, I can just transmit a list of mutations. The pre-patch could be run in the sub-thread, while the resulting mutations could be transferred to and applied in the main thread.

I know it sounds kind of insane.

mightyiam commented 7 years ago

Perhaps best that you write an initial proof of concept PR.

jorgebucaran commented 7 years ago

@aronallen Patched vdom trees as streams.

I don't think that sounds insane. If you have further thoughts on this, I'd want to hear them.

Also and just to make sure I'm on the same page, what do you mean by I/O in this case?

aronallen commented 7 years ago

@jbucaran by I/O i mean the overhead in the communication between the web worker thread and the main thread.

aronallen commented 7 years ago

@jbucaran

basically I want a flat list of mutations. something like

[
['remove', [0,2,1,2,5,1]],
['add', [0,2,1,2,5,1], node],
['update', [0,2,1,2,5,1], node]
]

The coordinates must be defined so that they assume all previous mutations in the same patch have happened

whmountains commented 7 years ago

I really hope this happens! I have been looking for a way to have diff and patch split up for a long time for that exact usecase.

Aron Nyborg Allen wrote:

Hi,

As some may know I am experimenting with running Cycle in WebWorkers. It works pretty well, but the main bottleneck is the postMessage API. Right now I am transmitting something i call vDOMSON, representing the entire component vTree, this is then mapped recursively to |snabbdom/h| which in turn is passed to the patch function.

To reduce I/O I thought it could be neat to have patch in two parts, so instead of transmitting the entire vDOMSON, I can just transmit a list of mutations. The pre-patch could be run in the sub-thread, while the resulting mutations could be applied in the main thread.

I know it sounds kind of insane.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/snabbdom/snabbdom/issues/231, or mute the thread https://github.com/notifications/unsubscribe-auth/AIRy02NhFYlIl9Zvz2pzEylNbVgGkrMrks5rTgpAgaJpZM4Lm2HZ.

staltz commented 7 years ago

@aronallen check out snabbdom's DOMAPI. It's essentially a wrapper for anything that actually touches the real DOM. Perhaps you can send serializations that refer to calls of those methods.

interface DOMAPI {
  createElement: (tagName: any) => HTMLElement;
  createElementNS: (namespaceURI: string, qualifiedName: string) => Element;
  createTextNode: (text: string) => Text;
  insertBefore: (parentNode: Node, newNode: Node, referenceNode: Node | null) => void;
  removeChild: (node: Node, child: Node) => void;
  appendChild: (node: Node, child: Node) => void;
  parentNode: (node: Node) => Node;
  nextSibling: (node: Node) => Node;
  tagName: (elm: Element) => string;
  setTextContent: (node: Node, text: string | null) => void;
}

The cool thing about snabbdom is that you can pass an instance of domAPI to init().

I know some of these methods take Element as argument, but this is actually vnode.elm, so it could be that vnode.elm on the web worker side is just some kind of virtual pointer (a string, if nothing else) to an element on the DOM.

Just an idea...

aronallen commented 7 years ago

@staltz thanks for the hint. If the DOM API only received virtual DOM nodes, and expected virtual DOM nodes it would be quite easy to port. I guess I could write a shallow implementation of <Node> in the webworker context.

jorgebucaran commented 7 years ago

@aronallen

If the DOM API only received virtual DOM nodes, and expected virtual DOM nodes

My two cents. The domapi is just but an object exposing the functions in staltz post above. Snabbdom implements this api using the function in document.* by default, but the idea is you can provide an object with your own implementation.

const patch = require("snabbdom").init([/* modules */],  myDOMAPI)
aronallen commented 7 years ago

@jbucaran yes, it is pretty neat. I think I can put something together.

mightyiam commented 7 years ago

This may open the way for multi-threaded UI in snabbdom itself? Is snabbdom ever a bottleneck in that regard? Even if used correctly?

aronallen commented 7 years ago

@mightyiam I don't know, my idea of using web workers is more about security by providing an encapsulated execution context. Since everything needs to be patched in the main thread anyway, I wouldn't imagine there to be any performance benefits. So there might be performance benefits, but I doubt it.

whmountains commented 7 years ago

There is some performance benefit.

If diffing takes a long time, the ui/scrolling won't jank if it's done in a worker thread. See http://www.pocketjavascript.com/blog/2015/11/23/introducing-pokedex-org

Aron Nyborg Allen wrote:

@mightyiam https://github.com/mightyiam I don't know, my idea of using web workers is more about security by providing an encapsulated execution context. Since everything needs to be patched in the main thread anyway, I wouldn't imagine there to be any performance benefits. So there might be performance benefits, but I doubt it.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/snabbdom/snabbdom/issues/231#issuecomment-274183549, or mute the thread https://github.com/notifications/unsubscribe-auth/AIRy09bOBclJGIqk_33bXXhqinJ_Sor2ks5rUSXugaJpZM4Lm2HZ.

jorgebucaran commented 7 years ago

@aronallen Found about ReactDOM Stream and remembered this issue for some reason.

tusharmath commented 7 years ago

@whmountains have u seen passive event listeners https://developers.google.com/web/updates/2016/06/passive-event-listeners

mightyiam commented 4 years ago

@aronallen title change OK with you?