vue生命周期
创建阶段
-
beforeCreate- 实例初始化之前调用,数据观察(
reactive)和事件尚未初始化 - 常用语初始化非响应式属性或插件相关的配置。
- 实例初始化之前调用,数据观察(
-
created- 实例创建完成,
data和methods可用 - 模板还未挂在到DOM,
this.$el不可用 - 常用场景:发起数据请求、初始化数据。
挂载阶段
- 实例创建完成,
-
beforeMount
- 模板挂载到DOM之前调用,
$el和虚拟DOM已经创建 - 很少使用,试用查看模板渲染前的状态。
-
mounted- 模板挂载到DOM后调用,$el 已经替换成真实DOM
- 常用场景:依赖操作DOM的请求或初始化第三方插件(如图表、动画)
更新阶段
-
beforeUpdate
- 数据更改触发虚拟DOM更新,但虚拟DOM还没重新渲染(尚未应用到真实DOM)。
- 可用来调试更新的中间状态,适合调试或在更新前作额外处理。
-
updated
- 数据更新并重新渲染DOM之后调用。
- 通常不建议在此阶段操作DOM,因为可能会引起无限更新循环。
销毁阶段
-
beforeDestroy(Vue2) /onBeforeUnmount(Vue3)- 实例销毁前调用
- 可用于清楚计时器、事件监听等
-
destroyed(Vue2)/onUmmounted(Vue3)- 实例销毁后调用,事件监听器解绑,子组件销毁。
Vue 2 和 Vue 3 生命周期对照表
在Vue3中,生命周期钩子通过组合式API替代,如下表所示:
| vue2生命周期 | Vue3组合式API | Vue3选项式API | 描述 |
|---|---|---|---|
| beforeCreate | - | - | 实例初始化之前,数据和事件还未初始化 |
| created | - | - | 实例创建完成,数据和方法已初始化,但模板未挂载 |
| beforeMount | onBeforeMount() | beforeMount() | 模板挂载到 DOM 之前调用,虚拟 DOM 已生成。 |
| mounted | onMounted() | mounted() | 模板挂载到 DOM 后调用,$el 已替换为真实 DOM。 |
| beforeUpdate | onBeforeUpdate() | beforeUpdate() | 数据更新后,虚拟 DOM 重新渲染之前调用。 |
| updated | onUpdated() | updated() | 数据更新并完成 DOM 渲染后调用。 |
| beforeDestroy | onBeforeUnmount() | beforeUnmount() | 组件销毁之前调用,用于清理操作。 |
| destroyed | onUnmounted() | unmounted() | 组件销毁后调用,实例及其所有内容都已被移除。 |
activated (keep-alive) | onActivated() | activated() | 被 keep-alive 缓存的组件激活时调用。 |
deactivated (keep-alive) | onDeactivated() | deactivated() | 被 keep-alive 缓存的组件停用时调用。 |
<template>
<keep-alive>
<MyComponent v-if="showComponent" />
</keep-alive>
<button @click="toggle">Toggle Component</button>
</template>
<script>
export default {
data() {
return {
showComponent: true
};
},
methods: {
toggle() {
this.showComponent = !this.showComponent;
}
}
};
</script>
<script>
export default {
name: 'MyComponent',
activated() {
console.log('Component activated');
},
deactivated() {
console.log('Component deactivated');
}
};
</script>
一般发请求的阶段
- 如果请求与DOM无关:在created钩子中发请求,因为数据已经可用,且无需等待DOM渲染完成。
- 如果请求依赖DOM或需要操作DOM:在
mounded钩子中发请求,确保DOM已经渲染完成。
v-for和v-if
在Vue中, v-if 和 v-show 是两个常用的条件指令,他们的主要区别体现在实现机制、性能开销和使用场景。
-
v-if
-
工作原理:
v-if根据条件动态的添加或移除 DOM 节点。 -
渲染时机:只有条件为
true时,Vue 才会创建并插入DOM 节点;条件为false时,Vue 会销毁对应的 DOM 节点。 -
性能影响:由于需要频繁的创建和销毁DOM ,
v-if在条件频繁切换时性能较差 -
使用场景:
- 适用于 初次加载时无需显示 或条件变化较少 的场景。
- 例如:数据需要异步加载,初始化状态下无需显示。
<template> <div> <p v-if="isVisible">我会被动态插入或移除</p> <button @click="toggle">切换显示</button> </div> </template> <script> export default { data() { return { isVisible: false }; }, methods: { toggle() { this.isVisible = !this.isVisible; } } }; </script>
-
-
v-show
-
工作原理:
v-show切换的是原生的cssdisplay属性(display:none或正常显示) -
渲染时机:无论条件为
true还是false, 元素始终会被渲染到DOM中,只是被隐藏或显示。 -
性能影响:由于只是简单的切换CSS 属性,
v-show切换开销很低,适合频繁切换的场景。 -
使用场景:
- 适用于 频繁显示/隐藏 的场景
- 例如:模态框的显隐、选项卡的切换。
<template> <div> <p v-show="isVisible">我会一直存在 DOM 中,只是显示或隐藏</p> <button @click="toggle">切换显示</button> </div> </template> <script> export default { data() { return { isVisible: false }; }, methods: { toggle() { this.isVisible = !this.isVisible; } } }; </script>
| 特性 | v-if | v-show |
|---|---|---|
| 实现机制 | 通过添加或移除DOM元素实现 | 通过切换元素的CSS display 属性实现 |
| 初始渲染 | 在条件为 true 时,才会渲染元素 | 元素总是渲染到DOM,只是默认隐藏 |
| 切换性能 | 开销较高:每次切换都会重新销毁和创建元素 | 开销较低:仅切换 display ,无需销毁和重建DOM |
| 使用场景 | 适合条件少改变的场景 | 适合频繁切换显示/隐藏的场景 |
Vue 的内部指令
Vue 提供了一系列内置指令,用于操作DOM、绑定数据和实现响应式效果。这些指令在模板中通过v-前缀使用。
Vue常用内置指令
| 指令 | 描述 |
|---|---|
v-if | 条件渲染,元素根据条件添加或移除 |
v-else | 必须与 v-if 或 v-else-if 一起使用,用于提供“否则”的分支 |
v-else-if | 条件渲染的“否则如果”分支。 |
v-show | 条件显示,切换元素的 display 属性。 |
v-for | 列表渲染,用于遍历数组或对象 |
v-bind | 动态绑定属性或class、style等。 |
v-on | 绑定事件监听器 |
v-model | 双向绑定输入控件的值 |
v-slot | 用于分发内容到子组件的插槽 |
v-pre | 跳过元素和子元素的编译 |
v-cloak | 防止模板闪烁(配合CSS使用) |
v-once | 渲染一次,之后不会再更新 |
v-html(Vue3) | 优化性能,条件满足是缓存组件渲染结果 |
Vue2 和Vue3 内置指令的区别
-
v-model 的改进
-
Vue2:
- v-model 默认绑定到表单元素的 value 属性
- 默认事件是input
- 只能绑定单个值
//父组件 <template> <child-component v-model:title="title" v-model:content="content"> </child-component> <div> <p>Title: {{ title }}</p> <p>Content: {{ content }}</p> </div> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { title: 'Default Title', content: 'Default Content' }; } }; </script> //子组件 <template> <div> <input type="text" :value="title" @input="$emit('update:title', $event.target.value)" placeholder="Edit Title"> <textarea :value="content" @input="$emit('update:content', $event.target.value)" placeholder="Edit Content"> </textarea> </div> </template> <script> export default { props: { title: String, // 对应 v-model:title content: String // 对应 v-model:content } }; </script>运行结果:
- 父组件的 title 和 content 数据分别绑定到子组件中的输入框和文本区域
- 子组件通过 @emit 发出事件更新父组件的数据
原理分析
-
多个 v-model 的语法
- vue3 中,默认
v-model的绑定属性为modelValue,对应时间为update:modelValue - 当需要绑定多个值时,可以通过命名的方式指定不同的绑定键(如
v-model:title和v-model:comtent)
- vue3 中,默认
-
事件名称约定
- vue 自动将
v-model:name转换为prop="name"和event="update:name" - 例如:
v-model:title实际上等效于
:title="title" @update:title="title = $event" - vue 自动将
-
灵活性
- 可以根据需求自定义多个
v-model,实现复杂场景的双休绑定
- 可以根据需求自定义多个
-
-
v-bind和v-on的简化语法- vue2 和vue3均支持
:和@的简写形式v-bind可写为:attrv-on可写为@event
- vue3 支持动态参数
<button :[dynamicAttr]="value">Click</button> - vue2 和vue3均支持
-
v-for的键值对写法-
vue2和vue3 的
v-for使用方法一致,用于渲染列表。 -
新增
- vue3 中的
v-for支持解构赋值
<template> <ul> <li v-for="({ user: { name, age }, job }, index) in data" :key="index"> {{ name }} ({{ age }}岁) - 职业: {{ job }} </li> </ul> </template> <script> export default { data() { return { data: [ { user: { name: "张三", age: 25 }, job: "工程师" }, { user: { name: "李四", age: 30 }, job: "设计师" }, { user: { name: "王五", age: 35 }, job: "产品经理" }, ], }; }, }; </script> - vue3 中的
-
-
新增指令
-
v-memo(Vue3)- 用于缓存组件的渲染结果,仅在依赖发生变化时重新渲染
<template v-memo="[key]"> <!-- 缓存渲染内容 --> </template>
-
-
指令绑定的增强
-
vue3 引入了对自定义指令的全面改进,支持
bind和update钩子合并为单个mounted和updated钩子app.directive('example', { mounted(el, binding) { console.log('Mounted:', binding.value); }, updated(el, binding) { console.log('Updated:', binding.value); } });
-
vue2 和 vue3 内置指令的完整对比表
| 指令 | vue2 | vue3 | 备注 |
|---|---|---|---|
| v-for | 支持 | 支持 | 无变化 |
| v-show | 支持 | 支持 | 无变化 |
| v-for | 支持 | 支持 | vue3支持解构赋值 |
| v-model | 单一绑定 | 多个绑定 | vue3支持绑定多个值 |
| v-bind | 支持 | 支持 | vue3支持动态参数 |
| v-on | 支持 | 支持 | vue3支持动态参数 |
| v-slot | 支持 | 支持 | 无变化 |
| v-pre | 支持 | 支持 | 无变化 |
| v-cloak | 支持 | 支持 | 无变化 |
| v-once | 支持 | 支持 | 无变化 |
| v-html | 支持 | 支持 | 无变化 |
| v-text | 支持 | 支持 | 无变化 |
| v-mome | 不支持 | 支持 | vue3新增,用于缓存渲染内容 |
vue的单向数据流
在vue中,单向数据流指的是数据从父组件通过 props 向子组件传递的流向是 单向的,即数据只能从父组件流向子组件,而不能从子组件反向修改父组件的状态。这种设计主要是为了保持数据的可预测性和组件之间的隔离性。
实现单向数据流的原因
- 提升数据的可维护性
- 单向数据流使得数据的来源清晰,减少了调试和维护的复杂性
- 防止数据不一致
- 如果允许子组件直接修改父组件的数据,会导致数据来源混乱,从而增加出错的可能性
- 保持组件的独立性
- 单向数据流使得组件更容易复用,因为他们依赖于外部的输入而不得直接修改外部的状态
应用场景
- 父组件传递数据
- 父组件向子组件传递数据(通过
props) - 子组件只能使用这些数据,不能直接修改
- 父组件向子组件传递数据(通过
- 子组件通知父组件
- 如果子组件需要让父组件知道某些事件(如用户输入变化),应通过 $emit 通知父组件,父组件再更新数据
computed 和 watch 的区别和运用的场景
- 定义和用途
| 特性 | computed | watch |
|---|---|---|
| 定义 | 用于声明式地定义基于响应式数据的派生值 | 用户监听响应式数据的变化并执行副作用(如异步操作) |
| 依赖的方式 | 自动追踪依赖的响应式数据,当依赖变化时,计算值会更新 | 手动指定监听的响应式数据,当数据变化时触发回调 |
| 返回值 | 必须有返回值,作为计算数据的值 | 无需返回值,执行自定义逻辑(如API调用) |
| 缓存 | 会缓存计算结果,只有依赖发生变化时才重新计算 | 不缓存,监听回调每次数据变化都会执行 |
应用场景
- computed 的适用场景
-
声明式计算属性
- 当需要通过一个或多个数据计算出一个派生值时,优先使用 computed
- 例:根据用户输入的名字动态生成全名
-
优化性能(带缓存)
- 如果依赖的数据频繁变化,但实际计算结果未变化,computed 的缓存机制可以减少不必要的重复计算
<template> <p>{{ fullName }}</p> </template> <script> export default { data() { return { firstName: 'John', lastName: 'Doe' }; }, computed: { fullName() { console.log('计算 fullName'); return `${this.firstName} ${this.lastName}`; } } }; </script>
watch的适用场景
-
执行副作用
-
当某些数据变化需要触发副作用(如API请求、定时器、DOM操作)时,使用
watch -
例:监听搜索框输入内容,动态获取搜索结果
<template> <input v-model="searchQuery" placeholder="搜索..." /> </template> <script> export default { data() { return { searchQuery: '' }; }, watch: { searchQuery(newQuery) { this.fetchResults(newQuery); } }, methods: { fetchResults(query) { console.log('Fetching results for:', query); } } }; </script> -
-
深度监听
- 当需要监听对象或数组的深层次变化时,
watch提供了deep选项
watch: { userProfile: { handler(newProfile) { console.log('User profile changed:', newProfile); }, deep: true } } - 当需要监听对象或数组的深层次变化时,
-
异步操作
- 如果数据变化需要执行异步任务(如延迟调用或API请求),
watch是最佳选择。
- 如果数据变化需要执行异步任务(如延迟调用或API请求),
-
选中指南
需求场景 推荐使用 需要基于响应式数据计算派生值,并且需要缓存结果 computed数据变化需要触发副作用(如调用函数、API请求) watch复杂的逻辑,需要监听对象或数组的深度变化 watch(deep)数据变化频繁且加上结果不会频繁变化 computed
vue 响应式数据的原理
-
vue2响应式原理
-
实现方式:
- vue2 使用
object.defineProperty将对象的每个属性转为getter和setter - 当读取属性时(
getter),vue 收集依赖(如模板中的绑定) - 当修改属性时(
setter),vue通知依赖重新渲染
- vue2 使用
-
优点
- 基础浏览器兼容性好,可以支持旧版本浏览器(如IE11)
-
缺点
-
无法检测新增属性或删除属性
- 必须通过
Vue.set()或this.$set()动态添加响应式属性
this.$set(this.obj, 'newProp', value); - 必须通过
-
-
数组变化的局限性
- vue2无法监听数组的直接赋值,例如修改数组的索引
- 必须使用 Vue 提供的方法(如
splice)或this.$set()
-
性能问题
- 对每个对象的每个属性都需要单独设置 getter 和setter ,处理深层嵌套对象的成本较高
-
-
vue3 响应式原理
核心机制:Proxy
-
实现方式
- vue3 使用 JavaScript 的 proxy 对整个对象进行代理,而不是逐个属性设置
- proxy 能监听对象的任意操作(读取、写入、删除、动态添加属性等)
- Vue 提供的 reactive 和 ref 是基于 proxy 实现的。
-
优点
-
支持动态属性
- 不再需要
Vue.set(),可以直接监听新增或删除的属性
- 不再需要
-
数组索引和长度变化的响应式
Proxy可以正确地追踪数组的索引修改和长度变化
const arr = reactive([1, 2, 3]); arr[2] = 4; // 自动触发更新 -
性能优化
- 对整个对象进行代理,不需要递归地为每个属性添加
getter和setter - 只有实际访问的属性才会触发代理逻辑
- 对整个对象进行代理,不需要递归地为每个属性添加
-
更现代的设计
- 使用
proxy提高了灵活性,为未来的JavaScript 标准和特性(如Relect)打下基础
- 使用
-
-
-
主要区别
特性 Vue2 Vue3 实现方式 Object.definePropertyProxy动态属性监听 需要使用 Vue.set()或$set支持直接监听新增和删除的属性 数组变化监听 不支持索引和长度变化,需要用特定方法 支持直接监听索引和长度的变化 性能 深层对象需递归处理,开销较大 整体代理,无需递归处理 API易用性 使用传统的 data配置项退阿金使用 reactive和ref兼容性 支持旧版浏览器(如IE11) 不支持IE11,需要现代浏览器支持
Vue的父子组件生命周期钩子函数执行顺序
在vue 中,父子组件的生命周期钩子函数执行顺序是按照组件的创建和销毁顺序来安排的。
-
vue2 的生命周期钩子函数执行顺序
组件挂载是(创建阶段)
- 父组件的
beforeCreate - 父组件的
created - 父组件的
beforeMount - 子组件的
created - 子组件的
beforeMount - 子组件的
mounted - 父组件的
mounted
组件更新时
- 父组件的
beforeUpdate - 子组件的
beforeUpdate - 子组件的
updated - 父组件的
updated
组件销毁时
- 父组件的
beforeDestory - 子组件的
beforeDestory - 子组件的
destoryed - 父组件的
destoryed
- 父组件的
-
vue3的生命周期钩子函数执行顺序
vue3中的生命周期钩子与 Vue2 基本一致,但钩子函数的名称发生了变化,同时引入了
setup函数。函数挂载时(创建阶段)
- 父组件的
setup - 父组件的
onBeforeMount - 子组件的
setup - 子组件的
onBeforeMount - 子组件的
onMounted - 父组件的
onMounted
组件更新时
- 父组件的 onBeforeUpdate
- 子组件的 onBeforeUpdate
- 子组件的 onUpdated
- 父组件的 onUpdated
组件销毁时
- 父组件的 onBeforeUnmount
- 子组件的 onBeforeUnmount
- 子组件的 onUnmounted
- 父组件的 onUnmounted
- 父组件的
-
执行顺序总结
| 阶段 | 父组件钩子 | 子组件钩子 |
|---|---|---|
| 挂载 | beforeCreate→created→beforeMount→ 等待子组件完成 →mounted | beforeCreate→created→beforeMount→mounted |
| 更新 | beforeUpdate→ 等待子组件完成 →updated | beforeUpdate→updated |
| 销毁 | beforeDestroy→ 等待子组件完成 →destroyed | beforeDestroy→destroyed |
虚拟DOM是什么,及其优缺点
虚拟DOM是一种编程概念,它通过在内存中创建一个轻量级的 JavaScript 对象(即虚拟DOM数),用来描述真实 DOM 的结构。
它是实际DOM 的抽象表示,可以被看做是一个用 JavaScript 对 DOM 数的模拟。
在Vue 和 React 等框架中, 虚拟DOM 的主要作用是提高性能,通过高效的算法将虚拟DOM与真实DOM进行对比(diff),并只更新发生变化的部分,而不是直接操作整个真实DOM。
虚拟DOM 的工作流程
-
创建虚拟DOM
-
使用JavaScript对象描述DOM结构
-
例如
const vnode = { tag: 'div', props: { id: 'app' }, children: [ { tag: 'h1', children: 'Hello, World!' }, { tag: 'p', children: 'This is a virtual DOM example.' } ] };
-
-
渲染虚拟DOM
- 框架根据虚拟DOM构建真实DOM并插入页面
-
更新虚拟DOM
- 当状态发生变化时,重新生成新的虚拟DOM
-
Diff 算法
- 对比新旧两个虚拟DOM树,找出变化的部分
-
更新真实DOM
- 将变化的部分应用到真实DOM上。
虚拟DOM 的优缺点
- 性能优化
- 最小化DOM操作
- 真实DOM操作时性能瓶颈,虚拟DOM通过计算出最小的差异集合,减少不必要的DOM操作
- 批量更新
- 虚拟DOM在内存中完成对比后,可以一次性应用所有更改
- 最小化DOM操作
- 跨平台能力
- 虚拟DOM是对真实DOM 的抽象,不仅可以用在浏览器中,也可以用在其他环境(如果服务器端渲染、移动端Natvie渲染)
- 简化复杂的状态更新
- 使用虚拟DOM后,开发者只需专注于描述组件的状态,框架会自动处理DOM的更新逻辑。
- 增强可维护性
- 使用声明式的方法描述UI,可以使代码更简洁、易读,降低开发和维护成本。
虚拟DOM的缺点
- 初次渲染较慢
- 初次将虚拟DOM转换为真实DOM 需要一定的开销
- 如果页面非常复杂,初次渲染的性能可能比直接操作DOM稍差
- 对小型应用的意义有限
- 对于简单或静态页面,直接操作DOM更高效,而虚拟DOM 的diff算法会正价额外的计算开销
- 难以掌握细颗粒度优化
- 虚拟DOM封装了DOM操作逻辑,开发者失去了对底层优化的完全控制。例如:手动对特地DOM节点进行优化可能更高效
虚拟DOM示例
原生DOM操作
const element = document.createElement('div');
element.id = 'app';
const title = document.createElement('h1');
title.textContent = 'Hello, World!';
element.appendChild(title);
document.body.appendChild(element);
虚拟 DOM
const vnode = {
tag: 'div',
props: { id: 'app' },
children: [
{ tag: 'h1', children: 'Hello, World!' }
]
};
// 将 vnode 渲染为真实 DOM 的伪代码
function render(vnode) {
const el = document.createElement(vnode.tag);
if (vnode.props) {
Object.keys(vnode.props).forEach(key => {
el.setAttribute(key, vnode.props[key]);
});
}
if (vnode.children) {
vnode.children.forEach(child => {
el.appendChild(render(child));
});
}
return el;
}
document.body.appendChild(render(vnode));
虚拟DOM 的应用场景
- 动态视图更新
- 需要频繁更新UI的应用(如表单、图表、游戏等)
- 跨平台开发
- RN、Weex等使用虚拟DOM将web技术应用于移动开发
- 复杂的状态管理
- 使用虚拟DOM可以将复杂的状态更新逻辑交给框架处理,降低开发成本。
总结
| 特性 | 虚拟DOM |
|---|---|
| 优点 | 提高性能、减少DOM操作、跨平台支持、简化状态更新 |
| 缺点 | 初次渲染慢、小型应用意义不大、对底层优化控制力不足 |
| 适用场景 | 动态页面更新、多平台开发、复杂状态管理 |
| 不适用场景 | 静态页面、小型应用、需要极致性能优化的场景 |
vue2、vue3、React的diff算法区别
vue合React都使用了虚拟DOM 和 Diff 算法来优化界面更新,但它们在具体实现和优化策略上存在一些差异。
-
Vue2的Diff算法
核心特性
- 模板驱动更新
- Vue2使用模板生成虚拟DOM,通过 object.defineProperty 实现响应式
- 更新过程是基于依赖跟踪( dependency tracking)的,只有相关的组件或节点会触发更新。
- 双层Diff过程
- vue2会优先比较组件级别的变化,再对子节点进行递归的逐层对比
- Diff算法优化
- 采用静态节点标记
- 编译时标记哪些节点是静态的(即内容不会变化),跳过这些节点的更新对比。
- 递归对比虚拟DOM树,深度优先
- 采用静态节点标记
局限性
- 无法充分利用现代浏览器特性
- vue2 的Diff 算法基于 Object.defineProperty,无法全局监控对象的属性变化
- 性能问题
- 对于大型复杂的DOM树,递归对比可能会带了一定性能开销。
- 模板驱动更新
-
Vue3的Diff算法
核心特性
-
基于Proxy 的响应式
- Vue3使用 Proxy 替代 Object.defineProperty ,能够精装追踪数据变化
- 只对发生变化的部分重新生成虚拟DOM,不需要全局更新
-
编译时优化 - Block Tree 概念:
- Vue 3 在编译时生成块(Block Tree),将动态内容和静态内容分离。
- 只对动态部分进行 Diff,从而减少无关部分的对比。 - 动态节点标记:
- Vue 3 会在编译时标记动态节点的范围,Diff 时直接定位到动态节点。
-
子树优先更新
- Vue 3 优化了子组件的更新逻辑,避免了不必要的深层递归。
-
Fragment 支持
- Vue 3 支持多根节点的组件(Fragment),减少多余的 DOM 包裹元素。
-
React 的Diff算法
核心特性
- 单向数据流
- React使用状态驱动更新,通过
setState或 Hooks 触发组件重新渲染。 - 每次更新都会生成完整的新虚拟DOM
- React使用状态驱动更新,通过
- 树形对比算法
- React的Diff 算法主要优化树形结构,通过以下规则减少计算量
- 同层比较:只比较同一层的节点,不会夸层对比。
- Key 优化:
- React使用 key 标识节点的唯一性,从而减少节点复用错误
- 如果 key 一致,直接复用节点;否则视为新增或删除。
- 对于复杂树结构,React 会根据节点类型进行复用、替换、或重建操作。
- React的Diff 算法主要优化树形结构,通过以下规则减少计算量
- Fiber 架构
- Fiber的出现
- React 在16版本引入了Fiber架构,支持任务分片和可中断更新
- 在更新虚拟 DOM 时,React 会根据优先级分配任务,从而实现动画、输入等高优先级任务的平滑处理。
- 时间切片
- Fiber 提供了一个异步的 Diff 过程,通过时间切片(Time Slicing)将更新分为多帧完成。
- Fiber的出现
优点:
- 支持中断的任务调度,适合复杂的用户交互场景。
- 优化了深度嵌套组件的更新过程。
缺点
- 每次更新都会生成完整的虚拟 DOM,可能带来一定的性能开销。
- Diff 过程复杂度较高
-
-
Diff 算法的对比
| 特性 | Vue 2 | Vue 3 | React |
|---|---|---|---|
| 响应式实现 | Object.defineProperty | Proxy | 状态驱动(setState 或 Hooks) |
| Diff 策略 | 深度优先,逐层递归 | 静态内容标记 + 动态内容快速定位 | 同层对比 + Key 优 |
| 性能优化 | 静态节点标记 | Block Tree,子树优先更新 | Fiber 架构,时间切片 |
| 动态节点标记 | 部分支持 | 编译时完全支持 | 依赖 key 提高效率 |
| 调度机制 | 无调度机制 | 单一任务模型 | 多任务优先级调度(Fiber) |
| 跨层对比 | 支持跨层对比 | 动态节点优先对比 | 不支持跨层对比 |
| 场景适用 | 静态内容较多的场景 | 动态与静态内容混合的场景 | 复杂的交互与动画场景 |