
Jason Miller


@developit on Github

@_developit on Twitter
serial library author
Constraints
are challenges
ADHD
into the void 0
;
    
    
    
    
    JSX
XML-like expression
compiled to a function call
 <div id="one">
   Hello
 </div>
    
 h('div', { id:'one' },
   'Hello'
 );
    JSX Explained
let world = 'World!';
<div one={ 1 }>
  Hello, { world }
</div>
let Link = 'a';
<Link>Test</Link>
    var world = 'World!';
h('div', { one: 1 },
  'Hello, ', world
);
var Link = 'a';
h(Link, 'Test');
    JSX Explained
JS expression
reference
- AKA createElement()
 - Calls inserted in place of every <tag>
 
  h(
    nodeName,    // String / Function
    attributes,  // Object / null
    ...children  // any remaining args
  )
    Factory Function
So... hyperscript?
  h('div', { id:'one' },
    'Hello'
  )
    
  h('div', { id:'one' },
    'Hello'
  )
    
 h(Foo, { hello: true })
    
  h('div#one', 'Hello')
    Hyperscript
Transpiled JSX

Virtual DOM
Virtual DOM
Objects representing
a DOM tree structure
Virtual DOM
JSX
hyperscript
Virtual DOM
syntax
function calls
objects
transform
invoke
We write this:
 <div id="foo">
   Hello!
   <br />
 </div>
    ... which transpiles to this:
 h('div', { id:'foo' },
   'Hello!',
   h('br')
 )
    ... which returns this:
 {
   nodeName:'div',
   attributes: { id:'foo' },
   children: [
     'Hello!',
     { nodeName:'br' }
   ]
 }
    
  function h(nodeName, attributes, ...children) {
    return { nodeName, attributes, children }
  }
    Virtual DOM Factory
hyperscript
virtual DOM
 function render(vnode) {  
   if (typeof vnode==='string')
     return document.createTextNode(vnode)
   let node = document.createElement(vnode.nodeName)
   for (let name in Object(vnode.attributes))
     node.setAttribute(name, vnode.attributes[name])
   for (let i=0; i<vnode.children.length; i++)
     node.appendChild(render(vnode.children[i]))
   return node
 }
    Simple Renderer
render( {
  nodeName:'div',
  attributes: { id:'foo' },
  children: [
    'Hello!',
    { nodeName:'br' }
  ]
} )
     function diffAttributes(node, vnode) {
   for (let name in Object(vnode.attributes))
     node.setAttribute(name, vnode.attributes[name])
 }
    Attributes or Properties?
 function diffProperties(node, vnode) {
   Object.assign(node, vnode.attributes)
 }
     function diffProps(node, vnode) {
   for (let name in Object(vnode.attributes)) {
     let value = vnode.attributes[name]
     if (name in node) {
       node[name] = value
     }
     else {
       node.setAttribute(name, value)
     }
   }
 }
    ?
?
Does it work?
 let vdom = (
   <div id="foo">
     <h1>Hello</h1>
     <ol>
       <li>One</li>
       <li>Two</li>
     </ol>
   </div>
 )
 let dom = render(vdom)
 document.body.appendChild(dom)
    YUP
Deploy It
Diffing
without magic
WHAT IF...
Instead of rendering,
we diffed the VDOM objects
against the current DOM
and applied any differences?
Virtual
Actual
 {
   nodeName: 'div',
   attributes: {
     key: 'value'
   },
   children: [
     'text node'
   ]
 }
     Element(
   nodeName: 'div',
   attributes: {
     getItem('key'),
     setItem('key', 'value')
   },
   childNodes: [
     TextNode('text node')
   ]
 )
    DOM
DOM
Comparable
Diff Process
<type>
children
attributes / props
new / updated?
1
2
3
Diff: type
is node component?
diff()
create/update component
render component
same type?
no
yes
no
yes
create new & replace
update in-place
Diff: children
has key?
for each child node
no
yes
append to unkeyed
add to keyed map
find match in
keyed / unkeyed
for each virtual child
diff against virtual child
insert at current index
remove unused children
1
2
3
What are keys for?
keys attribute meaningful order to a set of uniform elements.
 <li>one</li>
 <li>two</li>
 <li>three</li> <li>one</li>
 <li>three</li>
 <li>three</li> <li>one</li>
 <li>three</li><li key="1">one
<li key="2">two
<li key="3">three <li>one</li>
 <li>two</li>
 <li>three</li><li key="1">one
<li key="3">threeDiff: attributes/props
for each
old attribute
not in new attributes?
set to undefined
for each
new attribute
differs from old value?
set to
new value
1
2
Performance
Benchmark everying
"The DOM is slow"
YOUR DOM is slow
"The DOM is slow"
DOMination
use Text nodes
for text
  // Create it
  text = document.createTextNode('some text')
  // Insert it
  parent.insertBefore(text, <before | null>)
  // Flip it & reverse it
  text.nodeValue = 'ʇxǝʇ ǝɯos'
Text.nodeValue

almost 50% faster
DOM wanna miss a thing
avoid (DOM) getters
  // slow
  if (node.nodeType===3) { }
  // fast
  if (node.splitText) { }
  // ludicrous speed
  if (node.splitText!==undefined) { }


breaks when cross-document
getters
simple prototype property
DOM & dommer
avoid Live NodeLists
 // slow
 for (let i=parent.childNodes.length; i--; ) {
   parent.removeChild(parent.childNodes[i])
 }
 // fast
 while ( child = parent.lastChild ) {
   parent.removeChild(child)
 }
(bench setup overhead)
childNodes
lastChild
Measure
Always be profiling
DevTools Timeline

Something to obsess over.
IRHydra
- @mraleph
 - see how/why
 - shows deopts
 


git.io/hydraosx
ESBench.com
= Babel + Benchmark.js

visualize
Lessons
Be Explicit
Don't accidentally ToBoolean
 // don't write this:
 if (obj.props)
 // if you mean this:
 if (obj.props!==undefined)
could be 0, '', null...
Inline Helpers
Functions can be too generic.
 function hook(obj, name, a, b) {
   return obj[name] && obj[name](a, b);
 }
 hook(obj, 'foo', 1, 2);
 hook(obj, 'foo', 'a', {b:'c'});
 obj.foo(1, 2);deopts after a few different values
Short-Circuitting
The cheapest function call
is the one you never make
- unknown
Make decisions based on data
Fin
Preact: Into the void 0
By developit
Preact: Into the void 0
- 9,870