这次面试偏实战。面试官要求不使用ai的情况下,可以通过查找文档创建一个app。
我从React官网查找得到创建React的命令。
Q:面试官提问npx是什么。
过程中碰到了npx create-next-app@latest这样的命令。
npx 是 npm 内置的包执行工具,执行 npx create-next-app@latest 时,它会临时下载最新版的 create-next-app(无需全局安装),直接执行创建 Next.js 项目的命令,执行完就删除临时包。相比传统的全局安装方式,它避免了全局包污染,能始终用最新版本,还简化了操作步骤。
之后我基于vite构建了一个react应用
npm create vite@latest my-app -- --template react
Q:创建好应用后的第一个问题:StrictMode是什么?
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
一、核心作用
<StrictMode> 包裹整个 App 后,会对所有子组件(包括 App 及其内部所有组件) 生效,在开发环境下触发以下检查和行为:
1. 检测「过时/废弃的 React API」
扫描整个组件树中是否使用了 React 标记为“不安全/废弃”的 API(比如 React.createClass、UNSAFE_componentWillMount、findDOMNode 等),如果有会在控制台抛出明确的警告,提醒你替换为新 API,避免后续升级 React 版本时出现兼容问题。
2. 重复执行「纯函数类逻辑」,暴露非纯函数问题
React 要求组件渲染函数、useState 初始化函数、useMemo/useCallback 回调必须是纯函数(相同输入返回相同输出,无副作用)。StrictMode 会在开发环境下重复执行这些函数两次:
- 比如你的组件渲染时修改了全局变量、生成随机数(
useState(Math.random()))、直接修改 DOM,重复执行后会快速暴露这些非纯逻辑(比如全局变量值异常、随机数两次不一致),强制你规范代码。 - 注意:这种重复执行仅在开发环境生效,生产环境不会触发,不用担心影响线上功能。
3. 检测「不规范的副作用清理逻辑」
对组件中的 useEffect/useLayoutEffect,StrictMode 会做特殊校验:
- 首次挂载时:执行副作用函数 → 立即执行清理函数 → 再执行一次副作用函数;
- 目的是检测你是否规范编写了清理逻辑(比如定时器、事件监听、网络请求取消),避免内存泄漏。
- 比如你的组件中用
useEffect开启了定时器但没返回清理函数,StrictMode 会在控制台警告,且开发环境下会出现重复定时器的现象,让你及时发现问题。
4. 检测「违反不可变原则的操作」
比如组件中直接修改 props、state(而非通过 setState/useState 回调修改),或 Context.Provider 的 value 每次渲染都新建对象(导致子组件不必要重渲染),StrictMode 会抛出警告,引导你遵循 React 不可变数据的最佳实践。
二、关键注意点
- 仅影响开发环境:生产环境打包后,React 会自动忽略
<StrictMode>,不会有任何额外的检查/重复执行逻辑,也不会带来性能开销; - 开发环境组件渲染两次?正常!:你可能会发现开发环境下组件的
console.log输出两次,这是 StrictMode 故意重复执行渲染函数导致的,目的就是暴露非纯函数问题,不是 bug; - 全局生效:因为包裹了根组件
<App />,所以整个应用的所有组件都会被 StrictMode 检查,是项目级的全局校验。
三、简化版总结(面试/实操快速回答)
这段代码中 <StrictMode> 的核心作用是:
- 在开发环境下对整个应用的组件做全方位的“合规检查”,包括检测废弃 API、非纯函数、不规范的副作用清理、违反不可变原则的操作等;
- 提前暴露潜在问题,避免线上 bug;
- 生产环境自动失效,无任何性能影响。
简单来说,它就是 React 给开发者的“代码质检员”,只在开发时帮你挑错,上线后就“下班”,是保障项目代码质量的重要手段。
Q: 实现一个倒计时,从100减到0
这个问题的第一个陷阱就是闭包陷阱,如果直接count--,count值就始终会是99。
我这里通过setCount的函数式写法,让setCount从hooks链上直接获取之前的count值。这里也可以用useRef获得一个稳定的引用来解决。
第二个点就是,因为是在严格模式下进行的,所以useEffect会执行两次,会创建两个interval。所以需要卸载interval。否则会出现count一次减2的情况。
第三点在于减到0的时候就需要停止倒计时,也就是需要清除定时器。我这里在precount变成0的时候进行清除。
import { useEffect, useState } from 'react'
import './App.css'
function App() {
const [count, setCount] = useState(3)
useEffect(() => {
const timer = setInterval(() => {
setCount((preCount) => {
if(preCount > 0){
return preCount - 1
}else{
clearInterval(timer)
return 0
}
})
}, 1000)
return () => {
clearInterval(timer)
}
}, [])
return (
<>
{count}
</>
)
}
export default App