JavaScript学习记录:Js实现简易虚拟Dom

1.概念:

  • Virtual DOM 是真实DOM的映射。

  • 当虚拟 DOM 树中的某些节点改变时,会得到一个新的虚拟树。算法对这两棵树(新树和旧树)进行比较,找出差异,然后只需要在真实的 DOM 上做出相应的改变。

 2.设计思路:

  •         虚拟Dom映射到真实Dom的工具函数 createElement(…) ,将js对象映射为Dom对象
  •         比较两棵虚拟DOM树的差异并更新的工具函数updateElement(…) ,包括新节点的添加,老节点的移除以及节点的替换;

 3.实现:

  •  createElement(…),利用document.createTextNode 、document.createElement实现文本节点和dom节点的基本创建

/**
 * @description 传入node类型的js对象数据,生成Dom
 * @param node node类型js对象数据 节点数据
 */
function createElement(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  } else {
      const $el = document.createElement(node.type);
      node.children.map(createElement).forEach($el.appendChild.bind($el));
      return $el;
  }

 node的类型为:

const node = {
  type: "div", // 标签名或者是纯文本
  props: {}, // 扩展的功能,如class、id等属性的添加
  children: [],// node 类型的js对象数组
};
  • updateElement(…),利用createElement,添加一些条件判断 ,实现dom树的渲染、更新,应当考虑3个参数,$parent, newNode, oldNode ,稍后介绍,$parent是父节点

 首先是新节点的添加:

 // 不存在老节点,则直接创建新节点
function updateElement($parent, newNode, oldNode,index) {
  if (!oldNode) {
    $parent.appendChild(createElement(newNode));
  }
}

然后是存在oldNode存在而newNode为空的情况: 

// 实现移除老节点的操作,index为该节点在父节点的子节点数组中的位序
function updateElement($parent, newNode, oldNode, index) {
  if (!oldNode) {
    $parent.appendChild(createElement(newNode));
  } else if (!newNode) {
    $parent.removeChild($parent.childNodes[index]);
  }
}

 当oldNode,newNode都存在时:

// 节点的替换操作,
function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(createElement(newNode));
  } else if (!newNode) {
    $parent.removeChild($parent.childNodes[index]);
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(createElement(newNode), $parent.childNodes[index]);
  }
}
// 需要定义一个工具函数changed(newNode, oldNode),用于比较新老节点之间的差异
function changed(node1, node2) {
  return typeof node1 !== typeof node2 ||
         typeof node1 === ‘string’ && node1 !== node2 ||
         node1.type !== node2.type
}

当新旧节点没有差异时,需要调用updateElement实现递归操作其子节点:

// 完整的节点创建、更新函数
function updateElement($parent, newNode, oldNode, index) {
  if (!oldNode) {
    $parent.appendChild(createElement(newNode));
  } else if (!newNode) {
    $parent.removeChild($parent.childNodes[index]);
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(createElement(newNode), $parent.childNodes[index]);
  } else if (newNode.type) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i < newLength || i < oldLength; i++) {
      updateElement(
        $parent.childNodes[index],
        newNode.children[i],
        oldNode.children[i],
        i
      );
    }
  }
}

参考链接:https://mp.weixin.qq.com/s/VHlChNxA7-YQ9HodGb3r_g 

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值