八股(一)
一、虚拟dom
1、什么是虚拟dom
以往,我们改变更新页面,只能通过首先查找dom对象,再进行修改dom的方式来达到目的。 但这种方式相当消耗计算资源, 因为每次查询 dom ,都需要遍历整颗 dom 树。现在,我们用对象的方式来描述真实的 dom,并且通过对象与真实dom建立了一一对应的关系,那么每次 dom 的更改,通过找到相应对象,也就找到了相应的dom节点,再对其进行更新。这样的话,就能节省性能,因为js对象的查询,比对整个dom 树的查询,所消耗的性能要少。
2、虚拟dom的实现原理
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要 的 dom 操作,从而提高性能。具体如下:
①初始化虚拟 DOM 树:在应用程序加载时,会根据当前页面的状态和组件结构构建初始的虚拟 DOM 树。这个虚拟 DOM 树是一个 JavaScript 对象树,它对应着实际 DOM 结构的轻量级表示。
②状态变更触发更新:当应用程序状态发生变化时,比如用户交互导致数据变化,框架会触发重新渲染过程。在这个过程中,新的虚拟 DOM 树会被构建出来,代表了应用状态更新后的页面结构。
③虚拟 DOM 比较:新的虚拟 DOM 树和旧的虚拟 DOM 树会被进行比较,找出两者之间的差异。这个过程通常使用一种叫做"diffing algorithm"的算法来进行,它会尽可能高效地找出两个树之间的最小变更集合。
④生成更新补丁:根据比较的结果,生成描述如何更新实际 DOM 的操作序列,通常称为“补丁”(patch)或“更新”(update)。
⑤应用更新到实际 DOM:最后,将生成的更新补丁应用到实际的 DOM 上,更新页面内容。这个过程可能会使用一些优化策略,比如批量更新,以提高性能。
二、响应式数据
响应式数据是被监控的函数(vue2是watcher,vue3是effect)与数据的关联,不是数据与数据的关联!!
常见被监控的函数:render、computed、watchEffect、watch
来看一些例子,判断count的变化是否会导致doubled的变化
例一:
<template>
<div>得到传入的数据{{ count }}</div>
<div>doubled:{{ doubleCount }}</div>
</template>
<script setup>
const props = defineProps[{ count:Number, }];
const doubleCount = ref(props.count * 2);
</script>
当props里的count变化后,doubleCount是不会变化的,在template中的代码,是render函数执行的,当数据变化后,render函数会重新执行。
响应式数据是函数与数据的关联,而这里的props.count * 2是数据,doubleCount也是数据,这是典型的数据与数据的关联。
但是你如果手动改了doubleCount的值过后,template里的doubleCount是会变的,因为这是doubleCount和render函数产生的关联
或者你可以认为,props里的数据是只读的
例二
const doubleCount = ref(0);
watchEffect(()=>{
doubleCount.value = props.count * 2;
})
显然props.count变了doubleCount.value也会变
例三
function useDouble(count) {
const doubleCount = ref(count * 2);
watchEffect(()=>{
doubleCount.value = count * 2;
})
return doubleCount;
}
这里的watchEffect里的内容在初始时运行了一次,后面无论函数被调用了多少次,就再也不会被了执行了,因为watchEffect监视的数据必须是响应式数据。
但是如果你把count换成了props.count的话,watchEffect才真正生效了(以为props是响应式的)
例四:
const doubleCount = computed(()=> props.count * 2);
计算属性是函数,函数里面用到了某一个数据,那么将来这个数据变化后,这个函数会重新执行,重新的到新的值,所以这里当props.count变化后,doubleCount的值也会变化。