Last active
June 6, 2021 11:20
-
-
Save kl0tl/4043ac052d4a3978e759 to your computer and use it in GitHub Desktop.
DOM diffing inside a `Worker` with `virtual-dom`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var VDOMWorkerRenderer = require('./vdom-worker-renderer'); | |
var renderer = new VDOMWorkerRenderer(function (h) { | |
return function ui(state) { | |
return counter(state.counter) | |
}; | |
function counter(value) { | |
return h('div', { className: 'counter' }, [String(value)]); | |
} | |
}, { counter: 0 }); | |
document.querySelector('#ui-root').appendChild(renderer.node); | |
setInterval(function inc() { | |
renderer.state.counter += 1; | |
}, 1000); | |
requestAnimationFrame(function main() { | |
requestAnimationFrame(main); | |
renderer.render(); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function observe(object, listener) { | |
var observed = {}; | |
Object.keys(object).forEach(function (key) { | |
var descriptor = Object.getOwnPropertyDescriptor(object, key); | |
var value = object[key]; | |
if (typeof value === 'object' && value !== null) { | |
value = observe(value, whenSubPropertiesChanged); | |
} | |
Object.defineProperty(observed, key, { | |
get: function () { | |
return value; | |
}, | |
set: function (newValue) { | |
if ('value' in descriptor ? !descriptor.writable : false) return; | |
if (value === newValue) return; | |
if (typeof newValue === 'object' && newValue !== null) { | |
newValue = observe(newValue, whenSubPropertiesChanged); | |
} | |
listener([key], newValue, value); | |
value = newValue; | |
}, | |
enumerable: descriptor.enumerable, | |
configurable: descriptor.configurable | |
}); | |
function whenSubPropertiesChanged(path, newValue, oldValue) { | |
listener([key].concat(path), newValue, oldValue); | |
} | |
}); | |
return Object.seal(observed); | |
} | |
module.exports = observe; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function VDOMRenderer(definition, state) { | |
var self = this; | |
self.node = document.create('div'); | |
self.state = observe(state, function () { | |
var vtree = self._component(self.state); | |
var patches = vdom.diff(self._vtree, vtree); | |
self._vtree = vtree; | |
if (Object.keys(patches).length > 1) { | |
self._queue.push(patches); | |
} | |
}); | |
self._component = definition(vdom.h); | |
self._vtree = vdom.h(); | |
self._queue = []; | |
} | |
Object.defineProperty(VDOMRenderer.prototype, 'dirty', { | |
get: function () { | |
return Boolean(this._queue.length); | |
}, | |
enumerable: true, | |
configurable: false | |
}) | |
VDOMRenderer.prototype.render = function () { | |
if (!this.dirty) return; | |
var patches = null; | |
while ((patches = this._queue.shift())) { | |
this.node = vdom.patch(this.node, patches); | |
} | |
}; | |
module.exports = VDOMRenderer; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var vdom = require('virtual-dom'); | |
var observe = require('./observe'); | |
var vdomWorkerScript = document.querySelector('script[type="text/vdom-worker"]'); | |
var vdomWorkerSource = (vdomWorkerScript.textContent || vdomWorkerScript.innerText) | |
.replace('${ origin }', location.origin); | |
function VDOMWorkerRenderer(definition, state) { | |
var self = this; | |
var source = vdomWorkerSource | |
.replace('${ state }', JSON.stringify(state)) | |
.replace('${ definition }', definition.toString()); | |
var blob = new Blob([source]); | |
var url = URL.createObjectURL(blob); | |
self.node = document.createElement('div'); | |
self.worker = Object.defineProperty(new Worker(url), 'url', { | |
get: function () { return url }, | |
enumerable: true, | |
configurable: false | |
}); | |
self.worker.addEventListener('message', function (event) { | |
var message = event.data; | |
if (message.type === 'render') { | |
self.node = vdom.patch(self.node, JSON.parse(message.patches)); | |
} | |
}, false); | |
self.state = observe(state, function (path, newValue) { | |
self.worker.postMessage({ type: 'state:changed', path: path, value: newValue }); | |
}); | |
} | |
VDOMWorkerRenderer.prototype.render = function () { | |
this.worker.postMessage({ type: 'render' }); | |
}; | |
VDOMWorkerRenderer.prototype.dispose = function () { | |
this.worker.terminate(); | |
URL.revokeObjectURL(this.worker.url); | |
}; | |
module.exports = VDOMWorkerRenderer; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
importScripts('${ origin }/dist/vdom.js'); | |
(function () { | |
var oldVTree = vdom.h(); | |
var state = ${ state }; | |
var render = (${ definition }(vdom.h)); | |
self.addEventListener('message', function (event) { | |
var message = event.data; | |
if (message.type === 'state:changed') { | |
update(state, message.path, message.value); | |
} else if (message.type === 'render') { | |
var newVTree = render(state); | |
var patches = vdom.diff(oldVTree, newVTree); | |
oldVTree = newVTree; | |
if (Object.keys(patches).length > 1) { | |
self.postMessage({ type: 'render', patches: JSON.stringify(patches) }); | |
} | |
} | |
}); | |
function update(dest, path, value) { | |
for (var i = 0, length = path.length - 1; i < length; i += 1) { | |
dest = dest[path[i]]; | |
} | |
dest[path[i]] = value; | |
} | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment