当面试官问你patch()的实现过程时,还会不知道说什么吗吗?

本文探讨了在Vue.js面试中常见的patch()方法的实现,包括Diff算法的基础和patch()如何更新DOM。通过讲解createElement函数创建真实DOM,以及如何在属性变化时进行局部渲染,阐述了patch()的核心思想。虽然实际源码更复杂,但了解这些基本概念足以应对面试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

不得不说,随着越来越多的企业的前端框架选择vue之后,面试也变得越来越卷了,早在几年前,关于vue的面试题还只是v-model的使用、vue的生命周期、数据的变化是怎么检测到的等等一系列表面的问题,现在的情况却变成了,你没了解过vue的源码都不好意思说自己懂vue...

Diff算法

一说起diff算法,相信很多人都不陌生,对两个虚拟dom进行比较嘛,只将数据真正进行变化的地方更新在真实的dom上。其实vue对diff算法的实现就用到了我们的打补丁的 方式,也就是接下来提到的patch(),现在的面试经常会出现的一道面试题便是让我们手写一下patch()的实现过程(简单实现),这时候很多人便会一脸懵,我没写过这个呀,没关系,其实patch()呀,真没我们想的那么的高大上,也不是那么难以理解滴,废话少说,我们代码上见

patch的实现过程

function createElement(vnode){
  let tag = vnode.tag //目标元素
  let attrs = vnode.attr || {} //属性
  let children = vnode.children || [] //子节点
  if(!tag){
    return null
  }
  //创建对应dom
  let elem = document.createElement(tag)
  let attrName
  //给dom添加属性
  for(attrName in attrs){
    if(attrs.haOwnProperty(attrName)){
      elem.setAttribute(attrName,attr[attrName])
    }
  }
  //将子元素添加到目标元素
  children.forEach(function(childVnode){
    elem.appendChild(createElement(childVnode))
  })
  return elem
}

首先我们定义了一个名为createElement的函数,目的便是将虚拟dom转变为真实的dom,将此函数接收到的参数是我们通过render()函数生成的虚拟dom,我们起个名字叫做vnode。注意:这个render是vue源码中生成虚拟dom的函数,不是我们平时使用的render。我们使用了三个变量:tag来表示我们的目标元素,attrs来表示这个元素下面的属性,children表示这个元素下面还包含的子元素。因为目标元素下面的属性和子节点都有可能为空,所以要加一个“ || ”

接下来就开始判断了,首先,如果不存在tag,就没有往下进行的必要了,直接return一个null,完活。如果存在这个tag我们该怎么操作呢?其实啊就是使用的我们刚开始学习js的时候使用的document.createElement来创建一个真实的节点。节点创建好了,接下来是干嘛呢?没错,就是将下面的属性和子节点都挂上去不就可以了嘛。一个for循环,一个forEach(),大家都不陌生吧,挂载完成之后将这个真实的节点返回出去就完成了。

当属性发生变化时所进行的操作

不知道大家有没有想过一个问题,当数据进行变化的时候,我们的vue是把整个节点全部销毁,然后创建一个全新的节点吗?其实不是这样的,我们的vue是很聪明的,他会比较出两个虚拟dom之间的差异,只将变化的部分进行重新渲染,也就是我们平时经常提到的局部渲染。还是老套路,我们代码上见

function updataChildren(vnode,newVnode){
  let children = vnode.children || []
  let newChildren = newVnode.children || []
  children.forEach(function(childVnode,index){
    let newChildrenVnode = newChildren[index]
    //第一层没有变化
    if(childVnode.tag === newChildrenVnode.tag){
      updataChildren(childrenVnode,newChildrenVnode)
    }else{
      replaceNode(childrenVnode,newChildrenVnode)
    }
  })
}

我们定义了一个函数来对两个节点进行比较,一个是之前的虚拟dom,我们叫它vdom,一个是新生成的dom,我们叫它newVnode。还是熟悉的代码,还是熟悉的操作,将两个不同的元素下面的children列出来,循环进行比较每一层,如果第一层没有变化,那就进行递归操作,一直向下遍历,直到找到不同的地方,将变化的地方替换掉,到此为止,我们的patch操作就可以告一段落了。

总结

当然了,真正的源码中对patch的实现不可能就这么的简单,我们只创建了子节点,下面的属性两个小部分,而且进行更新的操作也只是简单的进行了实现。其实,面试中能把这些写出来并且给面试官讲清楚,实际上就已经足够了,他也不可能让我们把源码所实现的所有操作都写出来,别说我们了,面试官都不一定能全部写出来。其实啊,还有很多细节我们没去了解,看代码我们就可以看得出来,真实的操作中真的会逐层遍历每个子节点和子元素吗?那性能浪费的也太严重了吧,更高明的就在这里,vue会通过一系列操作,为每个节点中容易发生变化的地方做上记号,这样遍历起来就能更快的找到数据发生更改的地方,这个功能实现有亿点的复杂,这里就不做过多的介绍,感兴趣的朋友们去源码上一探究竟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值