React——Diff算法的简单实现

创建React元素类

//element.js

//React元素类
class Element {
  constructor(type, props, children) {
    this.type = type;
    this.props = props;
    this.children = children;
  }
}
//创建React元素
function createElement(type, props, children) {
  return new Element(type, props, children);
}

export { Element, createElement }; 

渲染React元素至页面

//render.js

import { Element } from "./element.js";
//设置DOM属性
function setAttr(ele, key, val) {
  switch (key) {
    case "value":
      if (ele.tagName == ("INPUT" || "TEXTAREA")) {
        ele.value = val;
      } else {
        ele.setAttribute(key, val);
      }
      break;
    case "className":
      ele.setAttribute("class", val);
      break;
    default:
      ele.setAttribute(key, val);
      break;
  }
}
//渲染React元素为DOM元素
function render(elementObject) {
  let ele = document.createElement(elementObject.type);
  for (const key in elementObject.props) {
    setAttr(ele, key, elementObject.props[key]);
  }
  elementObject.children.forEach((child) => {
    let childNode;
    if (child instanceof Element) {
      childNode = render(child);
    } else {
      childNode = document.createTextNode(child);
    }
    renderDOM(childNode, ele);
  });
  return ele;
}
//渲染DOM
function renderDOM(ele, target) {
  target.appendChild(ele);
  return ele;
}

export { setAttr, render, renderDOM };

diff算法核心

//diff.js

let Index = 0;

function diff(oldTree, newTree) {
  let patches = {};
  treeWalker(oldTree, newTree, patches, Index);
  return patches;
}

function treeWalker(oldNode, newNode, patches, index) {
  let currentPatch = []; //当前节点的补丁包
  if (!newNode) {
    //新DOM节点不存在
    currentPatch.push({ type: "REMOVE", index });
  } else if (isString(oldNode) && isString(newNode)) {
    //文本的变化
    currentPatch.push({ type: "TEXT", text: newNode });
  } else if (oldNode.type === newNode.type) {
    //当节点类型相同时,比较属性是否相同
    let attrs = diffAttr(oldNode.props, newNode.props);
    if (Object.keys(attrs).length) {
      currentPatch.push({ type: "ATTRS", attrs });
    }
    diffChildren(oldNode.children, newNode.children, patches);
  } else {
    //节点类型不相同,直接替换
    currentPatch.push({ type: "REPLACE", newNode: newNode });
  }

  if (currentPatch.length > 0) {
    patches[index] = currentPatch;
  }
}

function diffAttr(oldAttrs, newAttrs) {
  let patch = {};
  for (const key in oldAttrs) {
    if (oldAttrs[key] !== newAttrs[key]) {
      patch[key] = newAttrs[key];
    }
  }
  for (const key in newAttrs) {
    if (!oldAttrs.hasOwnProperty(key)) {
      patch[key] = newAttrs[key];
    }
  }
  return patch;
}

function diffChildren(oldChildren, newChildren, patches) {
  oldChildren.forEach((child, idx) => {
    treeWalker(child, newChildren[idx], patches, ++Index);
  });
}

function isString(node) {
  return Object.prototype.toString.call(node) == "[object String]";
}

export { diff, isString };

获取diff算法返回的补丁,为dom打上补丁

//patch.js

import { render } from "./render.js";
let Index = 0;
function patch(node, patches) {
  walker(node, patches, Index);
}

function walker(node, patches, index) {
  let patch = patches[index++];
  let childNodes = node.childNodes;
  childNodes.forEach((child) => {
    walker(child, patches, index++);
  });
  if (patch) {
    doPatch(node, patch);
  }
}

function doPatch(node, patch) {
  if (patch) {
    patch.forEach((item) => {
      switch (item.type) {
        case "ATTRS":
          for (const key in patch.attrs) {
            let value = patch.attrs[key];
            node.setAttribute(key, value);
          }
          break;
        case "TEXT":
          node.nodeValue = item.text;
          break;
        case "REMOVE":
          node.parentNode.removeChild(node);
          break;
        case "REPLACE":
          let newNode = render(item.newNode);
          node.parentNode.replaceChild(newNode, node);
          break;
        default:
          break;
      }
    });
  }
}

export default patch;

试验一下diff算法能否成功

//index.js

import { renderDOM, render } from "./src/DOM.js";
import { createElement } from "./src/element.js";
import { diff } from "./src/diff.js";
import patch from "./src/patch.js";

let VNode = createElement("ul", { className: "father" }, [
  createElement("li", { className: "child" }, ["a"]),
  createElement("li", { className: "child" }, ["b"]),
  createElement("li", { className: "child" }, ["c"]),
]);

let el = renderDOM(render(VNode), document.getElementById("app"));

let VNode2 = createElement(
  "div",
  { className: "father2", style: "color:red" },
  [
    createElement("div", { className: "child" }, ["d"]),
    createElement("div", { className: "child" }, ["f"]),
    createElement("h1", { className: "child" }, ["g"]),
  ]
);

let patches = diff(VNode, VNode2);

patch(el, patches);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的前端小黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值