vue模板的首次渲染,和重新渲染,有哪些区别?

发布时间 2024-01-12 18:03:03作者: 路泽宇

搞明白这个,能帮助我们理解开发中出现的很多问题。

 

一、我们先来回顾一下vue模板的渲染过程:

(1)执行render函数,生成虚拟DOM。

render函数是根据render、templete、el这三个选项得来的,优先级依次降低。如果有render函数,则直接使用;否则如果有templete选项,则把templete作为模板编译成render函数;否则把el的outerHTML作为模板编译成render函数。单页面组件中,使用<templete>标签作为模板编译成render函数。

render函数首次执行时,会触发模板依赖数据的getter,getter会记录哪个模板用到了自己,同时模板也会记录自己用到了哪些属性,这个过程叫依赖收集。

每次模板重新渲染时,模板中的表达式、methods函数、全局函数都会重新执行。因为render函数就是个函数,当它执行时,里面的表达式和函数都会跟着执行。但这里面有一个例外,就是计算属性,因为计算属性会缓存结果,多用计算属性是个好习惯。

(2)虚拟DOM和数据结合,生成真实的DOM。

(3)如果不是首次渲染,生成的虚拟DOM会和原来的进行比较,通过diff算法,计算出来最少的需要更新的DOM。

(4)如果是首次渲染,把DOM挂载到页面中。如果是重新渲染,则更新DOM。这一步是vue能影响的最后一步。

(5)浏览器渲染页面。这一步是浏览器渲染进程操作的,和vue无关。但vue可以在上一步里,计算出DOM的最小更新,来尽量避免回流和重绘。

 

二、首次渲染和重新渲染的不同

(1)执行时机和原理不同

vue从创建实例,到mounted执行结束,这一整个生命周期是一个同步的过程。意味着,如果你试图在created前加async,来让异步数据获取完成后再进入beforeMount钩子,是没用的。

模板的首次渲染,也是一个同步的过程。它是从beforeMount的同步代码结束开始,同步的生成虚拟DOM、真实DOM、挂载到页面这一系列操作之后,最后进入mounted钩子。

但是,从模板首次渲染完成,也就是从进入mounted钩子开始,之后的渲染逻辑就完全不一样了,被称为异步更新队列。如果说首次渲染是生命周期驱动的,那么重新渲染,就是数据的变化驱动的。

 

当模板所依赖的属性被重新赋值时,该属性的setter被触发。因为这个属性记录了哪些模板用到了自己,它就通知这些模板的Watcher进入微任务队列。在同一个事件循环中,如果一个模板所依赖的多个属性发生改变,它不会被重复放入到微任务队列。这就是我们所熟悉的,多次修改数据,vue只会更新DOM一次。

 

哪些操作会导致模板的重新渲染呢?从mounted钩子开始,所有对模板依赖数据的更改,都会导致模板的重新渲染。

 

比如,在mounted中同步修改数据,在浏览器事件中修改数据,在created中异步获取数据后修改数据等。

(2)依赖数据不同

 

要注意的是,我们通常习惯在created里异步获取数据,异步数据是不会参与到模板的首次渲染中的。

模板首次渲染采用的数据,是prop、data、计算属性的初始值。更准确的说,是render函数开始执行之前的数据。

那么,在beforeCreate、created、beforeMount里对数据进行的同步操作,是会作用于模板的首次渲染的。

 

三、所以,知道这些有什么用?

(1)在给data中的属性设初始值时,要考虑健壮性。不要忘了,这些初始值会用于模板的首次渲染。

在开发中我们经常犯这样的错误,就是没有考虑data初始值对首次渲染的兼容。当然,有个好处是代码会报错,帮助我们发现错误。

给我们的启发是什么呢?对data赋初值,能多赋,不要少赋。我以前喜欢把所有属性初始值都设为null,这个习惯很不好,容易出错,至少应该用[]、''、0这些代替null。

(2)开发中经常出现,进入页面后某个元素闪一下没了。

是因为我们在这个元素使用了v-if,首次使用初始值渲染时是有的,后面异步获取数据后重新计算就没了,所以会闪一下。

为了避免这个问题,我们应该养成一个习惯,就是元素默认是不显示的,拿到异步数据后再决定是否显示,这样就避免了闪一下的情况出现。如果在元素上再加一个进入动画,就更好了。