Web前端最新vue虚拟dom核心方法patch解析_vue patch,前端开发毕业设计题目

总结

为了帮助大家更好温习重点知识、更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。

包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。

前端面试题汇总

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

JavaScript

性能

linux

https://github.com/snabbdom/snabbdom

在这里插入图片描述

可以说理解了snabbdom 中的具体方法即理解了vue中虚拟dom的核心思想:
当状态更新时,将新的JavaScript对象和旧的JavaScript对象进行比较(即diff算法),运算出两者的差异,将差异应用到真正的DOM,以此减少了DOM的操作。

diff 策略

在这里插入图片描述

  • 只比较同一级,不跨级比较
  • 标签名不同,直接删除,不继续深度比较
  • 标签名相同,key相同就认为是相同节点,不继续比较
patch

在这里插入图片描述

patch方法是虚拟dom核心中的核心。在VNode(虚拟节点)改变后和初始化前都会调用。

主要逻辑如下:
  • 调用 module 中的 pre hook(生命周期相关)
  • 如果传入的是 Element 转成空的 vnode(这里在首次创建时,会传入一个dom元素)
  • 新旧节点为 sameVnode 的话,则调用 patchVnode 更新 vnode , 否则创建新节点
  • 创建新节点 ,插入新节点 ,移除旧节点
  • 调用元素上的 insert hook(生命周期相关)
  • 调用元素上的 post hook(生命周期相关)
源码解析:
function patch(oldVnode: VNode | Element, vnode: VNode): VNode {
  let i: number, elm: Node, parent: Node;
  const insertedVnodeQueue: VNodeQueue = [];
  // 调用 module 中的 pre hook
  for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();

  // 如果传入的是 Element 转成空的 vnode
  if (!isVnode(oldVnode)) {
  //创建一个空的vnode 并关联DOM元素 emptyNodeAt
    oldVnode = emptyNodeAt(oldVnode);
  }

  // sameVnode 时 (sel 和 key相同) 调用 patchVnode
  // 判断两个参数是否是相同的vnode
  // 比较标签名 key
  if (sameVnode(oldVnode, vnode)) {
    patchVnode(oldVnode, vnode, insertedVnodeQueue);
  } else {
    elm = oldVnode.elm as Node;
    parent = api.parentNode(elm);

    // 创建新的 dom 节点 vnode.elm
    createElm(vnode, insertedVnodeQueue);

    if (parent !== null) {
      // 插入 dom
      api.insertBefore(parent, vnode.elm as Node, api.nextSibling(elm));
      // 移除旧 dom
      removeVnodes(parent, [oldVnode], 0, 0);
    }
  }

  // 调用元素上的 insert hook,注意 insert hook 在 module 上不支持
  for (i = 0; i < insertedVnodeQueue.length; ++i) {
    (((insertedVnodeQueue[i].data as VNodeData).hook as Hooks).insert as any)(insertedVnodeQueue[i]);
  }

  // 调用 module post hook
  for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
  return vnode;
}

function emptyNodeAt(elm: Element) {
  const id = elm.id ? '#' + elm.id : '';
  const c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
  return vnode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
}

// key 和 selector 相同
function sameVnode(vnode1: VNode, vnode2: VNode): boolean {
  return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
}

patchVnode
主要逻辑如下:
  • 调用 module 中的 prepatch hook(生命周期相关)
  • 按照新旧Vnode 的text 和children 的差异做出差异化处理
  • 值得注意的是:新旧节点均存在 children,且不一样时,对 children 进行 diff 调用 updateChildren
function patchVnode(oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) {
  let i: any, hook: any;
  // 调用 prepatch hook 生命周期钩子
  if (isDef((i = vnode.data)) && isDef((hook = i.hook)) && isDef((i = hook.prepatch))) {
    i(oldVnode, vnode);
  }
  //设置新的vnode的dom 关联
  const elm = (vnode.elm = oldVnode.elm as Node);
  let oldCh = oldVnode.children;
  let ch = vnode.children;
  if (oldVnode === vnode) return;
  if (vnode.data !== undefined) {
    // 调用 module 上的 update hook 生命周期钩子
    for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
    i = vnode.data.hook;
    // 调用 vnode 上的 update hook 生命周期钩子
    if (isDef(i) && isDef((i = i.update))) i(oldVnode, vnode);
  }
  if (isUndef(vnode.text)) {
    if (isDef(oldCh) && isDef(ch)) {
      // 新旧节点均存在 children,且不一样时,对 children 进行 diff
      if (oldCh !== ch) updateChildren(elm, oldCh as Array<VNode>, ch as Array<VNode>, insertedVnodeQueue);
    } else if (isDef(ch)) {
      // 旧节点不存在 children 新节点有 children
      // 旧节点存在 text 置空
      if (isDef(oldVnode.text)) api.setTextContent(elm, '');
      // 加入新的 vnode
      addVnodes(elm, null, ch as Array<VNode>, 0, (ch as Array<VNode>).length - 1, insertedVnodeQueue);
    } else if (isDef(oldCh)) {
      // 新节点不存在 children 旧节点存在 children 移除旧节点的 children
      removeVnodes(elm, oldCh as Array<VNode>, 0, (oldCh as Array<VNode>).length - 1);
    } else if (isDef(oldVnode.text)) {
      // 旧节点存在 text 置空
      api.setTextContent(elm, '');
    }
  } else if (oldVnode.text !== vnode.text) {
    // 更新 text
    api.setTextContent(elm, vnode.text as string);
  }
  // 调用 postpatch hook 生命周期钩子
  if (isDef(hook) && isDef((i = hook.postpatch))) {
    i(oldVnode, vnode);
  }
}

核心方法updateChildren

在这里插入图片描述
新的Vnode 和 老的Vnode 都有children时,进入方法。

  • 对新旧node分别赋值 newStartIdx oldStartIdx newEndIdx oldEndIdx 进行指针循环遍历
  • 对不符合same 的节点进行劲增 其他的进行patchVnode 的替换
  • 循环结束处理剩余节点 进行批量删除或批量新增
  function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    let oldStartIdx = 0
    let newStartIdx = 0
    let oldEndIdx = oldCh.length - 1
    let oldStartVnode = oldCh[0]


**ES6**

*   列举常用的ES6特性:

*   箭头函数需要注意哪些地方?

*   let、const、var

*   拓展:var方式定义的变量有什么样的bug?

*   Set数据结构

*   拓展:数组去重的方法

*   箭头函数this的指向。

*   手写ES6 class继承。

![](https://img-blog.csdnimg.cn/img_convert/aac1740e50faadb9a6a7a5b97f9ccba8.png)

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

**微信小程序**

*   简单描述一下微信小程序的相关文件类型?

*   你是怎么封装微信小程序的数据请求?

*   有哪些参数传值的方法?

*   你使用过哪些方法,来提高微信小程序的应用速度?

*   小程序和原生App哪个好?

*   简述微信小程序原理?

*   分析微信小程序的优劣势

*   怎么解决小程序的异步请求问题?

![](https://img-blog.csdnimg.cn/img_convert/60b1dbe5c76e264468aa993416a9a031.png)

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值