Topic describes

HTML can be treated as a kind of serialization, where the browser interprets (deserializes) the HTML text, and then builds a DOM tree. In addition to xmL-based solutions, we can also try JSON. If you print the React representation for element, it looks like this:

const el = <div>
 <h1> this is </h1>
 <p className="paragraph"> a <button> button </button> from <a href="https://bfe.dev"><b>BFE</b>.dev</a>
 </p>
</div>;

console.log(el)
Copy the code

The following data structure is obtained:

{
  type: 'div'.props: {
    children: [{type: 'h1'.props: {
          children: ' this is '}}, {type: 'p'.props: {
          className: 'paragraph'.children: [
            ' a ',
            {
              type: 'button'.props: {
                children: ' button '}},' from',
            {
              type: 'a'.props: {
                href: 'https://bfe.dev'.children: [{type: 'b'.props: {
                      children: 'BFE'}},'.dev']}}]}}Copy the code

Topic link

Thought analysis

To implement JSX <=> virtual DOM, it is not very good to judge the difficulty, from the process of processing data to generate HTML more in line with the usual development logic, from vDOM -> JSX start it! The most intuitive rule, virtual DOM is a tree, we need to determine the parent-child relationship

  • atypeThere is *children
  • childrenContains string nodes and labeled nodes
  • atypeCorresponds to aprops
  • propsIn addition tochildrenThe rest are attributes on the element

With these four points sorted out, you can start writing code

function render(json) {
  // textNode
  if (typeof json === 'string') {
    return document.createTextNode(json);
  }
  
  // element
  const {type, props: {children, ... attrs}} = json;// Create a node when type occurs
  const element = document.createElement(type);
  
  // Add attributes to the node
  Object.entries(attrs).forEach(([attr, value]) = > element[attr] = value);
  
  // Add children to the node
  const childrenArr = Array.isArray(children) ? children : [children];
  childrenArr.forEach(child= > element.append(render(child)));
  
  return element;
}
Copy the code

JSX => vDOM. The first reaction is that the vDOM data structure (looks) is the same as JSX

function virtualize(element) {
  const result = {
    type: element.tagName,
    props: {}};// Add attributes first
  element.attributes.forEach... props[k] = v;
  
  // Add children to loop node recursion (add attributes and children)
  element.childNodes.forEach... props.children = newChildren
}
Copy the code

Follow this thread to complete the code:

function virtualize(element) {
  const result = {
    type: element.tagName.toLowerCase(),
    props: {}}// attrs
  element.attributes.forEach(attr= > {
    const name = attr.name === 'class' ? 'className' : attr.name;
    result.props[name] = attr.value;
  });
  
  // children
  const children = [];
  element.childNodes.forEach(node= > {
    if (node.nodeType === 3) {
      children.push(node.textContent);
    } else{ children.push(virtualize(node)); }}); result.props.children = children.length ===1 ? children[0] : children;

  return result;
}
Copy the code

AC code

function render(json) {
  // textNode
  if (typeof json === 'string') {
    return document.createTextNode(json);
  }
  
  // element
  const {type, props: {children, ... attrs}} = json;// Create a node when type occurs
  const element = document.createElement(type);
  
  // Add attributes to the node
  Object.entries(attrs).forEach(([attr, value]) = > element[attr] = value);
  
  // Add children to the node
  const childrenArr = Array.isArray(children) ? children : [children];
  childrenArr.forEach(child= > element.append(render(child)));
  
  return element;
}

function virtualize(element) {
  const result = {
    type: element.tagName.toLowerCase(),
    props: {}}// attrs
  element.attributes.forEach(attr= > {
    const name = attr.name === 'class' ? 'className' : attr.name;
    result.props[name] = attr.value;
  });
  
  // children
  const children = [];
  element.childNodes.forEach(node= > {
    if (node.nodeType === 3) {
      children.push(node.textContent);
    } else{ children.push(virtualize(node)); }}); result.props.children = children.length ===1 ? children[0] : children;

  return result;
}
Copy the code

conclusion

After the simple conversion of vDOM and JSX, we can try to write a simple version of the React. CreateElement title link to write a render and h function to generate JSX and VDOM, respectively

render(h(
  'div',
  {},
  h('h1', {}, ' this is '),
  h(
    'p',
    { className: 'paragraph' },
    ' a ',
    h('button', {}, ' button '),
    ' from ',
    h('a', 
      { href: 'https://bfe.dev' }, 
      h('b', {}, 'BFE'),
      '.dev'))))Copy the code

Function h corresponds to vDOM above

function h(type, props, ... children) { return { type, props: { ... props, children } }; }Copy the code

Render function as above

This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign