前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
  • Vue

    • 从源码解读Vue生命周期
    • 组件的本质
    • 有状态组件的设计
    • 设计 VNode
    • 辅助创建 VNode 的 h 函数
    • 自定义渲染器和异步渲染
    • 渲染器之挂载
    • 渲染器的核心 Diff 算法
    • 渲染器之patch
      • 基本原则
      • 替换 VNode
      • 更新标签元素
        • 更新标签元素的基本原则
        • 更新 VNodeData
        • 更新子节点
      • 更新文本节点
      • 更新 Fragment
      • 更新 Portal
      • 有状态组件的更新
        • 主动更新
        • 初步了解组件的外部状态 props
        • 被动更新
        • 我们需要 shouldUpdateComponent
      • 函数式组件的更新
    • 图解 Vue 响应式原理
    • 图解 Vue 异步更新
    • 剖析 Vue 内部运行机制
    • vue响应式原理模拟
    • vue状态管理之vuex
    • 理解Vue的设计思想及实现Vue
    • diff算法深入
    • vue router vuex原理分析
    • Vue3初探响应式原理.
    • vue2源码分析
    • vue组件化实践
  • React

  • Webpack

  • Node

  • Javascript

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

在上一章中我们讲解并实现了渲染器的挂载逻辑,本质上就是将各种类型的 VNode 渲染成真实DOM的过程。渲染器除了将全新的 VNode 挂载成真实DOM之外,它的另外一个职责是负责对新旧 VNode 进行比对,并以合适的方式更新DOM,也就是我们常说的 patch。本章内容除了让你了解基本的比对逻辑之外,还讲述了在新旧 VNode 比对的过程中应该遵守怎样的原则,让我们开始吧!

# 基本原则

通常重渲染(re-render)是由组件的更新开始的,因为在框架的使用层面开发者通过变更数据状态从而引起框架内部对UI的自动更新,但是组件的更新本质上还是对真实DOM的更新,或者说是对标签元素的更新,所以我们就优先来看一下如何更新一个标签元素。

我们首先回顾一下渲染器的代码,如下:

function render(vnode, container) {
  const prevVNode = container.vnode
  if (prevVNode == null) {
    if (vnode) {
      // 没有旧的 VNode,使用 `mount` 函数挂载全新的 VNode
      mount(vnode, container)
      // 将新的 VNode 添加到 container.vnode 属性下,这样下一次渲染时旧的 VNode 就存在了
      container.vnode = vnode
    }
  } else {
    if (vnode) {
      // 有旧的 VNode,则调用 `patch` 函数打补丁
      patch(prevVNode, vnode, container)
      // 更新 container.vnode
      container.vnode = vnode
    } else {
      // 有旧的 VNode 但是没有新的 VNode,这说明应该移除 DOM,在浏览器中可以使用 removeChild 函数。
      container.removeChild(prevVNode.el)
      container.vnode = null
    }
  }
}
@前端进阶之旅: 代码已经复制到剪贴板

如上高亮的两句代码所示,当使用 render 渲染器渲染一个全新的 VNode 时,会调用 mount 函数挂载该 VNode,同时让容器元素存储对该 VNode 对象的引用,这样当再次调用渲染器渲染新的 VNode 对象到相同的容器元素时,由于旧的 VNode 已经存在,所以会调用 patch 函数以合适的方式进行更新,如下代码所示:

// 旧的 VNode
const prevVNode = h('div')

// 新的 VNode
const nextVNode = h('span')

// 第一次渲染 VNode 到 #app,此时会调用 mount 函数
render(prevVNode, document.getElementById('app'))

// 第二次渲染新的 VNode 到相同的 #app 元素,此时会调用 patch 函数
render(nextVNode, document.getElementById('app'))
@前端进阶之旅: 代码已经复制到剪贴板

patch 函数会对新旧 VNode 进行比对,也就是我们所说的 diff,那么不同的两个 VNode 之间应该遵守怎样的比对规则呢?其实这个问题很容易回答,我们知道 VNode 有类型之分,不同类型的 VNode 之间存在一定的差异,所以不同的 VNode 之间第一个比对原则就是:只有相同类型的 VNode 才有比对的意义,例如我们有两个 VNode,其中一个 VNode 的类型是标签元素,而另一个 VNode 的类型是组件,当这两个 VNode 进行比对时,最优的做法是使用新的 VNode 完全替换旧的 VNode

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
  • Vue

    • 从源码解读Vue生命周期
    • 组件的本质
    • 有状态组件的设计
    • 设计 VNode
    • 辅助创建 VNode 的 h 函数
    • 自定义渲染器和异步渲染
    • 渲染器之挂载
    • 渲染器的核心 Diff 算法
    • 渲染器之patch
      • 基本原则
      • 替换 VNode
      • 更新标签元素
        • 更新标签元素的基本原则
        • 更新 VNodeData
        • 更新子节点
      • 更新文本节点
      • 更新 Fragment
      • 更新 Portal
      • 有状态组件的更新
        • 主动更新
        • 初步了解组件的外部状态 props
        • 被动更新
        • 我们需要 shouldUpdateComponent
      • 函数式组件的更新
    • 图解 Vue 响应式原理
    • 图解 Vue 异步更新
    • 剖析 Vue 内部运行机制
    • vue响应式原理模拟
    • vue状态管理之vuex
    • 理解Vue的设计思想及实现Vue
    • diff算法深入
    • vue router vuex原理分析
    • Vue3初探响应式原理.
    • vue2源码分析
    • vue组件化实践
  • React

  • Webpack

  • Node

  • Javascript

  • 综合