19-实现原生组件的渲染

60 阅读1分钟

React的简单实现

/**
 * @param {*} type 元素的类型
 * @param {*} config 配置对象
 * @param {*} children 儿子或儿子们
 */
function createElement(type, config, children) {
  if(config) {
    delete config.__source;
    delete config.__self;
  }
  let props = {...config};
  // 有多个儿子变成数组
  if(arguments.length > 3) {
    children = Array.prototype.slice.call(arguments, 2);
  }

  props.children = children;
  return {
    type,
    props
  }
}

const React = {
  createElement
}

export default React

ReactDOM.render实现

/**
 * @param {*} vdom 虚拟dom 
 * @param {*} container 把虚拟转真实dom -> 放置的容器中
 */
function render(vdom, container) {
  // 把虚拟dom变成真实dom
  const dom = createDOM(vdom);

  // 把真实的dom挂载到容器上面
  container.appendChild(dom);
}

/**
 * 虚拟dom -> 真实dom
 * @param {*} vdom 虚拟dom 
 */
function createDOM(vdom) {
  // 如果是数字或者字符串 -> 真实的文本节点
  if (["string", "number"].includes(typeof vdom)) {
    return document.createTextNode(vdom);
  }

  // 否则是虚拟DOM对象,也就是react元素
  let {type, props} = vdom;
  let dom = document.createElement(type);
  // 使用虚拟dom的属性添加给真实dom的属性
  updateProps(dom, props);

  // 处理children
  // 只有一个儿子,为文本的时候
  if(["string", "number"].includes(typeof props.children)) {
    // 优化方案
    dom.textContent = props.children; 
  } else if(typeof props.children === "object" && props.children.type) {
    // 只有一个儿子,并且为虚拟dom对象
    // **把儿子变成真实dom插在自己身上**
    render(props.children, dom);// 递归

  } else if (Array.isArray(props.children)) {
    // 多个儿子
    reconcileChildren(props.children, dom);
  } else {
    // 兜底的
    document.textContent = props.children ? props.children.toString(): "";
  }
  return dom;
}

/**
 * 
 * @param {*} dom 真实dom 
 * @param {*} newProps 新属性
 */
function updateProps(dom, newProps) {
  for(let key in newProps) {
    if(key === "children") continue;//单独处理children,不在这里处理
    if(key === "style") {
      let styleObj = props.style;
      for(let attr in styleObj) {
        dom.style[attr] = style[attr];
      }
    }else {
      // className也是可以赋值给属性的
      dom[key] = newProps[key];
    }
  }
}

/**
 * 处理children是数组的情况
 * @param {*} children 儿子组的虚拟dom
 * @param {*} dom 父亲的真实dom
 */
function reconcileChildren(children, dom) {
  for(let i = 0; i < children.length; i++) {
    let childVdom = children[i];
    // 递归
    render(childVdom, dom);
  }
}
const ReactDOM = {
  render
}

export default ReactDOM;

这里是渲染的原理后续接着补充完整