mithril.js
mithril.js copied to clipboard
Feature request: Custom renderer support
My proposal here is that we should see if we can create an alternate renderer that supports pluggable renderers, rather than just rendering to the DOM directly. There's a few reasons why we should support it in core:
- mithril-node-render should not be relying on our mock to work. We should instead let it use a custom renderer, so it's not coupled to what should be an implementation detail for our tests.
- I ran into a scenario talking with some Bloomberg people at JSConf who were familiar with Mithril that they went with React simply because it was the only one with decent, stable custom renderer support. Very few frameworks support this use case at all, with Vue being really awkward to use, Angular being an object-oriented mess, and pretty pretty much everyone else restricted to just .
- There's a lot more a custom renderer backend could do.
- We could finally stop shipping this directory - there's packages and builtins for all our mocks, so there's little reason our users should be depending on them. Besides, our mocks are specialized for us, not for our users, and we haven't even tried to maintain semver here, so why should we be shipping them in the first place?
- That README is also a bit out of place - it lists a version, but there's no package.json, and the file has been updated exactly once, just to normalize whitespace.
For obvious performance and size reasons, we shouldn't place this in the core bundle. Instead, we should create an alternate render
implementation specialized for custom renderers.
I do get the concern of a lot of duplicate code, but I feel there's enough different fundamentally that the duplication and added complexity is a little more tolerable:
- Namespace tracking doesn't need to exist. If necessary, the custom renderer can just infer from the parent and tag name itself. (It's not that hard, just a little edge case-y.)
- Element-specific hacks like for
contenteditable
,value
andselectedIndex
for<select>
, and thedocument.activeElement
IE hack are not necessary custom renderers enter the equation. - Event listener management could be made slightly different - we aren't bound to the traditional event interface, so we could get a little creative there.
- Most of the mess that's
setAttr
andremoveAttr
could be streamlined without all the edge cases that flood that part of the code. - We would need to address how to model assigning the vnodes to the root "element".
The only other modifications required are to 1. move the redraw service to accept a render service parameter instead of creating it directly unnecessarily ($window
is only used in that one place), and 2. move all the redraw service factory's callers to pass it a render service instead of a raw window
value. Incidentally, this would provide a couple benefits of its own:
- It would simplify testing the redraw service, since it'd just be testing how it calls a few methods.
- The redraw service wouldn't need to return its
renderService.render
, just returning the hooks that actually deal with redrawing. This consequently makes the code less brittle.
Yes, there is a lot of extra complexity, but it's not like some of it couldn't be abstracted out pretty easily - there's a lot of code not touching the closure at all that we could reuse, many of which are already highly tuned:
-
checkState
-
callHook
-
initLifecycle
-
updateLifecycle
-
initComponent
(mod an unused return value) -
This loop within
updateFragment
-
getKeyMap
-
makeLisIndices
-
getNextSibling
-
onremove
-
removeNode
if you abstract this little bit into its own function and pass that as a parameter. (Yes, that little bit could be made more efficient.) -
shouldNotUpdate
This is not something I want to block v2 (maybe v3), but it's designed to be non-breaking.
For those of you reading, apologies for the large wall of text. 😄
Oh, and something I just remembered: this is a revival of a few prior issues here:
- https://github.com/MithrilJS/mithril.js/issues/10
- https://github.com/MithrilJS/mithril.js/issues/722
- https://github.com/MithrilJS/mithril.js/issues/830
- https://github.com/MithrilJS/mithril.js/issues/856 (by me)
It's a near-dupe of all but #10 (which is mostly focused on strings), but I'd rather keep this one alive since it's the most detailed of them all.