react-2

2020-09-06  本文已影响0人  成熟稳重的李先生

用jsx写的代码,babel会将其转化为react的虚拟dom,如下图:

image.png
可以看到,转化后,是用React.createElement方法来生成dom的,它大约有3(更多)个参数: type,props, childrenprops是这个元素的详情,children可以有多个,代表这个元素的子元素, 那么它的执行结果又是什么呢?
image.png

上图打印出的对象就是react元素,他是一个类似真实dom的对象,type是标签名,props是其属性,像attribute,class,id等都存在于此,props中还有一个比较特殊的属性children,它是子元素集合(只有一个子元素(即文本元素)时,它的类型为string)。
接下来,我们就来写一个简易版的react+react-dom

// 首先,完成react
function createElement(type, config, ...children) {
  // 将children使用rest参数的方法合并起来
  let props = { ...config, children };
  return { type, props };  // 此时,就生成虚拟元素
}

export default {
  createElement
};

接下来是重头戏,react-dom

function render(element, parent) {
  if (typeof element === "string") {
    //文本元素,直接生成,随后插入父元素
    element = document.createTextNode(element);
  }
  let { type, props } = element; //element是一个虚拟dom对象
  if (typeof type === "string") {
    element = document.createElement(type);
    for (let propName in props) {
      if (propName === "style") {  // 添加样式
        Object.entries(props.style).forEach(([attr, value]) => {
          element.style[attr] = value;
        });
      } else if (propName === "className") {  // 添加class
        element.className = props.className;
      } else if (propName === "children") { 
        props.children.forEach(child => {  // 生成child并且插入父级
          return render(child, element);
        });
      }
    }
  }
  parent.appendChild(element);
}

export default {
  render
};

业务代码:

//index.js
...
let elm = React.createElement(
  "h1",
  { className: "title", style: { backgroundColor: "green" } },
  "hello",
  React.createElement("span", null, "world")
);
ReactDOM.render(elm, document.getElementById("root"));
image.png
正常输出了!
以上,只是演示了普通的html元素,接下来,再兼容函数组件类组件
//index.js
function Welcome(props) { //函数式组件
  return React.createElement(
    "h1",
    { className: "title", style: { backgroundColor: "green" } },
    props.name,
    React.createElement("span", null, props.age)
  );
}
// class Welcome extends React.Component {   //类组件
//   render() {
//     return React.createElement(
//       "h1",
//       { className: "title", style: { backgroundColor: "green" } },
//       this.props.name,
//       React.createElement("span", null, this.props.age)
//     );
//   }
// }
let element = React.createElement(Welcome, { name: "lc", age: 18 });
ReactDOM.render(element, document.getElementById("root"));

修改react/index.js,以支持类组件

function createElement(type, config, ...children) {
  let props = { ...config, children };
  let element = { type, props };
  return element;
}

class Component {
  static isReactComponent = true;
  constructor(props) {
    this.props = props;
  }
}

export default {
  createElement,
  Component,
};

再来修改react-dom,以支持解析函数式和类组件

//  react-dom/index.js
function render(element, parent) {
  let { type, props } = element; //element是一个虚拟dom对象
  if (typeof element === "string" || typeof element === "number") { //文本元素
    //文本元素,直接生成,随后插入父元素
    element = document.createTextNode(element);
  } else if (type.isReactComponent) { 
    //类组件
    element = new type(props).render();
    type = element.type;
    props = element.props;
  } else if (typeof type === "function") {  // 函数组件
    element = type(props);
    type = element.type;
    props = element.props;
  }
  if (typeof type === "string") {  // 
    element = document.createElement(type);
    for (let propName in props) {
      if (propName === "style") {
        Object.entries(props.style).forEach(([attr, value]) => {
          element.style[attr] = value;
        });
      } else if (propName === "className") {
        element.className = props.className;
      } else if (propName === "children") {
        props.children.forEach(child => {
          return render(child, element);
        });
      }else{
          dom.setAttribute(propName, props[propName])
      }
    }
  }
  parent.appendChild(element);
}

export default {
  render
};
上一篇 下一篇

猜你喜欢

热点阅读