vue2面试题之虚拟dom

222 阅读3分钟

面试官:聊聊你对虚拟dom的理解

1. 什么是虚拟dom?

虚拟dom本质上其实是一个js对象,用来描述页面的结构。 在vue中,每一个组件都对应一个虚拟dom树。 每个组件都有一个render函数,每个render函数都会返回一个虚拟dom树。 vnode.png vnodeOBJ.png

2. 为什么要使用虚拟dom?

说明这个问题之前,先来看看创建真实dom和js对象所耗费性能的比较 代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    console.log('创建10000个js对象和10000个真实dom对象耗时对比')
    const t = 10000;  
    console.time('JS Object')
    for (let i = 0; i < t; i++) {
        var o = {};
    }
    console.timeEnd('JS Object')
    console.time('DOM Object')
    for (let j = 0; j < t; j++) {
      var div = document.createElement('div')
    }
    console.timeEnd('DOM Object')
  </script>
</body>
</html>

控制台打印结果:

比较.png

可以看到,直接创建真实dom对象是非常损害性能的一个操作,创建数量越多,两者差距越大;而且真实dom的更新、插入等操作也非常耗费性能。因此,在vue中,每次创建组件和更新组件时都是通过render函数,生成对应的虚拟dom树,从而代替真实dom,提高渲染效率。

3. 虚拟dom如何转换为真实dom?

看到这里你可能会发现,在vue中虚拟dom最终还是要转换为真实dom,不是一样影响渲染效率吗?
没错,而且在vue中第一次渲染视图时比直接使用真实dom还要多消耗部分性能,但这是可以理解的,因为在后续再进行视图更新、插入等操作时,就能够大大提高渲染效率。

每当一个组件实例在首次渲染时,都会调用自己的render函数,从而生成一个对应的虚拟dom树,然后根据该虚拟dom树创建真实dom树,并将其挂载到页面的合适位置,此时,每一个虚拟dom都对应一个真实dom。并且在这个组件所依赖的响应式数据发生变化时,仍会调用render函数生成一个新的虚拟dom树,通过新树和旧树进行对比,找到两个dom树的最小变化,然后更新相应的虚拟dom,最后通过更新后的虚拟dom修改对应的真实dom,从而保证只对真实dom做了最小的改动。

4. 模板(template)和虚拟dom的关系

vue中有一个compile模块,主要作用就是将模板转换为render函数,然后通过调用render函数得到虚拟dom树。 编译过程:先将模板字符串转换为AST、再将AST转换为render函数。

如果使用传统的外部文件引入方式,则模板编译会发送在组件第一次加载时,称为运行时编译
如果是在vue-cli的默认配置下(runtimeCompiler:false),模板编译发生在打包时,称为模板预编译

编译也是一个非常耗费性能的过程,预编译可以有效提高运行时的性能,并且使用模板预编译,在运行的时候不需要对模板再进行编译,vue-cli打包时不会再包含vue中的compile模板,减少打包体积。

模板的存在仅仅是为了便于代码的书写,vue最终运行的时候,还是通过调用render函数得到虚拟dom树,而非模板。