Vue2 vs Vue3 区别
Composition API vs Options API:
Vue2:主要使用 Options API(data、methods、computed 等),代码组织按选项分块,逻辑分散。
Vue3:引入 Composition API(setup 函数或
- 性能优化:
- Vue3:使用 Proxy 实现响应式,性能更优(后文详细讲解)。
- Vue3:静态节点标记(Static Hoisting)、事件监听缓存,减少重新渲染开销。
- Vue3:打包体积更小,通过 Tree-Shaking 移除未使用的代码。
- 新特性:
- Vue3:支持 Fragments(多根节点组件)、Teleport(传送门)、Suspense(异步组件加载)。
- Vue3:更好的 TypeScript 支持,API 更友好。
- Vue3:移除了一些 Vue2 的 API,如 off、delete。
- 响应式系统:
- Vue2:基于 Object.defineProperty,有局限性。
- Vue3:基于 Proxy,支持动态添加属性、数组操作等(后文详细讲解)。
- 构建工具:
-
Vue2:依赖 Webpack 或其他打包工具。
-
Vue3:推荐 Vite,构建速度更快。
-
问:为什么 Vue3 引入 Composition API? 答:Options API 在大型项目中逻辑分散,难以维护。Composition API 允许按功能组织代码,复用性更强(如 use 函数),并支持更好的 TypeScript 类型推导。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Vue2 生命周期
-
钩子:
- beforeCreate:组件创建前,data 和 methods 未初始化。
- created:data 和 methods 已初始化,但 DOM 未挂载。
- beforeMount:DOM 渲染前。
- mounted:DOM 渲染完成。
- beforeUpdate:数据更新前。
- updated:数据更新后,DOM 重新渲染完成。
- beforeDestroy:组件销毁前。
- destroyed:组件销毁后。
-
基本钩子名称不变,但 beforeDestroy 改为 beforeUnmount,destroyed 改为 unmounted,更符合组件卸载的语义。
-
Composition API 中使用 onXxx 函数(如 onMounted、onUnmounted)替代 Options API 的钩子。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
diff
核心思想:
-
同层比较:只比较同一层级的节点,不跨层比较。
-
key 的作用:通过 key 标识节点,优化列表更新效率。
-
最小量更新:尽量复用现有 DOM 节点,只更新变化的部分。
-
新旧 VNode 比较:检查是否为相同节点(sameVnode:标签相同、key 相同)。
-
复用或更新:
-
如果是相同节点,递归比较子节点(patch)。
-
如果不同,直接替换整个节点树。
-
问:为什么需要 key?
- 答:key 帮助 Vue 识别节点,减少 DOM 操作,提高 diff 效率。没有 key,Vue 会按顺序比较,可能导致不必要的更新。
问:Vue3 的 diff 算法如何优化?
-
答:Vue3 通过静态标记跳过不变节点,优化列表比较算法(如最长递增子序列),减少 DOM 操作,提高性能。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<input v-model="message" />
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value" />
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问:为什么 React 推荐函数组件?
-
答:函数组件更简洁,易于测试和维护;Hooks 提供灵活的状态和副作用管理;避免 this 绑定问题;支持更好的代码复用。
useState
- 作用:管理函数组件的状态,提供状态变量和更新函数。
- 用法:const [state, setState] = useState(initialState);
-
state:当前状态值。
-
setState:更新状态的函数,支持直接值或函数式更新。
-
useEffect
- 作用:处理副作用(如数据获取、订阅、DOM 操作),模拟类组件的生命周期。
- 用法:useEffect(() => { ... }, [dependencies]);
- 第一个参数:副作用函数。
- 第二个参数:依赖数组,控制副作用执行时机。
- 可返回清理函数,在组件卸载或依赖变化时执行。
useEffect 如何模拟生命周期?
-
答:空依赖数组模拟 componentDidMount;依赖数组变化模拟 componentDidUpdate;返回清理函数模拟 componentWillUnmount。
diff 过程:
- 根节点比较:检查节点类型和属性是否变化。
- 子节点比较:
-
列表节点:使用 key 进行高效匹配,比较顺序、增删、更新。
-
非列表节点:递归比较子节点。
-
useEffect 是 React 的一个 Hook,用于在函数组件中处理副作用(side effects),如数据获取、订阅、DOM 操作、定时器等。
useEffect(() => {
// 副作用逻辑
return () => {
// 清理逻辑(可选)
};
}, [dependencies]);
-
第一个参数:副作用函数,包含需要执行的副作用逻辑。
-
第二个参数:依赖数组,控制副作用的执行时机。
-
返回值(可选):清理函数,在组件卸载或依赖变化前执行。
执行时机
-
组件渲染完成后执行副作用。
-
依赖数组变化时,重新执行副作用(先运行清理函数,再运行新副作用)。
-
组件卸载时,运行清理函数。
依赖数组的作用:
-
控制 useEffect 的执行频率。
-
空数组([]):仅在组件挂载和卸载时执行。
-
无依赖数组:每次渲染后都执行。
-
指定依赖(如 [count]):仅在依赖变化时执行。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
父组件 -> 子组件
-
方式:通过 props 传递数据或函数。
-
场景:父组件向子组件传递状态、配置或回调函数。
// 父组件 import React, { useState } from 'react'; import Child from './Child';
function Parent() { const [message, setMessage] = useState('Hello from Parent'); const handleChildClick = (childData) => { console.log('Received from child:', childData); };
return ; }
// 子组件 function Child({ message, onChildClick }) { return (
); }{message}
<button onClick={() => onChildClick('Hello from Child')}> Send to Parent
子组件 -> 父组件
- 方式:子组件通过调用父组件传递的回调函数(props 中的函数)通知父组件。
- 场景:子组件触发事件(如点击、输入),更新父组件状态。
- 代码示例(见上例):
- 子组件调用 onChildClick 向父组件传递数据。
问:兄弟组件通信为何不直接传递数据?
-
答:React 遵循单向数据流,兄弟组件无法直接通信,需通过父组件或 Context/State 管理工具作为中介。
-
父子组件通信:
- 父 -> 子:通过 props 传递数据或函数。
- 子 -> 父:通过回调函数通知父组件更新。
-
兄弟组件通信:
- 通过父组件共享状态。
- 使用 Context 或状态管理工具(如 Zustand)简化通信。
-
跨层级组件通信:
-
使用 Context 提供全局状态,适合简单场景。
-
使用状态管理库(如 Redux、Zustand)处理复杂状态。
-
-
状态是组件内部的数据,适合局部逻辑(如表单输入、计数器)。
-
全局状态是状态的扩展,解决跨组件或跨层级的数据共享问题。
-
全局变量是定义在全局作用域(如 window 或 global)的普通 JavaScript 变量,可被任何代码访问。无响应式:变化不会触发视图更新,需手动操作 DOM。