真实面经题目 · 原创解析

Vue和React在创建组件的时候,说说最应该注意哪三点?

创建 Vue 和 React 组件时最应该注意三点:职责边界是否清晰、数据流与状态归属是否合理、副作用与性能边界是否可控。Vue 和 React 写法不同,但优秀组件都应该低耦合、可组合、可预测、易测试、易维护。

出现于:阿里巴巴 · 前端

60 秒回答模板

可以重点看三件事。第一是组件职责边界:一个组件只解决一个明确问题,对外通过稳定的 props、事件、slot 或 children 暴露能力,避免把业务流程、展示细节和数据请求全部塞在一起。第二是数据流和状态归属:能由父级或外部状态管理的就不要在子组件里私藏,派生状态不要重复存储,Vue 里要注意 props 单向下行、emit 上行,React 里要注意受控组件、状态提升和不可变更新。第三是副作用和性能:请求、订阅、定时器、DOM 操作要放在合适的生命周期或 effect 中,并且清理干净;渲染性能上要避免不必要的响应式依赖、无意义重渲染和不稳定引用。Vue 偏声明式响应式和模板约束,React 偏函数式渲染和显式状态更新,但底层判断标准是一致的:组件输入输出清楚、状态来源单一、副作用可追踪。

考点 总原则
主线 职责边界
易错点 只回答 Vue 和 React 语法差异,没有上升到组…

深入解析

01

总原则

Vue 和 React 创建组件时,不应该先纠结语法,而应该先判断这个组件的边界、数据和副作用是否合理。组件本质上是 UI 与行为的封装单元,好的组件应该让调用方只关心输入、输出和可扩展点,而不需要理解内部实现。无论是 Vue 的 SFC、props、emit、slot,还是 React 的函数组件、props、children、hooks,目标都是让组件具备可复用、可组合、可测试和可维护的特征。

02

职责边界

第一点是职责边界。组件创建前要明确它是展示组件、容器组件、表单控件、业务模块还是页面级组件。展示组件应该尽量只依赖 props,不主动请求数据,也不关心业务流程;容器组件可以负责拉取数据、组合状态和分发事件;页面组件可以编排多个模块。边界不清会导致一个组件同时处理布局、请求、权限、状态转换、埋点和展示,后续任何需求变化都会牵一发动全身。

03

组件 API

职责边界落到代码上就是组件 API 设计。Vue 中要设计清楚 props、emits、slots 和 expose;React 中要设计清楚 props、children、回调函数和 ref 暴露。props 应该表达必要输入,避免把整个大对象随意传入导致组件知道过多外部结构;事件或回调应该表达用户意图,而不是暴露内部实现细节。例如选择项变化应该叫 onChange 或 emit update,而不是让外部直接操作内部 index、visible、cache 之类实现状态。

04

数据流

第二点是数据流与状态归属。组件内部状态越多,行为越难预测,所以要区分外部状态、本地交互状态和派生状态。外部状态通常由父组件、路由、服务端缓存或全局状态管理维护;本地状态适合保存弹窗开关、输入临时值、hover、展开折叠等只影响当前组件的状态;派生状态应优先通过 computed、selector、useMemo 或普通计算得到,而不是再存一份。Vue 和 React 都强调单向数据流,只是 Vue 用响应式系统降低了模板更新成本,React 则通过 setState 触发重新渲染。

05

状态边界

判断状态放在哪里,要看谁拥有修改权、谁需要读取、生命周期跟谁一致。如果多个兄弟组件需要同一份状态,就应该提升到共同父级或状态管理层;如果状态只服务于组件内部交互,就不要暴露给父级。Vue 中不能直接修改 props,而应通过 emit 或 v-model 协议通知父级;React 中不能直接改 props 或 state 对象,而应通过 setState 和不可变更新。这样做的核心不是形式主义,而是保证状态变化路径单一,方便定位问题和做测试。

06

副作用

第三点是副作用与生命周期。组件渲染应该尽量是输入到 UI 的纯映射,网络请求、订阅、定时器、手动 DOM 操作、埋点和外部库初始化都属于副作用,需要放在明确的位置。Vue 中通常放在 onMounted、watch、watchEffect、onUnmounted 或组合式函数中;React 中通常放在 useEffect 或 useLayoutEffect 中。副作用必须考虑依赖变化和清理逻辑,否则容易出现重复请求、内存泄漏、事件重复绑定、旧闭包读取过期状态等问题。

07

性能边界

性能不是创建组件后才补救,而是组件边界设计时就要考虑。Vue 中要避免把过大的对象全部做深层响应式、避免滥用 watch deep、避免模板中进行高成本计算;React 中要注意父组件更新导致子组件函数重新执行、对象和函数引用不稳定导致 memo 失效、useEffect 依赖不正确导致重复执行。列表渲染要使用稳定 key,重计算要有缓存策略,复杂组件要拆分渲染边界,但不要为了性能过早引入大量 memo 或缓存。

08

差异理解

Vue 更强调模板、响应式依赖收集和约定式组件通信,很多更新优化由框架自动完成;React 更强调函数组件每次渲染重新执行、显式状态更新和 hooks 依赖管理。因此 Vue 组件创建时要特别注意响应式数据边界、props 解构后是否丢失响应性、watch 使用是否准确;React 组件创建时要特别注意闭包、依赖数组、不可变数据和引用稳定性。共同点是组件不能隐藏复杂数据流,也不能把副作用混进渲染逻辑。

易错点

  • 只回答 Vue 和 React 语法差异,没有上升到组件设计原则。
  • 把组件创建理解成生命周期执行顺序,忽略职责边界、数据流和副作用管理。
  • 直接在 Vue 子组件中修改 props,或在 React 中直接修改 state 对象。
  • 把可以计算出来的派生数据重复存成状态,导致源数据和展示结果不一致。
  • 在渲染逻辑中发请求、绑定事件或操作 DOM,导致重复执行和难以清理。
  • 滥用全局状态,把本来只属于局部交互的状态放到 store 中,增加维护成本。
  • 列表 key 使用 index,忽略排序、删除、插入时的组件复用问题。
  • 为了所谓通用性设计大量 props 和分支,导致组件 API 难用且难维护。
  • React 中忽略 effect 依赖数组和闭包问题,Vue 中滥用 deep watch 或不清楚响应式边界。
  • 过早使用 memo、useMemo、useCallback 或复杂缓存,却没有先解决组件边界和数据结构问题。

面试官追问

为什么职责边界比性能更优先?

因为职责边界决定了后续状态、通信、副作用和性能优化的空间。一个职责混乱的组件,即使局部用了 memo、computed 或 shouldUpdate,也只是降低某次渲染成本,并不能解决需求变化时难改、难测、难复用的问题。性能问题很多时候也是边界问题,例如页面级组件持有所有状态导致任何输入都让整棵子树更新,或者一个业务组件同时监听多个无关数据源。先把组件拆成稳定的展示单元、容器单元和业务编排单元,性能优化才有明确落点。

Vue 和 React 的状态管理思路有什么不同?

共同点是都强调数据来源清晰和单向数据流。差异在于 Vue 基于响应式依赖追踪,状态变化后框架会更细粒度地知道哪些依赖需要更新;React 是函数组件重新执行,通过 state 更新驱动重新渲染,再依赖 reconciliation 和必要的 memo 控制成本。Vue 写组件时更容易因为响应式对象、深度监听、props 解构等细节产生问题;React 写组件时更容易因为闭包、不可变更新、依赖数组和引用稳定性产生问题。面试中不能只说语法不同,要说出这些差异如何影响组件设计。

什么时候状态应该放在组件内部?

如果状态只影响当前组件,并且生命周期也只跟当前组件一致,就适合放在内部,例如下拉框是否展开、输入框临时聚焦态、局部 tab。若多个组件需要共享、父组件需要控制、状态需要被 URL 或服务端数据驱动,或者状态变化会影响业务流程,就应该提升到父级或外部状态层。判断的关键是所有权和同步成本:如果留在子组件会导致父级还要维护一份影子状态,那就应该上移;如果上移后只是增加传参,没有实际共享价值,就应该保留在本地。

React hooks 和 Vue Composition API 有什么相似点?

二者都能把可复用逻辑从组件中抽出来,让组件主体更关注结构和交互。React 中用自定义 hook 抽离请求、订阅、表单状态、窗口尺寸等逻辑;Vue 中用 composable 抽离响应式状态、计算值、watch 和生命周期逻辑。区别是 React hook 受调用顺序和依赖数组约束,必须在组件或 hook 顶层调用;Vue composable 依赖响应式系统,通常返回 ref、reactive、computed 和方法。无论哪种方式,都不应该把无关逻辑硬塞进一个 hook 或 composable,否则只是把大组件换成了大函数。

创建列表组件时最容易犯什么错?

最常见的是 key 不稳定、状态归属混乱和渲染成本失控。key 如果使用 index,在增删、排序、筛选时可能导致组件实例复用错误,表现为输入框错位、选中状态错乱或动画异常。状态归属混乱是指每一行既有内部状态,父级又保存一份选中或编辑状态,最后同步困难。性能方面,如果列表项很重,父级每次状态变化都让所有行重新渲染,就需要考虑拆分行组件、稳定 props、虚拟滚动或缓存计算,但前提是先把数据结构和更新路径设计清楚。

如何判断一个组件是不是过度封装?

过度封装通常表现为 props 非常多、配置项难以理解、内部有大量分支处理不同业务场景、调用方为了使用它反而要写很多适配代码。如果组件抽象的是稳定通用能力,例如按钮、表单项、弹窗、表格基础能力,封装通常有价值;如果只是把几个页面临时相似的业务逻辑强行合并,后续差异会不断挤进组件内部,形成复杂配置和条件判断。判断边界是:复用应该减少整体复杂度,而不是把复杂度从调用方转移到组件内部。