浅谈虚拟DOM

一、前言

前段时间面试了几位同学,他们对虚拟DOM的回答常常强调虚拟DOM改进了性能,能提到关于重排和重绘的问题。然而虚拟DOM一定比直接操作真实DOM效率更高?除了解决重排和重绘的问题,虚拟DOM又有哪些优势呢?

二、举例

举个例子:假设有个列表,需要更新10项内容

1、直接操作真实DOM的情况:

遍历这个列表,并对每一项执行DOM操作

每次操作可能触发浏览器的重排(reflow)重绘(repaint)

如果这10次是同步操作的,浏览器可能会做出一些优化,但仍然会有比较大的开销,特别是在列表很大或者DOM结构足够复杂的情况下

2、通过虚拟DOM操作真实DOM的情况:

会计算更新后的虚拟DOM和当前虚拟DOM的差异(diffing)

将这些差异批量应用在真实DOM上,通常在一个渲染周期内完成

这种方式能够减少操作真实DOM次数,并减少重排(reflow)重绘(repaint)

3、性能比较:

如果只是简单的更新10次并没有复杂的界面或频繁的状态变化,直接操作真实DOM在渲染速度上可能会更快,因为避免了虚拟DOM的构建差异计算的开销。但如果这些操作导致频繁的重绘和重排,可能会影响帧率和用户交互的响应速度

在实践中虚拟DOM的性能优势并不总是立竿见影的,所以谈到虚拟DOM时不能仅仅只谈到性能问题

另: vue3相比vue2优化了diffing的算法,在diffing上的开销变小了

三、虚拟DOM的优势

虚拟DOM是提供真实DOM操作最小代价的中间商

1、批量更新和最小化DOM操作:减少重排、重绘

2、声明式渲染:让开发者专注于数据模型和如何映射到DOM的规则上,而不是操作DOM本身

3、组件化:描述了数据和真实DOM的关系,配合组件化开花,可以使用UI复用

4、跨平台:虚拟DOM作为一个中间层,可以不局限于浏览器,还可以渲染到其他平台。

总结起来,Vue使用虚拟DOM的原因主要是为了提高性能、简化开发和实现跨平台支持。通过虚拟DOM的机制,Vue可以减少实际DOM操作次数,提高应用的性能;简化开发者对DOM操作的关注,提高开发效率;实现跨平台支持,保持一致的开发体验。

四、vue生成真实DOM的过程

现在按以下步骤简单来实现下过程

1、 解析模板 :将模板字符串解析成一个抽象的语法树(AST)

2、 响应式系统:对组件的data对象进行响应式处理

3、 生成虚拟DOM:在首次渲染或数据更新时,渲染函数被调用,返回新的虚拟DOM树

4、 更新数据:当数据发生变化时,响应式系统会注意到这些变化,并触发重新渲染

5、 虚拟DOM Diffing:将虚拟DOM和上次保存的虚拟DOM进行对比,找出差异

6、 打补丁(patch):计算差异应用到真实DOM上,有发生变化的部分会被更新

注:当前只考虑tagName和插值内容,不考虑属性和嵌套标签。

js 复制代码
const regex = /<(.+?)\>{{(.+?)}}<\/\1>/;

// 编译模板生成AST
const compileTemplate = (template, data) => {
  const match = template.match(new RegExp(regex, "gi")) || [];
  return match.map((match) => {
    const [, tag, children] = match.match(new RegExp(regex, "i"));
    return {
      tag,
      children: data[children.trim()],
    };
  });
};

let _app = null;
let _template = "";
let _data = null;
let _vDOM = null;

// 响应式数据
const reactive = (data) => {
  _data = new Proxy(data, {
    get(obj, key) {
      return Reflect.get(obj, key);
    },
    set(obj, key, value) {
      const res = Reflect.set(obj, key, value);
      update(key, value);
      return res;
    },
  });

  return _data;
};

// 渲染
const render = (app, template, data) => {
  _vDOM = compileTemplate(template, data);
  _app = app;
  _template = template;
  _data = data;

  const fragment = document.createDocumentFragment();

  _vDOM.forEach((vnode) => {
    const { tag, children } = vnode;
    const node = document.createElement(tag);
    node.innerText = children;
    fragment.appendChild(node);
  });
   
  app.innerHTML = '' 
  app.appendChild(fragment);
};

// 更新
const update = (value) => {
  const newVDOM = compileTemplate(_template, _data);

  _vDOM.forEach((vnode, index) => {
    if (vnode.children !== newVDOM[index].children) {
      patch(newVDOM[index].children, index);
    }
  });
  
   _vDOM = newVDOM;
};

// 打补丁
const patch = (value, index) => {
  const childNodes = _app.children;
  if(childNodes[index]){
      childNodes[index].innerText = value;
  }
};

const template = `
    <p>{{ title }}</p>
    <p>{{ content }}</p>
`;

const data = reactive({
  title: "虚拟DOM",
  content: "vDOM",
});

render(document.getElementById("app"), template, data);

// 更改数据
data.title = "new VDOM"
data.content = "更新后的VDOM的内容"
相关推荐
哆啦A梦15881 小时前
点击Top切换数据
前端·javascript·vue.js
小光学长2 小时前
基于Vue的2025年哈尔滨亚冬会志愿者管理系统5zqg6m36(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
麦麦大数据4 小时前
D027 v2 vue+django+neo4j 基于知识图谱红楼梦问答系统 (新增问关系功能;新增知识节点和关系管理功能,neo4j增删改查功能)
vue.js·django·问答系统·知识图谱·neo4j·图谱管理·neo4j增删改查
菜鸟una5 小时前
【瀑布流大全】分析原理及实现方式(微信小程序和网页都适用)
前端·css·vue.js·微信小程序·小程序·typescript
专注前端30年5 小时前
2025 最新 Vue2/Vue3 高频面试题(10月最新版)
前端·javascript·vue.js·面试
我的div丢了肿么办7 小时前
js函数声明和函数表达式的理解
前端·javascript·vue.js
武天7 小时前
一个项目有多个后端地址,每个后端地址的请求拦截器和响应拦截器都不一样,该怎么封装
vue.js
潜心编码7 小时前
基于vue的停车场管理系统
前端·javascript·vue.js
三小河7 小时前
React Vite 中动态批量导入路由
前端·vue.js
拉不动的猪8 小时前
为什么不建议项目里用延时器作为规定时间内的业务操作
前端·javascript·vue.js