乞丐版react-dom

777 阅读2分钟

序言

一致使用react;一直想深入了解react运行机制

本周开始,跟着大神学习react源码

rander

获取虚拟DOM,调用createNode;将虚拟DOM生成为真实DOM

createNode

根据虚拟DOM的type类型将虚拟节点转换为真实节点;

  • 如果虚拟DOM类型是number或者string,调用updateTextComponent
  • 如果虚拟DOM的type类型为string,调用updateHostComponent
  • 如果虚拟DOM的type类型为function,还要判断函数是否是reactComponent
    • type.prototype.isReactComponent有值 调用updateClassComponent
    • 如果type.prototype.isReactComponent没有值 调用updateFunctionComponent
  • 其他类型判断为fragment标签,调用updateFragmentComponent

最后返回真实节点

updateTextComponent

调用document.createTextNode创建文本节点

function updateTextComponent(vnode) {
  const node = document.createTextNode(vnode);
  return node;
}

updateHostComponent

如果type是字符串表示什么意思呢?

type是字符串,表示type = 'div' 、a 、span 、 h1等html标签

通过调用 document.createElement 创建html节点

创建完html节点后,调用updateNode给html对象添加属性;

什么是html节点的属性呢?

  • class
  • id
  • style
  • key 等...
function updateHostComponent(vnode) { 
    const { type, props } = vnode; 
    const node = document.createElement(type); 
    updateNode(node, props); 
    reconcileChildren(node, props.children); 
    return node; 
}

updateNode

function updateNode(node, nextValue) { 
    Object.keys(nextValue).filter((k) => k !== 'children').forEach((k) => {
        node[k] = nextValue[k];
   }); 
}

这里就比较好理解了,html节点是个对象,对象吗,[]可以添加属性了,将prop属性枚举给html节点即可

代码

//把vnode -> node并插入

// vnode 虚拟节点
// node 真是节点
function render(vnode, container) {
  const node = createNode(vnode);

  container.appendChild(node);
}
function isStringOrNumber(sth) {
  return typeof sth === 'string' || typeof sth === 'number';
}
//将虚拟节点转换为真是节点
function createNode(vnode) {
  console.log('vnode', vnode);
  const { type } = vnode;
  let node;

  //原生标签节点
  if (typeof type === 'string') {
    node = updateHostComponent(vnode);
  } else if (isStringOrNumber(vnode)) {
    //文本标签
    node = updateTextComponent(vnode);
  } else if (typeof type === 'function') {
    //类组件,函数组件
    node = type.prototype.isReactComponent
      ? updateClassComponent(vnode)
      : updateFunctionComponent(vnode);
  } else {
    // fragment标签
    node = updateFragmentComponent(vnode);
  }
  return node;
}
function updateFragmentComponent(vnode) {
  //const d =
  const { props } = vnode;
  const node = document.createDocumentFragment();
  reconcileChildren(node, props.children);
  return node;
}

//根据虚拟节点创建类组件
function updateClassComponent(vnode) {
  const { type, props } = vnode;

  const instance = new type(props);
  const child = instance.render();

  const node = createNode(child);

  return node;
}

//根据虚拟节点创建函数组件

function updateFunctionComponent(vnode) {
  const { type, props } = vnode;
  const child = type(props);
  const node = createNode(child);
  return node;
}

//根据虚拟节点创建文本节点
function updateTextComponent(vnode) {
  const node = document.createTextNode(vnode);
  return node;
}

//根据虚拟节点生成原生标签节点,diva,p,h1
function updateHostComponent(vnode) {
  const { type, props } = vnode;
  const node = document.createElement(type);
  updateNode(node, props);
  reconcileChildren(node, props.children);
  return node;
}

//更新原生标签属性和属性值
function updateNode(node, nextValue) {
  Object.keys(nextValue)
    .filter((k) => k !== 'children')
    .forEach((k) => {
      node[k] = nextValue[k];
    });
}

// 处理子节点
function reconcileChildren(parentNode, children) {
  //将children转换为数组
  const newChildren = Array.isArray(children) ? children : [children];
  for (let i = 0; i < newChildren.length; i++) {
    const child = newChildren[i];
    // vnode 变成node ;并将node插入parentNode

    render(child || '', parentNode);
  }
}
export default { render };