Replies: 11 comments 13 replies
-
Middleware has the ability to halt, skip, and apply patches, so I think it could be doable from that approach. You would just need to be 100% sure the old markup matches the server, otherwise the deltas will be invalid. |
Beta Was this translation helpful? Give feedback.
-
Essentially every render in diffHTML is a Transaction instance. These transactions run predictable tasks in order. Once the last task runs, the transaction/render is considered complete. You can see the tasks here: export const tasks = {
schedule, shouldUpdate, reconcileTrees, syncTrees, patchNode, endAsPromise,
}; For handling only deltas on the client, we want all the tasks, except for The render APIs model the browser, so we have Here is a working solution of applying just delta/patches: import { Internals, innerHTML, toString, html, use } from 'diffhtml';
import { JSDOM } from 'jsdom';
// simulate browser
global.document = new JSDOM(`<!DOCTYPE html>`).window.document;
///////////////////////////////////////////////////////////////////////////////
const applyDeltasTask = config => transaction => {
const currentTasks = transaction.tasks;
const indexOfSyncTrees = currentTasks.indexOf(Internals.tasks.syncTrees);
// Only run this middleware when deltas are present.
if (!('deltas' in transaction.config)) {
return;
}
// Replace syncTrees with applyDeltas
currentTasks.splice(indexOfSyncTrees, 1, function applyDeltas(transaction) {
transaction.patches = transaction.config.deltas.map(x => {
if (x === Symbol.for('oldTree')) {
return transaction.oldTree;
}
return x;
});
});
};
///////////////////////////////////////////////////////////////////////////////
const mount = document.createElement('div');
use(applyDeltasTask());
innerHTML(mount, null, {
// Simulate deltas from the server, this just adds a hello world node
deltas: [
Internals.PATCH_TYPE.INSERT_BEFORE,
Symbol.for('oldTree'),
html`Hello world yeee!`,
null,
],
});
console.log(toString(mount));
// <div>Hello world yeee!</div> The most challenging part will be figuring out a way to align the references from the server to the client, specifically the deltas: [
Internals.PATCH_TYPE.INSERT_BEFORE,
mount, // Symbol.for('oldTree') or something more robust
html`Hello world!`,
null,
], |
Beta Was this translation helpful? Give feedback.
-
Many thanks! It’ll be next week before I get a chance to start on my prototype, but this is very encouraging, thanks.
|
Beta Was this translation helpful? Give feedback.
-
I've been making some pretty amazing progress for web worker support. One of the biggest revelations has been thinking about the client as simply the delta patcher and event handler (thank you for that!). This led to another revelation to migrate all of the actual application logic into the worker thread. Before I was trying to offload only the diffing, when in reality you need to offload the whole application. I've been able to churn out 60fps across multiple threads using this approach. From a server-perspective, I can imagine replacing the postMessage web worker calls, with a web socket handler. There isn't much difference otherwise. They have the same serialization issues, are both bidirectional, and can support array buffers (for future optimizations). I was thinking about the issue around event handlers, since the browser event system requires synchronous handling of event propagation. We have a module called diffhtml-middleware-synthetic-events. What if we allowed this to implement it's own event bubbling/propagation rules that allow asynchronous handling? This would effectively make the client extremely thin. It would dispatch emulated DOM events and apply deltas. |
Beta Was this translation helpful? Give feedback.
-
This sounds awesome! I’m still scaffolding my prototype but will hope to be ready to play late this week. Good work though - nice one.
…Sent from my iPhone
On 6 Dec 2021, at 18:53, Tim Branyen ***@***.***> wrote:
I've been making some pretty amazing progress for web worker support. One of the biggest revelations has been thinking about the client as simply the delta patcher and event handler (thank you for that!). This led to another revelation to migrate all of the actual application logic into the worker thread. Before I was trying to offload only the diffing, when in reality you need to offload the whole application. I've been able to churn out 60fps across multiple threads using this approach.
From a server-perspective, I can imagine replacing the postMessage web worker calls, with a web socket handler. There isn't much difference otherwise. They have the same serialization issues, are both bidirectional, and can support array buffers (for future optimizations).
I was thinking about the issue around event handlers, since the browser requests synchronous handling of event propagation. We have a module called diffhtml-middleware-synthetic-events. What if we allowed this to implement it's own event bubbling/propagation rules that allow asynchronous handling?
This would effectively make the client extremely thin. It would dispatch emulated DOM events and apply deltas.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
|
Beta Was this translation helpful? Give feedback.
-
I'm still making occasional progress on this original ask. I deviated a bit with web workers, but now I'm adapting that code into the server approach. One difference between the worker and the server is that the server needs to generate an entire HTML page, while the worker has the benefit of one already existing. This is problematic when it comes to hydrating. With the worker approach, there is no hydration, the same code cranks out renders and applies deltas the same for the first to the last render. With the server you'll most likely want to send a fully rendered initial page, and then progressively send deltas. Since the initial render generates the We could do a few things to solve this:
I have a feeling that What I'd expect to see from all this work by the end of the year is a common middleware that can be used in web workers and nodejs servers to produce deltas and apply them in a client side main thread. |
Beta Was this translation helpful? Give feedback.
-
Woot finally have the initial ask prototyped. Still missing events and DOM access, but for now, I have 60fps rendering a SVG clock using deltas via WebSockets. Check it out here: https://github.com/tbranyen-experimental/diffhtml-web-worker/blob/server/server.js. Full rendering occurs on the server. Memory is excellent! This could be pretty amazing for users with low-end devices. Kind of borrows from the technique used by companies streaming games, where they have an input (in our case dom/input events) system and streaming pre-rendered deltas. |
Beta Was this translation helpful? Give feedback.
-
Just posted another update that has some pretty interesting changes. One thing I realized is that no matter what, we're going to need synchronous access to properties on Server worker -> Server main thread -> Web Socket --> Main thread patches Since the server worker contains the calls to the Obviously it's still very rough, but once I get synchronous DOM handling, you could basically start porting apps to this architecture. Whichever fits your use case. |
Beta Was this translation helpful? Give feedback.
-
I was just thinking about this could change the game for instant live reloads. Instead of dealing with server and client code reloading, you can do it all from the server. You would create an initial worker per client connection, wait for code change from developer, then create a new worker, and share the last root state from the old worker as the current root state for diffing in the new worker, then shutdown the old worker. The new worker will synchronize and determine the deltas from what is currently rendered. So if you change one line, you should only see a text patch applied over the socket. |
Beta Was this translation helpful? Give feedback.
-
Turns out what we're working on is basically identical to Phoenix Frameworks LiveView feature. They render HTML on the server and calculate diffs to send over web sockets. I'm going to spend some time researching their approach and finishing up the synchronous global lookups. I've got some HMR progress as well, but going to learn some LiveView first and then pick this back up. I'm still experimenting with sharedarraybuffers and atomics to provide synchronous cross-thread access, but that is gonna be a big project unless we can somehow use partytown. |
Beta Was this translation helpful? Give feedback.
-
Just a note to say I've evaluated a bunch of vdom libraries and this is the only one I can find that:
I'm still sending the full page HTML from the server rather than fragments, but so far it is more than quick enough. Thanks! |
Beta Was this translation helpful? Give feedback.
-
Hi - if the server caches the clients current HTML and generates the new HTML, can the server use this library to determine just the deltas and have those sent to the client which then applies those deltas?
I didn't find anything obvious, but this seems an obvious and incredibly powerful (not implying cheap or expectations of who does the work ;-)) capability.
This might be obviously documented - apologies, I'm skim reading a bunch of these libraries ;-)
Beta Was this translation helpful? Give feedback.
All reactions