前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • Vue

    • 从源码解读Vue生命周期
    • 组件的本质
    • 有状态组件的设计
    • 设计 VNode
    • 辅助创建 VNode 的 h 函数
    • 自定义渲染器和异步渲染
    • 渲染器之挂载
    • 渲染器的核心 Diff 算法
    • 渲染器之patch
    • 图解 Vue 响应式原理
    • 图解 Vue 异步更新
    • 剖析 Vue 内部运行机制
    • vue响应式原理模拟
    • vue状态管理之vuex
    • 理解Vue的设计思想及实现Vue
    • diff算法深入
    • vue router vuex原理分析
    • Vue3初探响应式原理.
    • vue2源码分析
    • vue组件化实践
  • React

  • Webpack

  • Node

  • Javascript

  • 综合

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

虚拟DOM(一)|原理篇

文章学习导航
面试总览

30 秒速记

  • 核心判断:虚拟节点是对界面结构的描述,渲染器通过挂载与更新把声明式描述映射为宿主环境操作
  • 原理主线:围绕 「一、什么是 vdom」、「二、设计一个需求场景」、「三、vdom 的如何应用,核心 API 」 建立输入、状态变化与输出之间的因果关系
  • 文章范围:介绍了虚拟DOM(vdom)的概念、作用及其在前端开发中的应用场景,分析了传统DOM操作的性能瓶颈,并通过对比jQuery实现与vdom方案,讲解了vdom的核心API及其如何提升页面渲染效率,适合前端开发者深入理解虚拟DOM原理。
  • 边界与代价:key、节点类型和稳定序列决定复用边界;虚拟 DOM 解决可预测更新,不保证永远比手写 DOM 快
  • 工程落地:性能分析要看实际提交次数、节点移动和组件边界,不能只用“减少 DOM 操作”解释

虚拟 DOM 本质上是用 JavaScript 对象描述界面结构,再由渲染器把结构变化映射到真实 DOM。 首次渲染时,h 生成虚拟节点,patch(container, vnode) 完成挂载;数据变化后,再用新旧虚拟节点执行 patch(vnode, newVnode)。这样可以通过 diff 找出必须更新的节点,避免每次变化都把整棵真实 DOM 推倒重建。它仍然需要计算和最终的宿主操作,所以价值在于减少无效更新,并不等于完全没有性能成本。

这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。

版本校准: 原理文章中的代码代表特定实现与写作时间。应用到当前项目时,应先确认浏览器、框架或工具的主版本,再区分稳定的规范语义、可变化的内部实现和项目自身约束。

# 一、什么是 vdom

  • 用 JS 模拟 DOM 结构
  • DOM 变化的对比,放在 JS 层来做
  • 提高重绘性能

# 二、设计一个需求场景

img

用jQuery实现

img img img

遇到的问题

  • DOM 操作是“昂贵”的,js 运行效率高
  • 尽量减少 DOM 操作,而不是“推倒重来”
  • 项目越复杂,影响就越严重
  • vdom 即可解决这个问题

img

# 三、vdom 的如何应用,核心 API 是什么

什么是 vdom

img

介绍 snabbdom

img img

介绍 snabbdom - h 函数

img

介绍 snabbdom - patch 函数

img

重做jQuery的demo

  • 使用 data生成 vnode
  • 第一次渲染,将 vnode 渲染到 #container中
  • 并将 vnode 缓存下来
  • 修改 data 之后,用新 data 生成 newVnode
  • 将 vnode 和 newVnode 对比

img

核心 API

  • h(‘<标签名>’, {…属性…}, […子元素…])
  • h(‘<标签名>’, {…属性…}, ‘….’)
  • patch(container, vnode)
  • patch(vnode, newVnode)

# 四、介绍一下 diff 算法

# 4.1 vdom 为何使用 diff 算法

  • DOM 操作是“昂贵”的,因此尽量减少 DOM 操作
  • 找出本次 DOM 必须更新的节点来更新,其他的不更新
  • 这个“找出”的过程,就需要 diff 算法

img

patch(container, vnode)

img img

演示过程

img img img img

# 4.2 diff 实现过程

  • patch(container, vnode) 和 patch(vnode, newVnode)
  • createElment
  • updateChildren

面试官追问

追问 1列表页只改了一条数据,同事却准备无条件清空容器并重建全部节点;结合 patch、createElment 和 updateChildren,你会指出什么问题?
参考回答

无条件重建绕过了新旧虚拟节点的比较,也无法利用 updateChildren 只处理子节点变化。patch(container, vnode) 更接近初次挂载,patch(vnode, newVnode) 才表达更新入口;但源码未给出具体比较策略,不能进一步断言其移动复杂度。

追问 2你要把这套最小 diff 接入一个已有详情页,首屏挂载和后续状态更新分别应走什么调用路径?
参考回答

首屏应以真实容器和虚拟节点调用 patch(container, vnode),需要生成真实节点时交给 createElment。后续更新则以旧、新虚拟节点调用 patch(vnode, newVnode),子节点差异交给 updateChildren;还需自行明确旧节点引用如何保存。

追问 3组件从只有文本扩展为包含多层子节点,团队仍想只实现顶层 patch,这个约束变化会暴露什么缺口?
参考回答

顶层 patch 只能决定入口,无法独自完成多层子节点的新增、删除或更新,变化需要继续下沉到 updateChildren。节点首次出现时还依赖 createElment 映射为真实元素;详细源码缺失,因此类型变化和节点复用规则必须另行定义并测试。

追问 4线上列表更新后出现“数据已变、部分行仍是旧内容”,你会围绕这三个函数怎样定位第一个失效环节?
参考回答

先确认更新是否进入 patch(vnode, newVnode),再检查它是否把对应子节点交给 updateChildren。若差异已识别却没有真实节点可用,再追踪 createElment 的创建结果和挂载位置;仅凭界面残留不能直接判断是比较还是渲染失败。

追问 5评审时一方主张把创建、比较和子节点更新全部塞进一个 patch,另一方坚持拆分,你如何基于当前实现范围做取舍?
参考回答

拆分能让 patch 负责入口分派,createElment 负责创建,updateChildren 负责子节点更新,职责和测试边界更清楚。合并可能减少表面调用层级,却会耦合初次挂载与更新路径;不过源码信息有限,不宜声称拆分必然带来性能收益。

← 前端面试之组件化虚拟DOM(二) →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • Vue

    • 从源码解读Vue生命周期
    • 组件的本质
    • 有状态组件的设计
    • 设计 VNode
    • 辅助创建 VNode 的 h 函数
    • 自定义渲染器和异步渲染
    • 渲染器之挂载
    • 渲染器的核心 Diff 算法
    • 渲染器之patch
    • 图解 Vue 响应式原理
    • 图解 Vue 异步更新
    • 剖析 Vue 内部运行机制
    • vue响应式原理模拟
    • vue状态管理之vuex
    • 理解Vue的设计思想及实现Vue
    • diff算法深入
    • vue router vuex原理分析
    • Vue3初探响应式原理.
    • vue2源码分析
    • vue组件化实践
  • React

  • Webpack

  • Node

  • Javascript

  • 综合