阅读视图

发现新文章,点击刷新页面。

React&Vue知识点汇总

Vue

1. 声明式渲染

  • 模板语法{{ }} 文本插值、v-html 输出 HTML

  • 指令

    • 内置指令:v-bindv-onv-modelv-if/v-else-if/v-elsev-showv-forv-prev-cloakv-once
    • 自定义指令:全局 Vue.directive(Vue 2)/ app.directive(Vue 3),局部 directives 选项

2. 响应式系统

  • Vue 2:基于 Object.defineProperty,递归遍历对象属性,无法检测

    • 对象属性的添加/删除(需用 Vue.set / this.$set
    • 数组索引修改(需用变异方法或 Vue.set
    • 数组长度变化(需用 splice 等)
  • Vue 3:基于 Proxy,可监听动态添加属性、数组索引修改,性能更优,支持 MapSet 等原生集合的响应式

  • 响应式原理

    • 数据劫持:Vue 对 data 中的属性进行响应式处理(Vue 2 用 Object.defineProperty,Vue 3 用 Proxy)。
    • 依赖收集:当组件渲染或计算属性等执行时,会访问响应式数据,此时将当前正在执行的 Watcher(观察者)添加到该数据的依赖列表(Dep)中。
    • 派发更新:当数据被修改时,Dep 通知所有依赖它的 Watcher 执行更新。
    • 异步更新队列Watcher 更新时并不立即执行 DOM 操作,而是将自身推入一个队列,在下一个事件循环(microtask)中统一执行,并利用 nextTick 提供更新后的回调。

3. vue3 API

API 说明
ref() 声明任意类型的响应式数据,需通过 .value 访问。
reactive() 声明对象/数组类型的响应式数据,可直接访问属性。
computed() 定义计算属性,基于响应式依赖缓存结果。
watch() 监听特定数据源,在数据变化时执行副作用。
watchEffect() 自动追踪其内部使用的响应式数据,并在数据变化时立即重新运行。
onMounted()onUpdated()onUnmounted() 等 组件生命周期不同阶段执行的钩子函数,用法与选项式 API 对应。
provide()inject() 用于跨层级组件通信,祖先组件提供数据,后代组件注入使用。
toRefs()toRef()isRef()unref() 等 用于处理 ref / reactive 对象的辅助函数,帮助进行响应式转换和判断。
defineProps()defineEmits() 在 <script setup> 中声明组件的 props 和 emits,享受完整类型推导。
defineExpose() 在 <script setup> 中声明当前组件暴露给父组件的属性或方法。
defineOptions() 在 <script setup> 中声明组件名 (name) 或 inheritAttrs 等选项。

内置组件

组件 说明
<component> 用于动态渲染不同组件的“元组件”,通过 is 属性决定
<transition> 为单个元素或组件添加进入/离开过渡动画
<transition-group> 为列表中的多个元素或组件添加过渡动画
<keep-alive> 缓存动态组件,避免重复渲染和状态丢失
<teleport> 将组件模板的一部分渲染到 DOM 树中的指定位置
<suspense> 管理异步组件或依赖异步数据的组件,在等待时显示后备内容
<slot> 作为组件模板中的插槽出口,接收父组件分发的内容

内置指令

指令 说明
v-model 在表单元素或组件上创建双向绑定。
v-ifv-else-ifv-else 条件渲染,为 false 时不渲染元素。
v-show 条件渲染,通过 CSS 的 display 属性切换。
v-for 基于源数据多次渲染元素或模板块。
v-on (@) 绑定事件监听器。
v-bind (:) 动态地绑定一个或多个属性。
v-slot (#) 用于声明具名插槽或作用域插槽。

4. 生命周期钩子

阶段 Vue 2 钩子 Vue 3 钩子(Options API) Vue 3 钩子(Composition API)
初始化 beforeCreate, created setup() 代替 beforeCreate/created
挂载 beforeMount, mounted onBeforeMount, onMounted
更新 beforeUpdate, updated onBeforeUpdate, onUpdated
卸载 beforeDestroy, destroyed beforeUnmount, unmounted onBeforeUnmount, onUnmounted
错误捕获 errorCaptured onErrorCaptured
其他 activated, deactivated(keep-alive) onActivated, onDeactivated
调试 renderTracked, renderTriggered(开发) onRenderTracked, onRenderTriggered

5. 插槽

  • 默认插槽、具名插槽、作用域插槽(slot-scope in Vue 2,v-slot in Vue 2.6+ & Vue 3)
  • Vue 3 中 v-slot 统一为指令语法,slot 和 slot-scope 被废弃

6. 混入(Mixin)

  • 全局混入、局部混入
  • 合并策略:数据递归合并,同名钩子合并为数组,方法/组件/指令等直接覆盖
  • 缺点:命名冲突、隐式依赖、代码不直观 → 推荐组合式 API 替代

7. 自定义指令

  • 钩子函数:

    • Vue 2:bindinsertedupdatecomponentUpdatedunbind
    • Vue 3:beforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted
  • 参数:elbindingvnodeprevVnode

8. 过滤器(Filters)

  • Vue 2 支持模板内过滤器({{ msg | filter }})及全局/局部定义
  • Vue 3 中移除,推荐用计算属性或方法替代

9. 动画与过渡

  • <transition> 单元素过渡
  • <transition-group> 多元素/列表过渡
  • 类名约定:v-enter-from/v-enter-to 等(Vue 3 命名变化)
  • JavaScript 钩子:@before-enter@enter@after-enter 等

10、组件通信方式(详细对比)

方式 Vue 2 Vue 3
props / $emit 支持 支持,emit 需在 setup 中声明
v-model 单个,value + input 可多个,modelValue + update:modelValue,支持自定义修饰符
refs/refs / parent / $children $children 存在 移除 $children,推荐 ref + $parent 或组合式 API
provide / inject 默认非响应式,可传递响应式对象 支持响应式传递,可提供 ref/reactive
event bus new Vue() 作为总线 推荐用 mitt 等第三方库
Vuex Vuex 3 Vuex 4 / Pinia
slot 作用域 slot-scope v-slot 统一语法
组合式 API 可直接使用 ref 传递,逻辑复用更灵活

11、Vue Router 对比(3.x vs 4.x)

特性 Vue Router 3(Vue 2) Vue Router 4(Vue 3)
创建方式 new VueRouter(...) createRouter({ ... })
模式 mode: 'history' / 'hash' history: createWebHistory() / createWebHashHistory()
路由守卫 beforeEach / beforeResolve / afterEach 同,但支持组合式 API 中的 onBeforeRouteUpdate 等
路由元信息 meta
动态路由 addRoutes addRoute,且支持动态删除
导航故障 NavigationFailureType 更完善的类型
组合式 API 不支持 useRouteruseRoute

12、状态管理:Vuex vs Pinia

特性 Vuex 3/4 Pinia
设计理念 基于 Flux,强调 mutations / actions / getters 更简洁,直接修改 state,支持组合式 API
类型推断 需要额外处理 原生 TypeScript 支持
模块化 通过 modules 通过多个 store 自然分割
异步处理 actions 中 actions 中,可直接使用 async/await
代码量 较多模板代码 更少,更直观
热更新 有限支持 支持 store 热更新
Vue 3 推荐 可用,但官方转向 Pinia 官方推荐,轻量且强大

13、构建工具:Vue CLI vs Vite

特性 Vue CLI(基于 webpack) Vite
启动速度 慢(打包后启动) 极快(按需编译,原生 ES modules)
生产构建 基于 webpack,配置灵活但复杂 基于 Rollup,预配置更简单
插件生态 丰富的 webpack 插件 插件系统兼容 Rollup 插件,且提供 Vite 插件
配置方式 vue.config.js vite.config.js
开发环境 HMR 较慢(大规模项目) HMR 快速,保留状态
环境变量 VUE_APP_* VITE_*

14、响应式原理(Vue 2 vs Vue 3)

Vue 2 响应式

  • 遍历 data 对象,对每个属性递归调用 defineReactive,为每个属性创建 Dep(依赖收集器)。

  • 每个属性对应一个 Watcher(观察者),在渲染时收集依赖。

  • 缺点:

    • 无法检测对象属性的新增/删除(需用 Vue.set / this.$set)。
    • 无法直接通过索引修改数组(arr[0] = xx 不触发更新,需用变异方法如 pushsplice)。
    • 初始化时需要递归遍历,性能略差。

Vue 3 响应式

  • 基于 Proxy 代理整个对象,可拦截 getsetdeleteProperty 等操作。

  • 优点:

    • 动态添加/删除属性自动响应。
    • 数组索引修改和 length 变化自动响应。
    • 支持 MapSet 等原生集合。
    • 惰性响应式:只有访问到属性时才会递归代理,性能更好。

总结

Vue 2 通过 Object.defineProperty 劫持对象属性的 getter/setter 来实现响应式,但存在局限性,比如无法监听动态添加的属性,需要通过 Vue.set 处理。Vue 3 改用 Proxy,可以代理整个对象,支持多种操作拦截,解决了上述问题,同时性能更优。响应式核心是依赖收集和派发更新,在 getter 中收集依赖,在 setter 中触发更新,并通过异步队列实现批量更新

15、虚拟 DOM 与 diff 算法

diff 策略

  • 同层比较:只比较同一层节点,不跨层。
  • 双端比较(Vue 2):新旧 VNode 的 children 数组通过头尾交叉比较,找到可复用的节点。
  • 静态提升(Vue 3):编译时标记静态节点,更新时跳过它们。
  • Patch flag(Vue 3):标记动态节点,只更新变化的部分

总结

虚拟 DOM 是一种用 JS 对象模拟真实 DOM 的结构,通过 diff 算法对比新旧 VNode,找出差异并批量更新真实 DOM,减少了直接操作 DOM 的性能开销。Vue 2 的 diff 采用双端比较,Vue 3 则引入了静态提升和 patch flags,进一步优化了更新效率。key 是 diff 过程中识别节点的重要依据,使用稳定的 key 可以保证节点复用,避免渲染错误。

16、 生命周期钩子(执行顺序、使用场景)

父子组件生命周期顺序

  • 创建:父 beforeCreate → 父 created → 子 beforeCreate → 子 created → 子 beforeMount → 子 mounted → 父 mounted
  • 更新:父 beforeUpdate → 子 beforeUpdate → 子 updated → 父 updated
  • 销毁:父 beforeDestroy → 子 beforeDestroy → 子 destroyed → 父 destroyed

常用钩子作用

  • beforeCreate:实例初始化后,数据观测和事件配置之前。无法访问 data、props。
  • created:可访问数据,但 DOM 未挂载,适合异步请求、初始化数据。
  • mounted:DOM 已挂载,可操作 DOM,适合第三方库初始化。
  • beforeDestroy:销毁前,适合清除定时器、取消订阅。
  • activated / deactivatedkeep-alive 组件激活/停用。

17、$nextTick 原理及使用场景

原理

Vue 的异步更新队列。数据变化后,Vue 将开启一个队列,把同一个事件循环内的所有数据变化缓存起来,然后在下一个事件循环(microtask)统一执行 DOM 更新。$nextTick 的回调会在 DOM 更新完成后执行。

使用场景

  • 在数据变化后,需要获取更新后的 DOM 结构。
  • 需要在 mounted 钩子中确保子组件渲染完成。
  • 异步操作后需要等待 DOM 同步。

面试回答

$nextTick 利用 Promise 或 MutationObserver 等微任务机制,将回调延迟到下次 DOM 更新循环之后执行。我们常用来解决数据变化后立即操作 DOM 的问题,比如滚动到底部、获取元素宽高等。Vue 3 中同样有 nextTick 函数,可在组合式 API 中使用。

18、 keep-alive 实现原理及生命周期

作用

缓存不活动的组件实例,避免反复渲染。

原理

内部维护一个缓存对象(键是组件的 key 或自身),当组件切换时,将被移除的组件实例保留在缓存中,而不是销毁。再次激活时从缓存取出复用,触发 activated 和 deactivated 钩子。

相关属性

  • include / exclude:正则或数组,指定要缓存/不缓存的组件。
  • max:最大缓存数,超出时根据 LRU 策略删除。

生命周期

  • 首次进入:created → mounted → activated
  • 缓存后再次进入:activated(不会重新执行 created / mounted
  • 离开时:deactivated

面试回答

keep-alive 是一个抽象组件,它通过缓存 VNode 来保留组件状态,避免重复渲染。内部使用 LRU 算法管理缓存,可以通过 include 和 max 控制缓存策略。被缓存的组件会多出 activated 和 deactivated 钩子,用于在激活/停用时执行逻辑。

19、 组合式 API 与选项式 API 的优缺点

选项式 API(Vue 2 主流):

  • 优点:结构清晰(data、methods、computed 分块),适合初学者。
  • 缺点:逻辑分散,复杂组件难以维护;复用逻辑需借助 mixin,存在缺陷。

组合式 API(Vue 3 引入):

  • 优点:

    • 逻辑集中,按功能组织代码,可读性和可维护性高。
    • 逻辑复用简单,通过组合函数(hooks)实现,无命名冲突。
    • 更好的 TypeScript 类型推断。
  • 缺点:学习曲线稍陡,对初学者不够直观。

面试回答

“选项式 API 将组件选项按类型划分,代码直观但逻辑分散。组合式 API 将相关逻辑聚合在 setup 中,通过组合函数实现复用,尤其适合大型复杂组件。Vue 3 并未废弃选项式 API,两者可混用,但组合式 API 提供了更好的逻辑复用能力和类型支持,是未来的推荐写法。”

20、SSR 原理及优缺点

原理

  • 服务端运行 Vue 应用,生成 HTML 字符串直接返回给浏览器,客户端再“激活”(hydrate)为可交互应用。
  • 同构:同一份代码在服务端和客户端均可运行。

优点

  • 更好的 SEO:搜索引擎能抓取完整 HTML。
  • 更快的首屏加载:用户无需等待 JS 下载即可看到内容。

缺点

  • 开发复杂度高:需考虑 Node.js 环境兼容性。
  • 服务器负载大:每个请求都重新渲染,需注意缓存策略。
  • 部分 API 在服务端不可用(如 window、document),需条件判断。

面试回答

“SSR 在服务端将 Vue 组件渲染成 HTML,发送给客户端,然后客户端进行激活。它主要解决 SPA 的 SEO 问题和首屏加载速度。但实现成本较高,需要处理服务端和客户端环境的差异,并关注服务器性能。通常我们会借助 Nuxt.js(Vue 2)或 Nuxt 3(Vue 3)这样的框架来简化 SSR 开发。”

21、Vue 3 新特性及与 Vue 2 的区别

核心新特性

  • 组合式 API:更好的逻辑复用和代码组织。
  • Proxy 响应式:解决 Vue 2 的响应式局限,性能更优。
  • Teleport:将组件内容渲染到任意 DOM 位置。
  • Fragment:组件支持多个根节点。
  • Suspense:用于异步组件加载时的占位。
  • 全局 API 改造createApp 替代 new Vue,全局配置隔离。
  • 更好的 TypeScript 支持:源码用 TS 重写,类型更完善。
  • 性能提升:编译优化(静态提升、patch flag),打包体积更小。
  • Vite 官方构建工具:开发体验极大提升。

破坏性变更

  • 移除过滤器、$children$on/$once/$offv-on.native 等。
  • v-model 默认 prop 和事件变化,支持多个绑定。
  • v-if 与 v-for 优先级改变。

面试回答

“Vue 3 相比 Vue 2 在响应式系统、组合式 API、性能、TypeScript 支持等方面有重大改进。它引入了 Teleport、Suspense 等内置组件,并用 createApp 创建应用,避免全局污染。虽然有一些破坏性变更,但官方提供了迁移构建和工具帮助升级。Vue 3 也带来了更现代的构建工具 Vite,提升了开发体验。”

22、常见API使用方式 defineEmits、defineExpose、defineOptions、defineProps、

1. defineProps – 接收父组件传递的数据

作用:声明组件的 props(属性),代替传统的 props 选项。

基本用法

<script setup>
// 运行时声明(自动推断类型)
const props = defineProps(['title', 'count'])

// 带类型的声明(TypeScript)
const props = defineProps<{
  title: string
  count?: number   // 可选
}>()
</script>

父组件使用

<MyComponent title="Hello" :count="10" />

2. defineEmits – 向父组件发送事件

作用:声明组件可以触发的事件。

<script setup>
// 简单声明
const emit = defineEmits(['update', 'delete'])

// 带参数验证(TypeScript)
const emit = defineEmits<{
  (e: 'update', id: number): void
  (e: 'delete', name: string): void
}>()

// 触发事件
emit('update', 123)
</script>

父组件监听

<MyComponent @update="handleUpdate" @delete="handleDelete" />

3. defineExpose – 暴露组件内部属性/方法给父组件(通过 ref)

作用:默认 <script setup> 下的组件是关闭的,父组件无法通过 ref 访问其内部成员。使用 defineExpose 明确暴露。

<script setup>
import { ref } from 'vue'

const count = ref(0)
const increment = () => count.value++

// 只暴露 increment 和 count,其他不暴露
defineExpose({
  increment,
  count
})
</script>

父组件访问

<template>
  <MyComponent ref="childRef" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
const childRef = ref()

onMounted(() => {
  childRef.value.increment()   // 调用子组件方法
  console.log(childRef.value.count) // 读取子组件数据
})
</script>

4. defineOptions – 设置组件选项(Vue 3.3+)

作用:在 <script setup> 中声明组件名、继承属性、自定义选项等,无需单独的 <script> 块。

<script setup>
defineOptions({
  name: 'MyCustomName',      // 组件名称
  inheritAttrs: false,       // 是否继承非 prop 属性
  // 其他选项(如 components、directives 一般不在这里,但可自定义)
})
</script>

典型场景

  • 设置组件名(方便 Vue Devtools 识别)
  • 关闭属性继承(手动控制 $attrs

23、wacth & watchEffect 区别

特性 watch watchEffect
依赖收集 显式指定要监听的数据源(ref、reactive 属性、getter 函数) 自动收集回调函数中使用的所有响应式数据
初始执行 默认不执行,数据第一次变化时才执行(可配置 immediate: true 立即执行一次,同时收集依赖
访问新旧值 回调中提供旧值和新值 只能访问新值(无法直接获取旧值)
监听多个源 支持同时监听多个数据源(数组形式) 自动收集多个依赖,无需显式指定
精准控制 可以配置 deepflushimmediate 等选项 只有 flush 选项(以及 onTrack/onTrigger 调试)
停止监听 调用返回的函数 同样返回停止函数
适用场景 需要知道具体哪个数据变化、需要旧值、需要惰性执行 简单副作用,自动跟踪依赖,不需要旧值

1、watch 基础用法

<script setup>
import { ref, reactive, watch } from 'vue'

const count = ref(0)
const state = reactive({ name: 'Vue', age: 3 })

// 监听单个 ref
watch(count, (newVal, oldVal) => {
  console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})

// 监听 getter 函数
watch(
  () => state.age,
  (newAge, oldAge) => {
    console.log(`年龄从 ${oldAge} 变为 ${newAge}`)
  }
)

// 监听多个源(数组)
watch([count, () => state.age], ([newCount, newAge], [oldCount, oldAge]) => {
  console.log(`count: ${oldCount}->${newCount}, age: ${oldAge}->${newAge}`)
})

// 立即执行 + 深度监听
watch(
  () => state,
  (newVal, oldVal) => {
    console.log('state 变化了', newVal)
  },
  { immediate: true, deep: true }
)
</script>

2、watchEffect 基础用法

<script setup>
import { ref, reactive, watchEffect } from 'vue'

const count = ref(0)
const state = reactive({ name: 'Vue', age: 3 })

// 自动收集依赖:count 和 state.age
watchEffect(() => {
  console.log(`count: ${count.value}, age: ${state.age}`)
})
// 初始立即输出:count: 0, age: 3
// 之后任何依赖变化都会重新执行

// 停止监听
const stop = watchEffect(() => { /* ... */ })
stop() // 手动停止
</script>

24、provide() 和 inject() 跨层级组件通信例子

<!-- Ancestor.vue -->
<script setup>
import { provide, ref } from 'vue'

// 提供普通值
provide('theme', 'dark')

// 提供响应式数据(推荐)
const count = ref(0)
const updateCount = () => count.value++
provide('count', count)
provide('updateCount', updateCount)
</script>
<!-- Descendant.vue -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme', 'light') // 默认值 'light'
const count = inject('count')
const updateCount = inject('updateCount')

// 使用
console.log(theme)   // 'dark'
count.value++        // 响应式更新
updateCount()        // 调用方法
</script>

25、toRefs、toRef、isRef、unref 响应式引用工具

1. toRefs – 将响应式对象转换为普通对象,每个属性都是 ref

作用:解构 reactive 对象时保持响应性。

import { reactive, toRefs } from 'vue'

const state = reactive({ count: 0, name: 'Vue' })

// ❌ 直接解构会丢失响应性
let { count, name } = state
count++  // 不会触发视图更新

// ✅ 使用 toRefs 包装
const stateRefs = toRefs(state)
const { count, name } = stateRefs
count.value++ // 响应式生效

原理toRefs 为每个属性创建一个 ref 链接到原对象的对应属性。

2. toRef – 为响应式对象的单个属性创建 ref

作用:保持对源对象属性的响应式引用,常用于将 props 的某个属性转为 ref 以便传递。

import { reactive, toRef } from 'vue'

const state = reactive({ count: 0 })
const countRef = toRef(state, 'count')

countRef.value++   // 同时修改 state.count
console.log(state.count) // 1

典型场景:组合函数接收 props 中的某个属性并保持响应性。

// useFeature.js
import { toRef, watchEffect } from 'vue'
export function useFeature(propRef) {
  const propVal = toRef(propRef)  // 确保是 ref
  watchEffect(() => {
    console.log(propVal.value)
  })
}

3. isRef – 判断某个值是否为 ref

import { ref, reactive, isRef } from 'vue'

const count = ref(0)
const state = reactive({})
console.log(isRef(count)) // true
console.log(isRef(state)) // false

4. unref – 如果参数是 ref 则返回其 value,否则返回参数本身

作用:方便地获取值,无需手动判断 .value

import { ref, unref } from 'vue'

const count = ref(0)
const plain = 42

console.log(unref(count)) // 0
console.log(unref(plain)) // 42

// 等价于
function myUnref(val) {
  return isRef(val) ? val.value : val
}

常用场景:在组合函数中,参数可能是 ref 也可能是普通值,使用 unref 统一处理。

React

1. React 是什么?核心特点

React 是 Meta 开源的 JavaScript UI 库,专注于构建用户界面。核心特点

  • 声明式编程:描述 UI 状态,React 自动处理 DOM 更新。
  • 组件化:UI 拆分为独立可复用的组件。
  • 单向数据流:数据从父组件流向子组件,可预测、易调试。
  • 虚拟 DOM + Fiber:高效更新,可中断渲染。
  • JSX:JavaScript 语法扩展,允许在 JS 中写类似 HTML 的标记。

2. JSX

JSX 是 React.createElement 的语法糖

// JSX 写法
const element = <h1 className="title">Hello React</h1>;

// 编译后
const element = React.createElement('h1', { className: 'title' }, 'Hello React');

浏览器无法识别 JSX,需要通过 Babel 编译为普通 JS 代码才能执行

3、组件间通信

React 组件间通信方式取决于组件关系,主要方式如下

方式 适用场景 示例
Props 父→子传递数据 <Child message={msg} />
回调函数 子→父传递数据 父传函数给子,子调用 props.onChildData(data)
Context API 跨层级组件通信(避免 props 逐层传递) createContext + useContext
状态管理库 大型应用全局状态 Redux Toolkit、Zustand、Jotai
Refs 直接访问 DOM 或子组件实例 useRef + ref 属性
自定义 Hooks 复用状态逻辑 useAuthuseFetch 等
高阶组件(HOC) 共享逻辑(较少使用,Hooks 更优) 接收组件返回增强组件
Event Bus(事件总线) 任意组件通信(非 React 原生) mitt 等第三方库

4、API

4.1 内置 Hooks

Hook 说明
useState 在函数组件中添加和管理局部状态,返回当前状态和更新函数
useReducer 用于管理包含多个子值或依赖先前状态的复杂组件逻辑,基于 reducer 模式
useContext 读取并订阅组件中的 Context 值,避免 props 逐层传递
useRef 声明一个可变引用,可以保存任何可变值,最常见的用途是访问 DOM 元素
useImperativeHandle 自定义通过 ref 暴露给父组件的实例值,通常与 forwardRef 配合使用
useEffect 将组件连接到外部系统并处理副作用,如数据获取、订阅、手动 DOM 操作,在渲染后执行
useLayoutEffect 在浏览器重新绘制屏幕之前同步触发,用法与 useEffect 相同,但会阻塞视觉更新
useInsertionEffect 在 DOM 变异之前触发,专为 CSS-in-JS 库注入样式而设计
useMemo 缓存昂贵计算的结果,避免在每次渲染时重复计算,仅在依赖项变化时重新计算
useCallback 缓存函数定义,防止因函数重新创建导致的子组件不必要重新渲染
useTransition 将状态更新标记为"过渡",这种更新可以被中断,以避免阻塞用户界面
useDeferredValue 延迟更新 UI 的某一部分,以优先响应用户输入
useId 生成在客户端和服务器上保持稳定的唯一 ID,主要用于可访问性属性
useDebugValue 在 React DevTools 中为自定义 Hook 添加标签,用于调试
useSyncExternalStore 允许函数组件订阅外部 store(如第三方状态管理库或浏览器 API)
useOptimistic 允许在后台操作完成前乐观地更新 UI,提供即时反馈
useActionState 管理表单 action 的状态,包括 pending 状态和返回数据
use 通用的资源读取 API,用于读取 Promise 或 Context 等资源的值,可以在条件语句中调用

4.2 内置组件

这些是可以在 JSX 中使用的 React 内置组件,以 Symbol 常量形式导出

组件 说明
<Fragment> 让你无需向 DOM 添加额外节点即可对子元素列表进行分组,支持简写 <>...</>
<Profiler> 用于编程式测量 React 应用的渲染性能
<StrictMode> 用于检测应用中潜在问题的工具,不会渲染任何可见 UI
<Suspense> 允许在子组件完成加载前显示一个回退 UI
<Activity> React 19 新增 API,用于隐藏和恢复其子组件的 UI 和内部状态

4.3 工具类 API

API 说明
createContext 创建一个 Context 对象,可供组件向其子组件提供数据,搭配 useContext 使用
forwardRef 允许组件将 DOM 节点作为 ref 暴露给父组件,搭配 useRef 使用
lazy 允许在组件第一次被渲染前延迟加载其代码,实现代码分割
memo 允许组件在 props 未发生变化时跳过重新渲染,搭配 useMemo 和 useCallback 使用
startTransition 允许将状态更新标记为非紧急的,与 useTransition 类似但用于非 Hook 场景
act 在测试中用于包裹渲染和交互,确保在断言前所有更新已处理完毕
createElement 创建 React 元素,通常用 JSX 替代,但可在非 JSX 环境中使用
cloneElement 克隆并返回一个新的 React 元素,可覆盖原元素的 props
isValidElement 检查某个值是否为 React 元素
Children 提供 mapforEachcountonlytoArray 等工具方法,用于处理 props.children 不透明数据结构
Component 定义类组件的基类
PureComponent 与 Component 类似,但自带 shouldComponentUpdate 浅比较实现
createRef 创建 ref 对象,类组件中用于访问 DOM 元素

4.4 通用 DOM API

API 说明
createPortal 允许将子组件渲染到 DOM 树中父组件 DOM 层次之外的不同位置,常用于模态框、全局提示等
flushSync 强制 React 同步执行状态更新并立即刷新 DOM

4.5 资源预加载 API

这些 API 用于预加载脚本、样式表、字体等资源,从而让应用更快。基于 React 的框架通常会自动处理资源加载

API 说明
prefetchDNS 预解析 DNS 域名,提前获取 IP 地址,减少后续请求的 DNS 查询时间
preconnect 提前连接到预计请求资源的服务器,建立 TCP 连接和 TLS 握手,即使尚不确定具体需要哪些资源
preload 预获取并缓存预计要使用的资源(如样式表、字体、图片、外部脚本),但不执行,可节省时间
preloadModule 预获取预计要使用的 ESM 模块,但不执行
preinit 预获取并执行外部脚本,或预获取并插入样式表
preinitModule 预获取并执行一个 ESM 模块

4.6 通用 DOM API

API 说明
createPortal 允许将子组件渲染到 DOM 树中父组件 DOM 层次之外的不同位置,常用于模态框、全局提示等
flushSync 强制 React 同步执行状态更新并立即刷新 DOM

5、React Router(路由)

React Router v6 完全利用 Hooks 重构。

核心组件

组件 作用
BrowserRouter history 模式路由容器
HashRouter hash 模式路由容器
Routes / Route 定义路由规则
Link / NavLink 声明式导航
Outlet 嵌套路由占位符

核心 Hooks

Hook 作用
useParams 获取路由参数
useLocation 获取当前 location 对象
useNavigate 程序化导航
useRoutes 配置式路由(替代 Routes + Route

6、React-Redux

API 说明
<Provider store> 顶层组件,使 store 对下层组件可用。
connect(mapStateToProps, mapDispatchToProps, mergeProps, options) 高阶组件(HOC),将 store 中的 state 和 dispatch 映射到组件的 props。
useSelector(selector) Hook,从 store 中提取数据,当数据变化时强制组件重新渲染。
useDispatch() Hook,返回 store 的 dispatch 函数。
useStore() Hook,返回 store 实例本身(不常用)。

Redux 现代推荐: (Redux Toolkit + React-Redux Hooks )

必须掌握的核心 API

API 作用 一句话说明
configureStore 创建 store 像 createStore 但更智能,自动加 thunk 和 DevTools
createSlice 同时生成 reducer 和 action creators 传入 name、initialState、reducers 对象,自动生成
createAsyncThunk 处理异步 action 自动生成 pending/fulfilled/rejected 三个 action,并在 extraReducers 中处理
// store.js
import { configureStore, createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 1. 同步 slice
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: state => { state.value += 1 },     // 直接“修改”
    decrement: state => { state.value -= 1 },
    incrementByAmount: (state, action) => { state.value += action.payload }
  }
});

// 2. 异步 thunk
export const fetchUser = createAsyncThunk('user/fetch', async (userId) => {
  const res = await fetch(`/api/user/${userId}`);
  return res.json();
});

// 3. 异步 slice (处理 thunk 的三种状态)
const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, loading: false, error: null },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => { state.loading = true })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false;
        state.data = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  }
});

// 导出 action creators 和 reducer
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
    user: userSlice.reducer
  }
});

export default store;

React-Redux Hooks —— 在组件里用状态

两个核心 Hook

Hook 作用 类比
useSelector 从 store 中读取数据 类似 mapStateToProps
useDispatch 拿到 dispatch 函数 类似 mapDispatchToProps

使用步骤(配合上面的 store)

1. 顶层用 Provider 包裹

// main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
);

2. 组件内读取和派发

// Counter.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from './store';

function Counter() {
  // 读取状态
  const count = useSelector(state => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>+1</button>
      <button onClick={() => dispatch(decrement())}>-1</button>
      <button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
    </div>
  );
}

3. 异步 thunk 的派发

import { fetchUser } from './store';

function UserProfile({ userId }) {
  const dispatch = useDispatch();
  const { data, loading, error } = useSelector(state => state.user);

  useEffect(() => {
    dispatch(fetchUser(userId));
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  return <div>{data?.name}</div>;
}

7、React 底层原理

1. 虚拟 DOM

虚拟 DOM 是用 JS 对象描述真实 DOM 结构

好处

  • 减少频繁的真实 DOM 操作
  • 跨平台(React Native)
  • 便于 diff 算法计算差异

2. Diff 算法

核心策略

  • 同层比较,不跨层级
  • 类型不同 → 直接销毁重建
  • 类型相同 → 对比属性
  • 列表靠 key 识别节点复用,复杂度优化到 O(n)

为什么 key 不能用 index?
数组增删前置元素会导致 index 错乱,引发组件状态错位、DOM 复用错误。应使用唯一稳定的业务 id

3. Fiber 架构(React 16+)

解决旧 React 一次性渲染卡死主线程的问题

两个阶段

  • Render 阶段:可中断、分片、优先级调度,遍历构建 Fiber 树
  • Commit 阶段:不可中断,一次性更新 DOM、布局、绘制

4. React 更新流程

触发 setState → 生成更新任务 → Fiber 调和(可中断)→ 收集 DOM 变更 → Commit 一次性渲染 → 浏览器绘制

5. 合成事件

React 自己实现了一套事件系统(事件委托到 root 节点),性能高,与原生事件混用时,原生先执行 → 合成后执行,阻止冒泡互不生效

❌