React Virtual DOM

Why Using Virtual DOM?

How Browser Render

Updating Flow

  1. Browser have to parses the HTML
  2. It removes the child element of elementId
  3. Updates the DOM with the “New Value”
  4. Re-calculate the CSS for the parent and child
  5. Update the layout i.e. each elements exact co-ordinates on the screen
  6. Traverse the render tree and paint it on the browser display
document.getElementById('elementId').innerHTML = "New Value"

What React Virtual DOM Do

  1. More Easily to Bind
  2. Efficient diff algorithm
  3. Batched update operations
  4. Efficient update of sub tree only
  5. Uses observable instead of dirty checking to detect change
  6. Cross Browser Support
  7. Cross Platform Support (Ex: React-Native)

How React Building
Virtual DOM?

Mechanism

How to create?

<div class="title">
  <span>RRRRRRR Stanney</span>
  <ul>
    <li>Apple</li>
    <li>Orange</li>
  </ul>
</div>
const VitrualDom = {
  type: 'div',
  props: { class: 'title' },
  children: [
    {
      type: 'span',
      children: 'RRRRRRR Stanney'
    },
    {
      type: 'ul',
      children: [
        { type: 'li', children: 'Apple' },
        { type: 'li', children: 'Orange' }
      ]
    }
  ]
}

createElement

class App extends Component {
  render() {
    return <div>Stanney</div>;
  }
}

class App extends Component {
  render() {
    return React.createElement('div', null, `App Stanney`);
  }
}


JSX -> Babel

<div>
  <img src="member.png" className="profile" />
  <Hello />
</div>;

React.createElement("div", null, React.createElement("img", {
  src: "member.png",
  className: "profile"
}), React.createElement(App, null));

1. 這也是為何 Component 會需要一定是大寫的原因
2. 也同時是因為 Component 不能是動態名稱的原因

SOURCE: createElement

export function createElement(type, config, children) {
  let propName;

  // Reserved names are extracted
  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  if (config != null) {
    // ...
    // Handle Props
  }

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  // ...
  // Get Children
  // Resolve default props
  if (type && type.defaultProps) {
    // ...
    // Handle defaultProps
  }
  if (__DEV__) {
    if (key || ref) {
      const displayName =
        typeof type === 'function'
          ? type.displayName || type.name || 'Unknown'
          : type;
      if (key) {
        defineKeyPropWarningGetter(props, displayName);
      }
      if (ref) {
        defineRefPropWarningGetter(props, displayName);
      }
    }
  }
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

1. Handle Props
2. Get Children

3. Handle Default Props

SOURCE: Handle props

if (hasValidRef(config)) {
  ref = config.ref;
}
if (hasValidKey(config)) {
  key = '' + config.key;
}

self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
  if (
    hasOwnProperty.call(config, propName) &&
    !RESERVED_PROPS.hasOwnProperty(propName)
  ) {
    props[propName] = config[propName];
  }
}

1. Take ref、key、self、source from config
2. Set Other props from config

SOURCE: Get Children

  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }

1. Check Children Length
2. Set one children or childArray

SOURCE: Handle Default Props

if (type && type.defaultProps) {
  const defaultProps = type.defaultProps;
  for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
}

Virtual DOM to
Real DOM

Flow

Diff

When every time setState call

React Synthetic Event

Event dispatch and DOM event flow 

Event dispatch and DOM event flow

const get = (id) => document.getElementById(id);
const $list = get('list');
const $list_item = get('list_item');
const $list_item_link = get('list_item_link');
  
// list 的捕獲
$list.addEventListener('click', (e) => {
  console.log('list capturing', e.eventPhase);
}, true)
  
// list 的冒泡
$list.addEventListener('click', (e) => {
  console.log('list bubbling', e.eventPhase);
}, false)
  
// list_item 的捕獲
$list_item.addEventListener('click', (e) => {
  console.log('list_item capturing', e.eventPhase);
}, true)
  
// list_item 的冒泡
$list_item.addEventListener('click', (e) => {
  console.log('list_item bubbling', e.eventPhase);
}, false)
  
// list_item_link 的捕獲
$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link capturing', e.eventPhase);
}, true)
  
// list_item_link 的冒泡
$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link bubbling', e.eventPhase);
}, false)
list capturing
1
list_item capturing
1
list_item_link capturing
2
list_item_link bubbling
2
list_item bubbling
3
list bubbling
3

React Synthetic Event

Demo

Made with Slides.com