实现一个 Mini React:核心功能详解 - diff 算法的迷你版实现

94 阅读1分钟

xdm,又要到饭了,又更新代码了!

总结一下上一篇完成的内容,

  1. 实现了mini useEffect hook函数

有兴趣的可以点这里查看useEffect hook的迷你版实现

这篇将会实现一个diff 算法的迷你版本。这里,我们实现一个简化版的 Diff 算法,支持节点类型和文本内容的比较。

function updateDom(dom, prevProps, nextProps) {
  // 移除旧的属性
  Object.keys(prevProps).forEach(name => {
    if (name.startsWith('on')) {
      const eventType = name.slice(2).toLowerCase();
      dom.removeEventListener(eventType, prevProps[name]);
    } else if (!(name in nextProps)) {
      dom[name] = '';
    }
  });

  // 设置新的属性
  Object.keys(nextProps).forEach(name => {
    if (name.startsWith('on')) {
      const eventType = name.slice(2).toLowerCase();
      dom.addEventListener(eventType, nextProps[name]);
    } else if (name === 'style') {
      Object.assign(dom.style, nextProps[name]);
    } else {
      dom[name] = nextProps[name];
    }
  });
}

function diff(vdom, container, oldDom = container.firstChild) {
  if (!oldDom) {
    render(vdom, container);
    return;
  }

  if (vdom.type !== oldDom.nodeName.toLowerCase() && vdom.type !== 'TEXT_ELEMENT') {
    const newDom = document.createElement(vdom.type);
    updateDom(newDom, {}, vdom.props);
    vdom.children.forEach(child => render(child, newDom));
    container.replaceChild(newDom, oldDom);
    return;
  }

  // 更新属性
  updateDom(oldDom, oldDom.props || {}, vdom.props);

  // 更新子节点
  const max = Math.max(vdom.children.length, oldDom.childNodes.length);
  for (let i = 0; i < max; i++) {
    diff(vdom.children[i], oldDom, oldDom.childNodes[i]);
  }
}

上述 diff 函数比较新旧虚拟 DOM,更新属性并递归比较子节点。

  1. 如果节点类型不同,则替换整个节点;
  2. 否则,更新属性并递归处理子节点。
  3. diff算法的主旨就是完成对比新旧虚拟dom,找出需要更新的地方,利用cpu的算力减少不必要的渲染。

虽然真正的源码会有更加复杂的实现但是思路和解决的问题是相同的。

下一篇,我们将自己实现迷你版本的fiber 架构以帮助我们增强其原理的理解。

如果这样的长度/强度你觉得可以接受,觉得有帮助,可以继续阅读下一篇,实现一个 Mini React:核心功能详解 - fiber 的迷你版实现。

如果文章对你有帮助,请点个赞支持一下!

啥也不是,散会。