前端面试通关宝典:解析44道React测试题(下)

图片
作者|Yan Levin
译者|核子可乐
策划|丁晓昀
在面试 React 前端开发岗位时,我们当然应该为即将面对的技术问题做好充分准备。React 是目前用户界面构建领域最具人气的 JavaScript 库之一,企业雇主往往非常注重评估受试者对 React 核心概念、最佳实践以及相关技术方法的掌握情况。在本文中,我们将演讲 React 前端开发者在面试中经常遇到的 44 个问题。通过熟悉这些问题和答案,大家有望增加成功几率,充分展现自己在 React 开发方面的知识储备和技能水平。咱们闲言少叙,马上进入正题。
上篇是前22道题,下篇是后22道题,你能顺利通关吗?
22. 如何根据条件渲染元素?
我们可以使用各种条件运算符,包括三元运算符。
return ( {isVisible && I'm visible!});
return ( {isOnline ? I'm online! : I'm offline});
if (isOnline) { element = I'm online!;} else { element = I'm offline;}return ( {element});
了解更多:
https://react.dev/learn/conditional-rendering
23. userMemo 的用途是什么?它是如何起效的?
useMemo 用于缓存和记忆计算结果。
传递创建函数和依赖项数组。只有当任意依赖项的值发生变更时,useMemo 才会重新计算记忆值。这种优化方式能避免每次渲染都引发昂贵的计算过程。
使用首个参数,该函数将接受执行计算的回调;使用第二个依赖项数组,则仅当至少一个依赖项发生更改时,该函数才会重新执行计算。
const memoValue = useMemo(() => computeFunc(paramA, paramB), [paramA, paramB]);
了解更多:
https://react.dev/reference/react/useMemo
24. useCallback 的用途是什么,它是如何起效的?
useCallback hook 将返回回调的一个记忆版本,且仅当依赖项之一的值发生更改时该版本才会随之更改。其主要作用,就是在将回调传递给依赖于链接等效性的子组件时,避免触发不必要的渲染。
const callbackValue = useCallback(() => computeFunc(paramA, paramB), [paramA, paramB]);
了解更多:
https://react.dev/reference/react/useCallback
25. useMemo 和 useCallback 之间有什么区别?
useMemo 用于记忆计算结果,而 useCallback 用于记忆函数本体。
useMemo 缓存的是计算值,如果依赖项未更改,则在后续渲染时直接返回该值。
useCallback 缓存的是函数本体,只要依赖项未发生更改,则直接返回同一实例。
26. React Context 是什么?
React Context 是一项功能,提供一种通过组件树传递数据的方法,避免在每个层次上手动传递 props。它允许我们创建一个全局状态,树中的任何组件无论位置如何、均可以访问该状态。当我们需要在未通过 props 直接连接的多个组件之间共享数据时,就可以使用 Context。
React Context API 包含以下三个主要部分:
createContext: 此函数用于创建一个新的 context 上下文对象。
Context.Provider: 此组件用于向 context 提供值,其中打包有需要访问该值的组件。
Context.Consumer 或 useContext hook: 此组件或 hook 负责使用 context 中的值。它可以在上下文提供方内的任意组件中使用。
通过使用 React Context,我们可以避免 prop 钻取(即在多个层次的组件间传递项目)并轻松管理更高级别的状态,保证代码更具组织性的执行效率。
了解更多:
https://react.dev/learn/passing-data-deeply-with-context
27. useContext 的用途是什么,它是如何起效的?
在典型的 React 应用程序当中,数据使用 props 以自上而下(从父组件到子组件)的方式传递。但是,这样的方式对于某些特定类型的 props(例如选定的语言、UI 主题)来说可能过于繁琐,因为需要将其传递给应用程序中的多个组件。Context 上下文提供一种在组件之间共享此类数据的方法,而无需通过树结构中的各个层次显式传递 props。
当 context 的值改变时,调用 useContext 的组件也将随之进行重新渲染。如果重新渲染组件的成本很高,这里可以使用记忆机制进行优化。
const App = () => { const theme = useContext(ThemeContext); return ( Some div );}
了解更多:
https://react.dev/reference/react/useContext
28. useRef 的用途是什么,它是如何起效的?
useRef 返回一个可修改的 ref 对象,即一个属性。其中的当前值由传递的参数进行初始化。返回的对象将在组件的整个生命周期之内持续存在,且不会因渲染而发生改变。
其常见用法是以命令的形式访问后代,例如使用 ref,我们可以显式引用 DOM 元素。
const App = () => { const inputRef = useRef(null); const buttonClick = () => { inputRef.current.focus(); } return ( <>Focus on input tag )}
了解更多:
https://react.dev/reference/react/useRef
29. React.memo() 是什么?
React.memo() 是一种高阶组件。如果您的组件始终使用不变的 props 渲染相同的内容,则可以将其打包在 React.memo() 调用当中,通过记忆固定结果的方式提高性能。也就是说,React 将直接使用上次渲染的结果,避免重新执行渲染。React.memo() 只影响 props 的更改。如果一个函数组件被打包进 React.memo 并使用 useState、useReducer 或 useContext,那么当状态或上下文发生变化时,它将被重新渲染。
import { memo } from 'react';const MemoComponent = memo(MemoComponent = (props) => { // ...});
了解更多:
https://react.dev/reference/react/memo
30. React Fragment 是什么?
在 React 当中,经常需要从组件返回多个元素。Fragments 允许我们建立子元素列表,而无需在 DOM 当中创建不必要的节点。
<>// or
了解更多:
https://react.dev/reference/react/Fragment
31. React Reconciliation 是什么?
Reconciliation 协调是一种 React 算法,用于区分一个元素树与另一个元素树,借此确定其中需要替换的部分。
Reconciliation 也就是我们之前提到的虚拟 DOM 背后的算法。其基本定义是:在渲染一个 React 应用程序时,描述该应用程序的元素树是在保留的内存当中生成。之后,该树将被包含在渲染环境当中——例如浏览器应用程序,并被翻译成一组 DOM 操作。当应用程序状态更新时,则会生成一个新树。将新树与之前的树进行比较,即可准确计算并得出对更新后的应用程序进行重绘所需要的操作。
了解更多:
https://react.dev/learn/preserving-and-resetting-state
32. 在使用 map() 时,为什么需要列表中的键?
这些键将帮助 React 确定哪些元素已经被更改、添加或者移除。只有指定了这些键,React 才能随时间推移匹配数组元素。在选择键时,最好使用那些能够明确区分列表项与其相邻的字符串。大多数情况下,我们可以使用数据中的 ID 作为键。
const languages = [ { id: 1, lang: "JavaScript", }, { id: 2, lang: "TypeScript", }, { id: 3, lang: "Python", },];const App = () => { return ({languages.map((language) => ({language.lang} ))} );}
了解更多:
https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key
33. 如何在 Redux Thunk 中处理异步操作?
要使用 Redux Thunk,我们需要将其作为中间件导入。操作创建者不仅需要返回一个对象,还应返回一个以 dispatch 调度为参数的函数。
export const addUser = ({ firstName, lastName }) => { return dispatch => { dispatch(addUserStart()); } axios.post('https://jsonplaceholder.typicode.com/users', { firstName, lastName, completed: false }) .then(res => { dispatch(addUserSuccess(res.data)); }) .catch(error => { dispatch(addUserError(error.message)); })}
了解更多:
https://redux.js.org/usage/writing-logic-thunks
34. 如何跟踪函数组件中对象字段的变化?
要实现变化跟踪,我们需要使用 useEffect hook,并将对象的字段作为依赖项数组进行传递。
useEffect(() => { console.log('Changed!')}, [obj.someField])
35. 如何访问 DOM 元素?
这里可以使用由 React.createRef() 或 useRef() hook 创建的 refs,并将该 ref 属性附加至 React 元素。通过访问所创建的 ref 引用,即可通过 ref.current 来访问 DOM 元素。
const App = () => { const myRef = useRef(null); const handleClick = () => { console.log(myRef.current); // Accessing the DOM element }; return (Click Me );}export default App;
36. 什么是自定义 hook?
自定义 hook,是用于在不同组件之间重用逻辑的函数。这是一种打包可重用逻辑的方法,以便在多个组件之间轻松实现共享和重用。自定义 hook 通常是以单词“use”开头的函数,并可根据需求调用其他 hooks。
了解更多:
https://react.dev/learn/reusing-logic-with-custom-hooks
37. 什么是公共 API?
以索引文件为例,公共 API 通常是指向外部模块或组件公开、且可供访问的接口或函数。
以下是表示公共 API 的索引文件代码示例:
// index.jsexport function greet(name) { return `Hello, ${name}!`;}export function calculateSum(a, b) { return a + b;}
在此示例中,index.js 文件充当公共 API,其中会导出函数 greet() 和 calculateSum()。通过导入,即可从其他模块处访问这些函数。其他模块可以导入,并使用这些函数作为自身实现的组成部分:
// main.jsimport { greet, calculateSum } from './index.js';console.log(greet('John')); // Hello, John!console.log(calculateSum(5, 3)); // 8
通过从索引文件导出特定函数,我们即可定义模块的公共 API,并允许其他模块使用这些函数。
38. 创建自定义 hook 需要遵循哪些规则?
Hook 的名称以“use”开头。
如果必要,可使用现成的 hook。
不要为 hook 调用设置条件。
将可重用逻辑提取至自定义 hook 当中。
自定义 hook 必须为纯函数。
自定义 hook 可以返回值,也可以返回其他 hook。
尽可能在命名时清晰描述自定义 hook 的作用。
了解更多:
https://react.dev/learn/reusing-logic-with-custom-hooks
39. 什么是 SSR(服务器端渲染)?
服务器端渲染(SSR)是一种用于在服务器上渲染页面,再将完整渲染的页面发送至客户端进行呈现的技术。它允许服务器为网页生成完整的 HTML 标记(包括其中的动态内容),并将其作为请求的响应发送至客户端。
在传统的客户端渲染方法当中,客户端只接收最小的 HTML 页面,之后向服务器发出额外的数据和资源请求,再利用这些数据和资源在客户端渲染页面。这不仅会导致初始页面加载速度变慢,对于搜索引擎优化(SEO)也有负面影响,因为搜索引擎爬虫很难对 JavaScript 驱动的内容建立索引。
使用 SSR,服务器通过执行必要的 JavaScript 代码来生成用于最终页面呈现的 HTML。也就是说,客户端从服务器处接收到的完全渲染后的页面,从而减少了额外的资源需求。SSR 缩短了初始页面加载时间,并允许搜索引擎轻松索引内容,从而实现更好的 SEO 效果。
SSR 在各种框架和库中都很常见,例如用于 React 的 Next.js 和用于 Vue.js 的 Nuxt.js。这些框架能够帮助我们处理服务器端渲染逻辑,大大降低 SSR 的实现门槛。
40. 使用 SSR 有哪些好处?
缩短初始加载时间:SSR 允许服务器将完全渲染的 HTML 页面发送至客户端,从而减少客户端所需的处理量。这样可以缩短初始加载时间,让用户更快看到完整页面。
SEO 友好:搜索引擎可以有效抓取并索引 SSR 页面的内容,因为完全渲染的 HTML 在初始响应中已经可用。这就提高了内容在搜索引擎中的可见性,有助于带来更好的搜索排名。
可及性:SSR 使得禁用 JavaScript 或者使用辅助选项的用户也能正常访问内容。通过在服务器上生成 HTML,SSR 能够为所有用户提供可靠且易于访问的浏览体验。
低带宽环境下性能更好:SSR 减少了客户端需要下载的数据量,有利于低带宽或高延迟环境下的用户体验。这一点对于使用移动网络、或者固网速度较慢的用户尤其重要。
尽管 SSR 具备诸多优势,但需要注意的是,与客户端渲染方法相比,这也会给服务器负载和维护带来更多复杂性。因此在实施之前,应认真考虑缓存、可扩展性和服务器端渲染性能优化等因素。
41. 你了解 Next.js 中的哪些主要函数?
1. getStaticProps: 此方法用于在构建过程中获取数据,并将页面预渲染为静态 HTML。它能确保构建时的数据可用性,且不会因后续请求而发生更改。
export async function getStaticProps() { const res = await fetch('https://api.example.com/data'); const data = await res.json(); return { props: { data } };}
2. getServerSideProps: 此方法用于根据每个请求获取数据,并在服务器端预渲染页面。如果需要获取经常更改、或者仅供特定用户使用的数据,则可以使用此方法。
export async function getServerSideProps() { const res = await fetch('https://api.example.com/data'); const data = await res.json(); return { props: { data } };}
3. getStaticPaths: 此方法用于在动态路由当中,指定需要在构建时预渲染的路径列表,常用于获取带有参数的动态路由数据。
export async function getStaticPaths() { const res = await fetch('https://api.example.com/posts'); const posts = await res.json(); const paths = posts.map((post) => ({ params: { id: post.id } })); return { paths, fallback: false };}
了解更多:
https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating
42. 什么是 linters?
Linters 是用于检查源代码中是否存在潜在错误、bug 风格不一致及可维护性问题的工具,可帮助执行编码标准并保障整个代码库的代码质量与一致性。
Linters 的基本原理,是扫描源代码并将其与一组预先定义的规则或指南进行比较。具体规则可以包含语法与格式约定、最佳实践、潜在 bug 及代码异味。当 linter 发现违规情况时,将会生成警告或错误,并高亮显示需要注意的一行或多行代码。
使用 linter 有以下几大好处:
代码质量:Linters 有助于发现并防范潜在 bug、代码异味和反模式,从而提高代码质量。
一致性:Linters 会强制执行编码约定和风格指南,确保整个代码库的格式和代码结构保持一致,特别适合多位开发者处理同一项目的情况。
可维护性:通过尽早发现问题并推行良好的编码实践,linters 有助于提高代码的可维护性,让代码库更易于理解、修改和扩展。
效率:Linters 能够实现代码审查流程自动化,抢在常见错误引发开发 / 生产流程实际问题前将其发现,帮助开发人员节约时间。
常见的 linters 工具包括面向 JavaScript 的 ESLint,用于 CSS 的 Stylelint 以及 Sass。
了解更多:
https://eslint.org/docs/latest/use/getting-started
43. 你了解哪些 React 架构解决方案?
市面上有多种用于构建 React 项目的架构解决方案和模式,比较主流的包括:
MVC(模型 - 视图 - 控制器):MVC 是一种传统架构模式,主要将应用程序分为三大主要组成部分:模型、视图和控制器。React 可以在 View 视图层中渲染 UI,而其他库或框架则可用于 Model 模型层和 Controller 控制器层。
Flux:Flux 是 Facebook 专门针对 React 应用程序推出的应用架构。它遵循单向数据流,即数据仅沿单一方向流动,因此应用程序的状态更改更易于理解和调试。
原子设计:原子设计并非 React 所独有,而是一种将 UI 拆分成更小且可重用组件的设计方法。它鼓励构建小型、独立且能通过组合建立更复杂 UI 的组件。
容器与组件模式:此模式将表示(组件)与逻辑和状态管理(容器)区分开来。其中组件专门负责渲染 UI,而容器则处理业务逻辑和状态管理。
功能切片设计:这是一种用于组织和构建 React 应用程序的现代架构方法,强调根据功能或模块对应用程序代码库进行划分,从而解决可扩展性、可维护性和可重用性等难题。
44. 什么是功能切片设计?
这是一种用于组织和构建 React 应用程序的现代架构方法,强调根据功能或模块对应用程序代码库进行划分,从而解决可扩展性、可维护性和可重用性等难题。
在功能切片设计当中,应用程序的每个功能或模块都被组织为一个单独的目录,其中包含全部必要组件、actions、reducers 及其他相关文件。这有助于保持代码库的模块化和隔离性,降低开发、测试和维护的难度。
功能切片设计有助于对关注点做清晰划分,并为功能划定边界。这允许不同团队或开发人员独立处理不同功能,而不必分神于冲突或依赖项管理。
图片
本篇是面试题后22道,前22道题请点击此处查看。
声明:本文为 InfoQ 翻译,未经许可禁止转载。
原文链接:
https://dev.to/m_midas/44-react-frontend-interview-questions-2o63
收官之战!2023 年最后一场会议——QCon 全球软件开发大会·上海站,将于 12 月 28-29 日在上海·中优城市万豪酒店举办。
此次大会策划了 GenAI 和通用大模型应用探索、AI Agent 与行业融合应用的前景、LLM 时代的性能优化、智能化信创软件 IDE、LLM 时代的大前端技术、高性能网关设计、面向人工智能时代的架构、构建本土编程语言生态的实践、性能工程:提升效率和创新的新方法、LLM 推理加速和大规模服务、现代数据架构演进、建设弹性组织的经验传递、SaaS 云服务弹性架构设计、大模型研究进展与产业应用展望等专题。