React 18 相比传统 DOM 操作有哪些优势?
What advantages does React 18 have over traditional DOM manipulation?
- *考察点:React价值理解。*
共 48 道题目
What advantages does React 18 have over traditional DOM manipulation?
What advantages does React 18 have over traditional DOM manipulation?
考察点:React价值理解。
答案:
React 18 相比传统DOM操作提供了声明式编程范式和高效的渲染机制。传统DOM操作需要手动管理元素的创建、更新和销毁,而React通过虚拟DOM和Fiber架构实现了自动化的DOM管理,让开发者专注于描述UI应该是什么样子,而非如何操作DOM。
主要优势:
代码示例:
// 传统DOM操作
const button = document.createElement('button');
button.textContent = count;
button.addEventListener('click', () => {
count++;
button.textContent = count;
});
// React 18方式
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
适用场景:
How does event handling in JSX differ from native HTML events?
How does event handling in JSX differ from native HTML events?
考察点:JSX事件机制。
答案:
JSX中的事件处理基于React的合成事件(SyntheticEvent)系统,它是对原生DOM事件的封装和统一。合成事件提供了跨浏览器的一致性接口,同时保持了与原生事件相同的API,但在事件委托、性能优化和开发体验方面有显著改进。
主要区别:
代码示例:
// HTML原生事件
<button onclick="handleClick()">Click me</button>
// JSX事件处理
function Button() {
const handleClick = (e) => {
e.preventDefault(); // 必须显式调用
console.log('Button clicked', e.nativeEvent); // 访问原生事件
};
return <button onClick={handleClick}>Click me</button>;
}
// 事件委托示例
function List() {
const handleItemClick = (e) => {
console.log('Clicked item:', e.target.textContent);
};
return (
<ul onClick={handleItemClick}>
<li>Item 1</li>
<li>Item 2</li>
</ul>
);
}
注意事项:
What is the difference between function components and class components in usage?
What is the difference between function components and class components in usage?
考察点:组件类型对比。
答案:
函数组件和类组件是React中定义组件的两种方式。React 16.8引入Hooks后,函数组件已成为主流推荐方式。函数组件语法简洁,结合Hooks可以实现类组件的所有功能,同时提供更好的性能和开发体验。
主要区别:
代码示例:
// 类组件
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.increment = this.increment.bind(this);
}
componentDidMount() {
document.title = `Count: ${this.state.count}`;
}
componentDidUpdate() {
document.title = `Count: ${this.state.count}`;
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.increment}>{this.state.count}</button>;
}
}
// 函数组件 + Hooks
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
适用场景:
How do props implement data transfer between parent and child components?
How do props implement data transfer between parent and child components?
考察点:数据流向理解。
答案:
props是React中实现父子组件数据传递的机制,遵循单向数据流原则。父组件通过props向子组件传递数据,子组件通过props接收数据并可以调用父组件传递的回调函数来实现向上通信。这种设计保证了数据流的可预测性和组件的独立性。
传递机制:
代码示例:
// 父组件
function Parent() {
const [message, setMessage] = useState('Hello');
const [count, setCount] = useState(0);
const handleChildMessage = (childData) => {
setMessage(childData);
};
return (
<div>
<h1>{message}</h1>
<Child
title="子组件标题"
count={count}
onMessage={handleChildMessage}
user={{ name: 'Alice', age: 25 }}
/>
</div>
);
}
// 子组件
function Child({ title, count, onMessage, user }) {
const handleClick = () => {
onMessage(`子组件说:当前计数是 ${count}`);
};
return (
<div>
<h2>{title}</h2>
<p>用户:{user.name},年龄:{user.age}</p>
<p>计数:{count}</p>
<button onClick={handleClick}>发送消息给父组件</button>
</div>
);
}
// 使用PropTypes进行类型检查
Child.propTypes = {
title: PropTypes.string.isRequired,
count: PropTypes.number,
onMessage: PropTypes.func,
user: PropTypes.object
};
Child.defaultProps = {
count: 0,
onMessage: () => {}
};
最佳实践:
How does React 18's automatic batching affect state updates?
How does React 18’s automatic batching affect state updates?
考察点:批处理机制。
答案:
React 18的自动批处理将多个状态更新自动合并为一次重渲染,显著提升了应用性能。不同于React 17只在事件处理器中进行批处理,React 18扩展了批处理范围,包括Promise、setTimeout等异步操作中的状态更新也会被自动批处理。
批处理影响:
代码示例:
function BatchingExample() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const [count3, setCount3] = useState(0);
// React 18中只会触发一次重渲染
const handleClick = () => {
setCount1(c => c + 1);
setCount2(c => c + 1);
setCount3(c => c + 1);
// 三次更新会被批处理
};
// 异步操作中的批处理(React 18新特性)
const handleAsyncClick = () => {
setTimeout(() => {
setCount1(c => c + 1);
setCount2(c => c + 1);
// React 18中这两个更新也会被批处理
}, 1000);
};
// 阻止批处理(需要立即更新)
const handleFlushSync = () => {
flushSync(() => {
setCount1(c => c + 1);
});
// 这会立即触发渲染
flushSync(() => {
setCount2(c => c + 1);
});
// 这也会立即触发渲染
};
console.log('Component rendered'); // 观察渲染次数
return (
<div>
<p>Count1: {count1}</p>
<p>Count2: {count2}</p>
<p>Count3: {count3}</p>
<button onClick={handleClick}>同步批处理</button>
<button onClick={handleAsyncClick}>异步批处理</button>
<button onClick={handleFlushSync}>强制立即更新</button>
</div>
);
}
注意事项:
What scenarios are controlled and uncontrolled components suitable for respectively?
What scenarios are controlled and uncontrolled components suitable for respectively?
考察点:表单控制策略。
答案:
受控组件和非受控组件是React中处理表单输入的两种策略。受控组件的值完全由React状态管理,提供更好的数据控制;非受控组件依赖DOM自身状态,通过ref获取值,适合简单表单或与第三方库集成。
适用场景对比:
受控组件适用场景:
非受控组件适用场景:
代码示例:
// 受控组件示例
function ControlledForm() {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// 实时验证
if (name === 'email') {
setErrors(prev => ({
...prev,
email: !value.includes('@') ? '邮箱格式不正确' : ''
}));
}
};
return (
<form>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="邮箱"
/>
{errors.email && <span className="error">{errors.email}</span>}
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="密码"
/>
</form>
);
}
// 非受控组件示例
function UncontrolledForm() {
const emailRef = useRef();
const passwordRef = useRef();
const fileRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const formData = {
email: emailRef.current.value,
password: passwordRef.current.value,
file: fileRef.current.files[0]
};
console.log('提交数据:', formData);
};
return (
<form onSubmit={handleSubmit}>
<input
ref={emailRef}
defaultValue="[email protected]"
placeholder="邮箱"
/>
<input
ref={passwordRef}
type="password"
placeholder="密码"
/>
{/* 文件上传只能是非受控的 */}
<input
ref={fileRef}
type="file"
/>
<button type="submit">提交</button>
</form>
);
}
选择建议:
How does useEffect's dependency array affect side effect execution?
How does useEffect’s dependency array affect side effect execution?
考察点:依赖数组机制。
答案:
useEffect的依赖数组决定了副作用函数何时执行,是React Hooks中控制副作用执行时机的关键机制。React通过浅比较依赖数组中的值来判断是否需要重新执行副作用函数,这种设计既保证了性能又避免了不必要的副作用执行。
依赖数组的三种形式:
代码示例:
function EffectExample({ userId, filter }) {
const [user, setUser] = useState(null);
const [count, setCount] = useState(0);
// 1. 无依赖数组 - 每次渲染都执行
useEffect(() => {
console.log('每次渲染都执行');
});
// 2. 空依赖数组 - 仅在挂载/卸载时执行
useEffect(() => {
console.log('组件挂载');
return () => {
console.log('组件卸载');
};
}, []);
// 3. 依赖userId - 当userId改变时执行
useEffect(() => {
async function fetchUser() {
if (userId) {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
}
}
fetchUser();
}, [userId]); // 只有userId变化时才重新获取用户数据
// 4. 多个依赖 - 任一依赖变化都会执行
useEffect(() => {
document.title = `User: ${user?.name || 'Unknown'} - Filter: ${filter}`;
}, [user?.name, filter]);
// 5. 函数依赖的正确处理
const fetchData = useCallback(async () => {
const response = await fetch(`/api/data?filter=${filter}`);
return response.json();
}, [filter]);
useEffect(() => {
fetchData().then(data => {
console.log('数据更新:', data);
});
}, [fetchData]); // 使用useCallback包装的函数作为依赖
// 6. 清理副作用
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => {
clearInterval(timer); // 清理定时器
};
}, []); // 空依赖,定时器只创建一次
return (
<div>
<p>Count: {count}</p>
<p>User: {user?.name}</p>
</div>
);
}
常见陷阱和最佳实践:
How to understand the asynchronous update characteristics of useState?
How to understand the asynchronous update characteristics of useState?
考察点:异步状态更新。
答案:
useState的异步更新是React为了性能优化而采用的设计策略。状态更新函数调用后不会立即改变状态值,而是安排在下次渲染时更新。这种异步机制配合批处理能够避免不必要的重渲染,但也要求开发者理解其执行时机和正确的使用方式。
异步更新特性:
代码示例:
function AsyncUpdateExample() {
const [count, setCount] = useState(0);
const [message, setMessage] = useState('');
// 1. 基本异步更新演示
const handleBasicUpdate = () => {
console.log('更新前:', count); // 0
setCount(count + 1);
console.log('更新后:', count); // 仍然是0,因为异步更新
// 实际的count值会在下次渲染时变为1
};
// 2. 多次更新的陷阱
const handleMultipleUpdates = () => {
setCount(count + 1); // count = 0,设置为1
setCount(count + 1); // count仍然是0,设置为1
setCount(count + 1); // count仍然是0,设置为1
// 最终结果是1,而不是3
};
// 3. 函数式更新解决方案
const handleFunctionalUpdate = () => {
setCount(prevCount => prevCount + 1); // 1
setCount(prevCount => prevCount + 1); // 2
setCount(prevCount => prevCount + 1); // 3
// 最终结果是3
};
// 4. 异步操作中的闭包陷阱
const handleAsyncOperation = () => {
setTimeout(() => {
// 这里的count可能是过期的值
setCount(count + 1); // 可能不是期望的结果
// 正确做法:使用函数式更新
setCount(prevCount => prevCount + 1);
}, 1000);
};
// 5. 复杂场景:依赖前一个状态的更新
const handleComplexUpdate = () => {
// 批处理:这些更新会在同一次渲染中应用
setCount(prevCount => {
const newCount = prevCount + 1;
setMessage(`Count updated to ${newCount}`);
return newCount;
});
};
// 6. 使用useEffect监听状态变化
useEffect(() => {
console.log('Count changed to:', count);
// 这里可以获取到最新的count值
}, [count]);
// 7. 立即获取更新后的值(不推荐,仅用于特殊场景)
const handleImmediateRead = () => {
const newCount = count + 1;
setCount(newCount);
// 使用计算出的newCount而不是count
console.log('New count will be:', newCount);
};
return (
<div>
<p>Count: {count}</p>
<p>Message: {message}</p>
<button onClick={handleBasicUpdate}>基本更新</button>
<button onClick={handleMultipleUpdates}>多次更新陷阱</button>
<button onClick={handleFunctionalUpdate}>函数式更新</button>
<button onClick={handleAsyncOperation}>异步操作</button>
<button onClick={handleComplexUpdate}>复杂更新</button>
<button onClick={handleImmediateRead}>立即读取</button>
</div>
);
}
最佳实践:
What impact do React 18's concurrent features have on developers?
What impact do React 18’s concurrent features have on developers?
考察点:并发特性认知。
答案:
React 18的并发特性为开发者带来了更细粒度的渲染控制和更好的用户体验优化能力。这些特性让React能够中断、暂停和恢复组件渲染,根据任务优先级智能调度,但也要求开发者理解新的心智模型和潜在的副作用。
主要影响:
代码示例:
import { startTransition, useDeferredValue, Suspense } from 'react';
function ConcurrentExample() {
const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
// 延迟值,用于优化搜索体验
const deferredQuery = useDeferredValue(query);
// 紧急更新:用户输入
const handleInputChange = (e) => {
setQuery(e.target.value); // 高优先级,立即更新
};
// 非紧急更新:搜索结果
const handleSearch = () => {
startTransition(() => {
// 低优先级更新,可以被中断
const searchResults = performExpensiveSearch(deferredQuery);
setResults(searchResults);
});
};
// 模拟昂贵的搜索操作
const performExpensiveSearch = (query) => {
// 模拟计算密集型操作
const results = [];
for (let i = 0; i < 10000; i++) {
if (i.toString().includes(query)) {
results.push(`Result ${i}`);
}
}
return results.slice(0, 100);
};
return (
<div>
<div>
<input
value={query}
onChange={handleInputChange}
placeholder="搜索..."
/>
<button onClick={handleSearch}>
搜索 {isPending && '(搜索中...)'}
</button>
</div>
{/* Suspense边界处理异步加载 */}
<Suspense fallback={<div>Loading results...</div>}>
<SearchResults results={results} />
</Suspense>
</div>
);
}
// 使用memo优化组件渲染
const SearchResults = React.memo(({ results }) => {
return (
<ul>
{results.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
);
});
// 并发安全的组件示例
function ConcurrentSafeComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchData() {
const result = await api.getData();
// 检查组件是否仍然挂载
if (!cancelled) {
setData(result);
}
}
fetchData();
return () => {
cancelled = true; // 清理函数防止内存泄漏
};
}, []);
return data ? <div>{data.content}</div> : <div>Loading...</div>;
}
开发注意事项:
How to avoid directly modifying state in React?
How to avoid directly modifying state in React?
考察点:不可变性原则。
答案:
React要求遵循不可变性原则,不能直接修改state对象。直接修改state会导致组件无法正确重新渲染,因为React使用Object.is()进行浅比较来判断state是否发生变化。正确的做法是创建新的对象或数组来替换原有状态。
不可变更新模式:
代码示例:
function ImmutableStateExample() {
const [user, setUser] = useState({
name: 'Alice',
age: 25,
preferences: {
theme: 'dark',
language: 'zh-CN'
},
hobbies: ['reading', 'coding']
});
// ❌ 错误:直接修改state
const handleWrongUpdate = () => {
user.age = 26; // 直接修改
user.hobbies.push('gaming'); // 直接修改数组
setUser(user); // 组件不会重新渲染
};
// ✅ 正确:对象属性更新
const handleObjectUpdate = () => {
setUser(prevUser => ({
...prevUser,
age: prevUser.age + 1
}));
};
// ✅ 正确:嵌套对象更新
const handleNestedUpdate = () => {
setUser(prevUser => ({
...prevUser,
preferences: {
...prevUser.preferences,
theme: prevUser.preferences.theme === 'dark' ? 'light' : 'dark'
}
}));
};
// ✅ 正确:数组更新操作
const handleArrayOperations = () => {
setUser(prevUser => ({
...prevUser,
hobbies: [
...prevUser.hobbies,
'gaming' // 添加新元素
]
}));
};
const handleArrayRemove = (indexToRemove) => {
setUser(prevUser => ({
...prevUser,
hobbies: prevUser.hobbies.filter((_, index) => index !== indexToRemove)
}));
};
const handleArrayUpdate = (indexToUpdate, newHobby) => {
setUser(prevUser => ({
...prevUser,
hobbies: prevUser.hobbies.map((hobby, index) =>
index === indexToUpdate ? newHobby : hobby
)
}));
};
// ✅ 使用useCallback优化更新函数
const handleComplexUpdate = useCallback((updates) => {
setUser(prevUser => {
// 深度合并逻辑
const mergeDeep = (target, source) => {
const result = { ...target };
for (const key in source) {
if (source[key] && typeof source[key] === 'object') {
result[key] = mergeDeep(target[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
};
return mergeDeep(prevUser, updates);
});
}, []);
return (
<div>
<h3>用户信息</h3>
<p>姓名: {user.name}</p>
<p>年龄: {user.age}</p>
<p>主题: {user.preferences.theme}</p>
<p>爱好: {user.hobbies.join(', ')}</p>
<button onClick={handleObjectUpdate}>年龄+1</button>
<button onClick={handleNestedUpdate}>切换主题</button>
<button onClick={handleArrayOperations}>添加爱好</button>
</div>
);
}
// 使用Immer简化不可变更新
import { produce } from 'immer';
function ImmerExample() {
const [state, setState] = useState(initialComplexState);
const handleUpdateWithImmer = () => {
setState(produce(draft => {
// 可以直接"修改"draft对象
draft.user.age += 1;
draft.user.hobbies.push('gaming');
draft.user.preferences.theme = 'light';
}));
};
return (
<button onClick={handleUpdateWithImmer}>
使用Immer更新
</button>
);
}
最佳实践:
What problems does Context API solve? What is its basic usage?
What problems does Context API solve? What is its basic usage?
考察点:Context基础用法。
答案:
Context API解决了React中的"props drilling"(属性钻取)问题,允许跨多层组件传递数据而无需逐层传递props。它提供了一种在组件树中共享全局状态的机制,适用于主题、用户认证、语言设置等需要被多个组件访问的数据。
解决的核心问题:
代码示例:
import { createContext, useContext, useState, useReducer } from 'react';
// 1. 创建Context
const ThemeContext = createContext();
const UserContext = createContext();
// 2. 主题Context Provider
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
const value = {
theme,
toggleTheme
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
// 3. 用户Context与Reducer结合
const userReducer = (state, action) => {
switch (action.type) {
case 'LOGIN':
return {
...state,
user: action.payload,
isAuthenticated: true
};
case 'LOGOUT':
return {
...state,
user: null,
isAuthenticated: false
};
case 'UPDATE_PROFILE':
return {
...state,
user: { ...state.user, ...action.payload }
};
default:
return state;
}
};
function UserProvider({ children }) {
const [state, dispatch] = useReducer(userReducer, {
user: null,
isAuthenticated: false,
loading: false
});
const login = async (credentials) => {
try {
const user = await authenticateUser(credentials);
dispatch({ type: 'LOGIN', payload: user });
} catch (error) {
console.error('Login failed:', error);
}
};
const logout = () => {
dispatch({ type: 'LOGOUT' });
};
const value = {
...state,
login,
logout,
dispatch
};
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}
// 4. 自定义Hook简化使用
const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
};
const useUser = () => {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within UserProvider');
}
return context;
};
// 5. 使用Context的组件
function Header() {
const { theme, toggleTheme } = useTheme();
const { user, logout, isAuthenticated } = useUser();
return (
<header className={`header ${theme}`}>
<h1>My App</h1>
<div>
<button onClick={toggleTheme}>
切换到 {theme === 'light' ? '暗色' : '亮色'} 主题
</button>
{isAuthenticated ? (
<div>
<span>欢迎, {user.name}</span>
<button onClick={logout}>退出</button>
</div>
) : (
<LoginButton />
)}
</div>
</header>
);
}
function LoginButton() {
const { login } = useUser();
const handleLogin = () => {
login({ username: '[email protected]', password: '123456' });
};
return <button onClick={handleLogin}>登录</button>;
}
// 6. 应用根组件
function App() {
return (
<ThemeProvider>
<UserProvider>
<div className="app">
<Header />
<main>
<Dashboard />
<ProfilePage />
</main>
</div>
</UserProvider>
</ThemeProvider>
);
}
// 7. 多个Context的组合使用
function Dashboard() {
const { theme } = useTheme();
const { user, isAuthenticated } = useUser();
if (!isAuthenticated) {
return <div>请先登录</div>;
}
return (
<div className={`dashboard ${theme}`}>
<h2>仪表板</h2>
<p>当前用户: {user.name}</p>
<p>当前主题: {theme}</p>
</div>
);
}
使用注意事项:
What is the mechanism of key in React?
What is the mechanism of key in React?
考察点:key底层原理。
答案:
React中的key是虚拟DOM diff算法的重要优化机制,用于标识列表中每个元素的唯一性。当组件重新渲染时,React通过key来判断哪些元素是新增、删除、移动或更新的,从而进行精确的DOM操作,避免不必要的元素重建。
工作机制:
代码示例:
function KeyExample() {
const [items, setItems] = useState([
{ id: 1, name: 'Apple', price: 1.2 },
{ id: 2, name: 'Banana', price: 0.8 },
{ id: 3, name: 'Orange', price: 1.5 }
]);
const [showInput, setShowInput] = useState(false);
// ❌ 错误:使用索引作为key
const BadExample = () => (
<ul>
{items.map((item, index) => (
<li key={index}>
<input type="text" defaultValue={item.name} />
<span>{item.price}</span>
</li>
))}
</ul>
);
// ✅ 正确:使用稳定的唯一标识符作为key
const GoodExample = () => (
<ul>
{items.map((item) => (
<li key={item.id}>
<input type="text" defaultValue={item.name} />
<span>{item.price}</span>
<button onClick={() => removeItem(item.id)}>删除</button>
</li>
))}
</ul>
);
// 演示key的重要性:添加到列表开头
const addItemToStart = () => {
const newItem = {
id: Date.now(),
name: 'New Item',
price: Math.random() * 2
};
setItems([newItem, ...items]);
};
const removeItem = (id) => {
setItems(items.filter(item => item.id !== id));
};
const shuffleItems = () => {
const shuffled = [...items].sort(() => Math.random() - 0.5);
setItems(shuffled);
};
// 条件渲染中的key使用
const ConditionalExample = () => (
<div>
{showInput ? (
<input key="search" type="text" placeholder="搜索..." />
) : (
<button key="toggle" onClick={() => setShowInput(true)}>
显示搜索框
</button>
)}
</div>
);
return (
<div>
<h3>Key 机制演示</h3>
<div>
<button onClick={addItemToStart}>添加到开头</button>
<button onClick={shuffleItems}>随机排序</button>
<button onClick={() => setShowInput(!showInput)}>
切换输入框
</button>
</div>
<div style={{ display: 'flex', gap: '20px' }}>
<div>
<h4>❌ 使用索引作为key(会有问题)</h4>
<BadExample />
</div>
<div>
<h4>✅ 使用唯一ID作为key</h4>
<GoodExample />
</div>
</div>
<ConditionalExample />
</div>
);
}
// 复杂场景:嵌套列表中的key
function NestedListExample() {
const [categories, setCategories] = useState([
{
id: 'fruits',
name: '水果',
items: [
{ id: 'apple', name: '苹果' },
{ id: 'banana', name: '香蕉' }
]
},
{
id: 'vegetables',
name: '蔬菜',
items: [
{ id: 'carrot', name: '胡萝卜' },
{ id: 'broccoli', name: '西兰花' }
]
}
]);
return (
<div>
{categories.map(category => (
<div key={category.id}>
<h3>{category.name}</h3>
<ul>
{category.items.map(item => (
<li key={`${category.id}-${item.id}`}>
{item.name}
</li>
))}
</ul>
</div>
))}
</div>
);
}
// Fragment中的key使用
function FragmentKeyExample({ items }) {
return (
<div>
{items.map(item => (
<React.Fragment key={item.id}>
<h4>{item.title}</h4>
<p>{item.description}</p>
</React.Fragment>
))}
</div>
);
}
Key选择原则:
常见问题和解决方案:
Why do components render twice in React 18 strict mode?
Why do components render twice in React 18 strict mode?
考察点:严格模式机制。
答案:
React 18严格模式下的双重渲染是为了帮助开发者检测副作用和并发安全问题而设计的开发时特性。这种机制通过故意重复执行组件函数、Effect和事件处理器来暴露不安全的副作用,确保组件在并发渲染环境下能够正确工作。
双重渲染的目的:
代码示例:
import { StrictMode, useState, useEffect, useRef } from 'react';
// 1. 演示严格模式下的双重渲染
function StrictModeExample() {
const [count, setCount] = useState(0);
const renderCount = useRef(0);
// 在严格模式下这会执行两次
renderCount.current += 1;
console.log(`组件渲染第 ${renderCount.current} 次`);
return (
<div>
<p>Count: {count}</p>
<p>Render count: {renderCount.current}</p>
<button onClick={() => setCount(c => c + 1)}>增加</button>
</div>
);
}
// 2. ❌ 有副作用的组件(严格模式会暴露问题)
function ComponentWithSideEffects() {
const [data, setData] = useState(null);
// ❌ 问题:直接在组件函数中产生副作用
// 严格模式下会执行两次,可能导致重复的网络请求
if (!data) {
fetch('/api/data').then(res => res.json()).then(setData);
}
return <div>{data ? data.message : 'Loading...'}</div>;
}
// 3. ✅ 修正:将副作用移到useEffect中
function ComponentWithoutSideEffects() {
const [data, setData] = useState(null);
useEffect(() => {
let cancelled = false;
fetch('/api/data')
.then(res => res.json())
.then(result => {
if (!cancelled) {
setData(result);
}
});
return () => {
cancelled = true; // 清理函数防止内存泄漏
};
}, []);
return <div>{data ? data.message : 'Loading...'}</div>;
}
// 4. Effect的双重执行演示
function EffectStrictModeExample() {
const [user, setUser] = useState(null);
useEffect(() => {
console.log('Effect执行:设置用户数据');
// 模拟订阅
const subscription = subscribeToUser(userId, setUser);
return () => {
console.log('Effect清理:取消订阅');
subscription.unsubscribe();
};
}, []);
// 在严格模式下,你会看到:
// "Effect执行:设置用户数据"
// "Effect清理:取消订阅"
// "Effect执行:设置用户数据"(再次执行)
return <div>User: {user?.name}</div>;
}
// 5. 正确处理双重渲染的模式
function ProperStrictModeComponent() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const abortControllerRef = useRef();
const fetchItems = useCallback(async () => {
// 取消之前的请求
abortControllerRef.current?.abort();
const controller = new AbortController();
abortControllerRef.current = controller;
setLoading(true);
try {
const response = await fetch('/api/items', {
signal: controller.signal
});
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
setItems(data);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Fetch error:', error);
}
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchItems();
return () => {
abortControllerRef.current?.abort();
};
}, [fetchItems]);
return (
<div>
{loading ? 'Loading...' : `Found ${items.length} items`}
</div>
);
}
// 6. 应用根组件中启用严格模式
function App() {
return (
<StrictMode>
<div className="app">
<h1>React 18 Strict Mode Demo</h1>
<StrictModeExample />
<ComponentWithoutSideEffects />
<EffectStrictModeExample />
<ProperStrictModeComponent />
</div>
</StrictMode>
);
}
严格模式检查项目:
最佳实践:
What other uses does useRef have besides getting DOM?
What other uses does useRef have besides getting DOM?
考察点:useRef多重用途。
答案:
useRef除了获取DOM元素外,还有多种重要用途。它创建一个可变的引用对象,其.current属性可以存储任何值,且在组件重新渲染时保持不变。这使得useRef成为存储可变值、缓存计算结果、保存定时器ID等场景的理想选择。
主要用途:
代码示例:
import { useRef, useState, useEffect, useCallback } from 'react';
// 1. 存储可变值(不触发重渲染)
function Counter() {
const [count, setCount] = useState(0);
const renderCount = useRef(0);
// 每次渲染时增加,但不会触发重渲染
renderCount.current += 1;
return (
<div>
<p>Count: {count}</p>
<p>Render times: {renderCount.current}</p>
<button onClick={() => setCount(c => c + 1)}>增加</button>
</div>
);
}
// 2. 保存定时器ID和清理
function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef(null);
const [isRunning, setIsRunning] = useState(false);
const startTimer = () => {
if (!isRunning) {
intervalRef.current = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
setIsRunning(true);
}
};
const stopTimer = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
setIsRunning(false);
}
};
const resetTimer = () => {
stopTimer();
setSeconds(0);
};
useEffect(() => {
// 组件卸载时清理定时器
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
return (
<div>
<p>Time: {seconds}s</p>
<button onClick={startTimer} disabled={isRunning}>开始</button>
<button onClick={stopTimer} disabled={!isRunning}>停止</button>
<button onClick={resetTimer}>重置</button>
</div>
);
}
// 3. 跟踪前一个值
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
function PreviousValueExample() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<div>
<p>当前值: {count}</p>
<p>前一个值: {prevCount}</p>
<button onClick={() => setCount(c => c + 1)}>增加</button>
</div>
);
}
// 4. 缓存昂贵计算的结果
function ExpensiveCalculation({ input }) {
const cacheRef = useRef(new Map());
const getResult = useCallback((input) => {
// 检查缓存
if (cacheRef.current.has(input)) {
console.log('从缓存获取结果');
return cacheRef.current.get(input);
}
// 模拟昂贵计算
console.log('执行昂贵计算');
const result = input * input * Math.random();
// 缓存结果
cacheRef.current.set(input, result);
return result;
}, []);
const result = getResult(input);
return <div>计算结果: {result.toFixed(2)}</div>;
}
// 5. 防抖Hook实现
function useDebounce(callback, delay) {
const timeoutRef = useRef(null);
return useCallback((...args) => {
// 清除之前的定时器
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// 设置新的定时器
timeoutRef.current = setTimeout(() => {
callback(...args);
}, delay);
}, [callback, delay]);
}
function SearchInput() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const debouncedSearch = useDebounce(async (searchQuery) => {
if (searchQuery) {
// 模拟API调用
const response = await fetch(`/api/search?q=${searchQuery}`);
const data = await response.json();
setResults(data);
}
}, 500);
const handleInputChange = (e) => {
const value = e.target.value;
setQuery(value);
debouncedSearch(value);
};
return (
<div>
<input
value={query}
onChange={handleInputChange}
placeholder="搜索..."
/>
<ul>
{results.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</div>
);
}
// 6. 存储组件实例数据
function ComponentInstance() {
const instanceRef = useRef({
id: Math.random().toString(36),
createdAt: new Date(),
callCount: 0
});
const handleClick = () => {
instanceRef.current.callCount += 1;
console.log(`组件 ${instanceRef.current.id} 被点击 ${instanceRef.current.callCount} 次`);
};
return (
<div>
<p>组件ID: {instanceRef.current.id}</p>
<p>创建时间: {instanceRef.current.createdAt.toLocaleString()}</p>
<button onClick={handleClick}>点击我</button>
</div>
);
}
// 7. 与第三方库集成
function ChartComponent({ data }) {
const chartRef = useRef(null);
const chartInstanceRef = useRef(null);
useEffect(() => {
if (chartRef.current && !chartInstanceRef.current) {
// 初始化图表库
chartInstanceRef.current = new SomeChartLibrary(chartRef.current);
}
if (chartInstanceRef.current) {
// 更新图表数据
chartInstanceRef.current.updateData(data);
}
}, [data]);
useEffect(() => {
return () => {
// 清理图表实例
if (chartInstanceRef.current) {
chartInstanceRef.current.destroy();
}
};
}, []);
return <div ref={chartRef} className="chart-container" />;
}
使用场景总结:
How to handle form validation in React?
How to handle form validation in React?
考察点:表单处理实践。
答案:
React中的表单验证可以通过多种方式实现,从简单的内置验证到复杂的第三方库集成。常见方法包括实时验证、提交时验证、以及结合表单库如React Hook Form或Formik进行高级表单管理。
验证策略:
代码示例:
import { useState, useReducer } from 'react';
// 1. 基础表单验证实现
function BasicFormValidation() {
const [formData, setFormData] = useState({
email: '',
password: '',
confirmPassword: ''
});
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
// 验证规则
const validateField = (name, value) => {
switch (name) {
case 'email':
return !value
? '邮箱不能为空'
: !/\S+@\S+\.\S+/.test(value)
? '邮箱格式不正确'
: '';
case 'password':
return !value
? '密码不能为空'
: value.length < 6
? '密码至少6位'
: '';
case 'confirmPassword':
return !value
? '请确认密码'
: value !== formData.password
? '密码不一致'
: '';
default:
return '';
}
};
// 处理输入变化
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// 实时验证
if (touched[name]) {
const error = validateField(name, value);
setErrors(prev => ({
...prev,
[name]: error
}));
}
};
// 处理失焦
const handleBlur = (e) => {
const { name, value } = e.target;
setTouched(prev => ({
...prev,
[name]: true
}));
const error = validateField(name, value);
setErrors(prev => ({
...prev,
[name]: error
}));
};
// 提交表单
const handleSubmit = (e) => {
e.preventDefault();
// 验证所有字段
const newErrors = {};
Object.keys(formData).forEach(name => {
const error = validateField(name, formData[name]);
if (error) newErrors[name] = error;
});
setErrors(newErrors);
setTouched(Object.keys(formData).reduce((acc, key) => {
acc[key] = true;
return acc;
}, {}));
if (Object.keys(newErrors).length === 0) {
console.log('表单提交:', formData);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
onBlur={handleBlur}
placeholder="邮箱"
/>
{touched.email && errors.email && (
<span className="error">{errors.email}</span>
)}
</div>
<div>
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
onBlur={handleBlur}
placeholder="密码"
/>
{touched.password && errors.password && (
<span className="error">{errors.password}</span>
)}
</div>
<div>
<input
name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
placeholder="确认密码"
/>
{touched.confirmPassword && errors.confirmPassword && (
<span className="error">{errors.confirmPassword}</span>
)}
</div>
<button type="submit">注册</button>
</form>
);
}
// 2. 使用useReducer管理复杂表单状态
const formReducer = (state, action) => {
switch (action.type) {
case 'SET_FIELD':
return {
...state,
values: {
...state.values,
[action.name]: action.value
}
};
case 'SET_ERROR':
return {
...state,
errors: {
...state.errors,
[action.name]: action.error
}
};
case 'SET_TOUCHED':
return {
...state,
touched: {
...state.touched,
[action.name]: true
}
};
case 'RESET':
return action.initialState;
default:
return state;
}
};
function AdvancedFormValidation() {
const initialState = {
values: { email: '', password: '' },
errors: {},
touched: {}
};
const [state, dispatch] = useReducer(formReducer, initialState);
// 自定义Hook:字段处理
const useField = (name) => {
return {
value: state.values[name] || '',
error: state.errors[name],
touched: state.touched[name],
onChange: (e) => {
dispatch({ type: 'SET_FIELD', name, value: e.target.value });
},
onBlur: () => {
dispatch({ type: 'SET_TOUCHED', name });
const error = validateField(name, state.values[name]);
dispatch({ type: 'SET_ERROR', name, error });
}
};
};
const email = useField('email');
const password = useField('password');
return (
<form>
<input
type="email"
placeholder="邮箱"
{...email}
/>
{email.touched && email.error && <span>{email.error}</span>}
<input
type="password"
placeholder="密码"
{...password}
/>
{password.touched && password.error && <span>{password.error}</span>}
</form>
);
}
// 3. 使用React Hook Form(推荐)
import { useForm } from 'react-hook-form';
function ReactHookFormExample() {
const {
register,
handleSubmit,
watch,
formState: { errors, isSubmitting }
} = useForm({
mode: 'onChange' // 实时验证
});
const watchPassword = watch('password');
const onSubmit = async (data) => {
// 提交逻辑
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('email', {
required: '邮箱不能为空',
pattern: {
value: /\S+@\S+\.\S+/,
message: '邮箱格式不正确'
}
})}
placeholder="邮箱"
/>
{errors.email && <span>{errors.email.message}</span>}
<input
type="password"
{...register('password', {
required: '密码不能为空',
minLength: {
value: 6,
message: '密码至少6位'
}
})}
placeholder="密码"
/>
{errors.password && <span>{errors.password.message}</span>}
<input
type="password"
{...register('confirmPassword', {
required: '请确认密码',
validate: value =>
value === watchPassword || '密码不一致'
})}
placeholder="确认密码"
/>
{errors.confirmPassword && <span>{errors.confirmPassword.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? '提交中...' : '提交'}
</button>
</form>
);
}
// 4. 异步验证示例
function AsyncValidationExample() {
const [usernameStatus, setUsernameStatus] = useState('');
const checkUsername = async (username) => {
if (username.length < 3) return;
setUsernameStatus('checking');
try {
const response = await fetch(`/api/check-username?username=${username}`);
const { available } = await response.json();
setUsernameStatus(available ? 'available' : 'taken');
} catch (error) {
setUsernameStatus('error');
}
};
const debouncedCheck = useCallback(
debounce(checkUsername, 500),
[]
);
return (
<div>
<input
onChange={(e) => {
setUsernameStatus('');
debouncedCheck(e.target.value);
}}
placeholder="用户名"
/>
{usernameStatus === 'checking' && <span>检查中...</span>}
{usernameStatus === 'available' && <span>✓ 可用</span>}
{usernameStatus === 'taken' && <span>✗ 已被占用</span>}
</div>
);
}
最佳实践:
In what situations should components be split?
考察点:组件设计原则。
答案:
组件拆分是React开发中的重要设计决策,遵循单一职责原则和关注点分离。合理的组件拆分能提高代码可维护性、可复用性和可测试性。拆分的时机通常基于功能复杂度、复用需求、性能考虑和团队协作等因素。
拆分时机:
代码示例:
// ❌ 拆分前:单一大组件承担过多职责
function UserProfilePage() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [followers, setFollowers] = useState([]);
const [isEditing, setIsEditing] = useState(false);
const [editForm, setEditForm] = useState({});
const [loading, setLoading] = useState(false);
useEffect(() => {
// 获取用户信息
fetchUserData();
fetchUserPosts();
fetchUserFollowers();
}, []);
const fetchUserData = async () => {
// 用户数据获取逻辑
};
const handleEditProfile = () => {
// 编辑资料逻辑
};
const handlePostLike = (postId) => {
// 点赞逻辑
};
const handleFollow = (userId) => {
// 关注逻辑
};
// 200+ 行的渲染逻辑
return (
<div>
{/* 用户信息展示 */}
{/* 帖子列表 */}
{/* 关注者列表 */}
{/* 编辑表单 */}
</div>
);
}
// ✅ 拆分后:按功能职责拆分为多个组件
// 1. 用户信息组件
function UserInfo({ user, onEdit, isEditing }) {
if (isEditing) {
return <UserEditForm user={user} onSave={onEdit} />;
}
return (
<div className="user-info">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.bio}</p>
<button onClick={() => onEdit(user)}>编辑资料</button>
</div>
);
}
// 2. 用户编辑表单组件
function UserEditForm({ user, onSave, onCancel }) {
const [formData, setFormData] = useState(user);
const handleSubmit = (e) => {
e.preventDefault();
onSave(formData);
};
return (
<form onSubmit={handleSubmit}>
<input
value={formData.name}
onChange={(e) => setFormData(prev => ({
...prev,
name: e.target.value
}))}
/>
<textarea
value={formData.bio}
onChange={(e) => setFormData(prev => ({
...prev,
bio: e.target.value
}))}
/>
<button type="submit">保存</button>
<button type="button" onClick={onCancel}>取消</button>
</form>
);
}
// 3. 帖子列表组件
function PostList({ posts, onLike }) {
return (
<div className="post-list">
{posts.map(post => (
<PostItem
key={post.id}
post={post}
onLike={onLike}
/>
))}
</div>
);
}
// 4. 单个帖子组件
function PostItem({ post, onLike }) {
const [isLiked, setIsLiked] = useState(post.isLiked);
const [likeCount, setLikeCount] = useState(post.likeCount);
const handleLike = async () => {
setIsLiked(!isLiked);
setLikeCount(prev => isLiked ? prev - 1 : prev + 1);
try {
await onLike(post.id);
} catch (error) {
// 回滚状态
setIsLiked(isLiked);
setLikeCount(post.likeCount);
}
};
return (
<article className="post-item">
<h3>{post.title}</h3>
<p>{post.content}</p>
<button
className={`like-btn ${isLiked ? 'liked' : ''}`}
onClick={handleLike}
>
❤️ {likeCount}
</button>
</article>
);
}
// 5. 关注者列表组件
function FollowersList({ followers, onFollow }) {
return (
<div className="followers-list">
<h3>关注者</h3>
{followers.map(follower => (
<FollowerItem
key={follower.id}
follower={follower}
onFollow={onFollow}
/>
))}
</div>
);
}
// 6. 重构后的主组件
function UserProfilePage() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [followers, setFollowers] = useState([]);
const [isEditing, setIsEditing] = useState(false);
// 自定义Hook提取数据获取逻辑
const { loading, error } = useUserData(setUser, setPosts, setFollowers);
const handleEditProfile = (userData) => {
setUser(userData);
setIsEditing(false);
};
const handlePostLike = async (postId) => {
// 点赞API调用
await likePost(postId);
};
const handleFollow = async (userId) => {
// 关注API调用
await followUser(userId);
};
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return (
<div className="user-profile-page">
<UserInfo
user={user}
onEdit={handleEditProfile}
isEditing={isEditing}
/>
<PostList
posts={posts}
onLike={handlePostLike}
/>
<FollowersList
followers={followers}
onFollow={handleFollow}
/>
</div>
);
}
// 7. 自定义Hook提取逻辑
function useUserData(setUser, setPosts, setFollowers) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const [userData, postsData, followersData] = await Promise.all([
fetchUser(),
fetchUserPosts(),
fetchUserFollowers()
]);
setUser(userData);
setPosts(postsData);
setFollowers(followersData);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [setUser, setPosts, setFollowers]);
return { loading, error };
}
拆分原则:
拆分策略:
What is the difference between createRoot and render in React 18?
What is the difference between createRoot and render in React 18?
考察点:渲染API变化。
答案:
React 18引入了新的createRoot API来替代传统的ReactDOM.render,这个变化支持了React 18的并发特性和自动批处理。createRoot提供了更好的性能和新功能支持,而旧的render API虽然仍可使用但会进入legacy模式,无法享受React 18的新特性。
主要区别:
代码示例:
import { createRoot } from 'react-dom/client';
import { hydrateRoot } from 'react-dom/client';
// ❌ React 17及之前的方式(Legacy模式)
import ReactDOM from 'react-dom';
function LegacyRendering() {
const container = document.getElementById('root');
// 旧的渲染方式
ReactDOM.render(<App />, container);
// 卸载
ReactDOM.unmountComponentAtNode(container);
// 服务端渲染水合
ReactDOM.hydrate(<App />, container);
}
// ✅ React 18推荐方式(并发模式)
function ModernRendering() {
const container = document.getElementById('root');
// 创建root
const root = createRoot(container);
// 渲染应用
root.render(<App />);
// 卸载应用
root.unmount();
}
// ✅ 服务端渲染的新方式
function SSRRendering() {
const container = document.getElementById('root');
// 水合应用
const root = hydrateRoot(container, <App />);
// 后续可以重新渲染
root.render(<App newProp="value" />);
}
// 并发特性演示
function App() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([]);
// React 18的自动批处理演示
const handleClick = () => {
// 在createRoot中,这些更新会被自动批处理
setCount(c => c + 1);
setItems(prev => [...prev, `Item ${prev.length + 1}`]);
// 即使在异步操作中也会批处理
setTimeout(() => {
setCount(c => c + 1);
setItems(prev => [...prev, `Async Item ${prev.length + 1}`]);
}, 100);
};
// 使用startTransition标记非紧急更新
const [isPending, startTransition] = useTransition();
const handleExpensiveUpdate = () => {
startTransition(() => {
// 大量计算的低优先级更新
const newItems = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);
setItems(newItems);
});
};
return (
<div>
<h1>React 18 并发特性演示</h1>
<p>Count: {count}</p>
<button onClick={handleClick}>触发批处理更新</button>
<button onClick={handleExpensiveUpdate}>
{isPending ? '更新中...' : '触发大量更新'}
</button>
<Suspense fallback={<div>Loading...</div>}>
<ItemList items={items} />
</Suspense>
</div>
);
}
// 使用useDeferredValue优化渲染
function ItemList({ items }) {
const deferredItems = useDeferredValue(items);
return (
<ul>
{deferredItems.slice(0, 100).map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
// 迁移指南和兼容性处理
function MigrationExample() {
// 检测React版本并使用合适的API
const renderApp = (App, container) => {
if (typeof createRoot === 'function') {
// React 18+
const root = createRoot(container);
root.render(<App />);
return () => root.unmount();
} else {
// React 17-
ReactDOM.render(<App />, container);
return () => ReactDOM.unmountComponentAtNode(container);
}
};
return { renderApp };
}
// 错误边界在新API下的使用
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h2>Something went wrong.</h2>;
}
return this.props.children;
}
}
// 完整的应用启动示例
function startApplication() {
const container = document.getElementById('root');
if (!container) {
throw new Error('Root container not found');
}
// 创建root并渲染应用
const root = createRoot(container);
root.render(
<StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</StrictMode>
);
// 开发环境下的热更新支持
if (process.env.NODE_ENV === 'development' && module.hot) {
module.hot.accept('./App', () => {
const NextApp = require('./App').default;
root.render(
<StrictMode>
<ErrorBoundary>
<NextApp />
</ErrorBoundary>
</StrictMode>
);
});
}
// 返回清理函数
return () => {
root.unmount();
};
}
迁移建议:
注意事项:
How to implement conditional styles in React?
How to implement conditional styles in React?
考察点:动态样式处理。
答案:
React中实现条件样式有多种方法,包括内联样式、CSS类名切换、CSS-in-JS库、CSS模块等。选择合适的方法取决于项目需求、团队偏好和性能考虑。最常用的是通过条件逻辑动态设置className或style属性。
实现方法:
代码示例:
import { useState } from 'react';
import classNames from 'classnames';
import styled from 'styled-components';
// 1. 基础条件样式实现
function BasicConditionalStyles() {
const [isActive, setIsActive] = useState(false);
const [theme, setTheme] = useState('light');
const [status, setStatus] = useState('default'); // 'default', 'success', 'error'
return (
<div>
{/* 方法1: 三元运算符设置className */}
<button
className={isActive ? 'btn btn-active' : 'btn btn-inactive'}
onClick={() => setIsActive(!isActive)}
>
{isActive ? '激活' : '未激活'}
</button>
{/* 方法2: 模板字符串拼接 */}
<div className={`container theme-${theme} ${isActive ? 'active' : ''}`}>
<p>当前主题: {theme}</p>
</div>
{/* 方法3: 内联样式条件设置 */}
<div
style={{
backgroundColor: isActive ? '#007bff' : '#6c757d',
color: isActive ? 'white' : 'black',
padding: '10px',
borderRadius: isActive ? '8px' : '0px',
transition: 'all 0.3s ease'
}}
>
动态内联样式
</div>
{/* 方法4: 对象形式的条件className */}
<div
className={classNames('card', {
'card-active': isActive,
'card-success': status === 'success',
'card-error': status === 'error',
[`theme-${theme}`]: true
})}
>
使用classNames库
</div>
</div>
);
}
// 2. 高级条件样式模式
function AdvancedConditionalStyles() {
const [loading, setLoading] = useState(false);
const [progress, setProgress] = useState(0);
const [size, setSize] = useState('medium'); // 'small', 'medium', 'large'
// CSS变量动态设置
const dynamicStyles = {
'--progress': `${progress}%`,
'--button-size': size === 'small' ? '32px' : size === 'large' ? '56px' : '40px'
};
return (
<div style={dynamicStyles}>
{/* 进度条样式 */}
<div className="progress-container">
<div
className="progress-bar"
style={{ width: `${progress}%` }}
/>
</div>
{/* 复合条件样式 */}
<button
className={classNames('btn', {
'btn-loading': loading,
'btn-small': size === 'small',
'btn-large': size === 'large',
'btn-disabled': progress === 100
})}
disabled={loading || progress === 100}
onClick={() => setLoading(!loading)}
>
{loading ? (
<>
<span className="spinner" />
加载中...
</>
) : (
'点击按钮'
)}
</button>
</div>
);
}
// 3. CSS-in-JS实现(styled-components)
const StyledButton = styled.button`
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
/* 条件样式通过props */
background-color: ${props =>
props.$variant === 'primary' ? '#007bff' :
props.$variant === 'danger' ? '#dc3545' :
'#6c757d'
};
color: ${props => props.$variant ? 'white' : 'black'};
/* 状态样式 */
opacity: ${props => props.disabled ? 0.6 : 1};
transform: ${props => props.$isPressed ? 'scale(0.95)' : 'scale(1)'};
/* 尺寸变体 */
${props => props.$size === 'large' && `
padding: 16px 32px;
font-size: 18px;
`}
${props => props.$size === 'small' && `
padding: 8px 16px;
font-size: 14px;
`}
/* 悬停效果 */
&:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
`;
function StyledComponentExample() {
const [variant, setVariant] = useState('primary');
const [size, setSize] = useState('medium');
const [isPressed, setIsPressed] = useState(false);
return (
<div>
<StyledButton
$variant={variant}
$size={size}
$isPressed={isPressed}
onMouseDown={() => setIsPressed(true)}
onMouseUp={() => setIsPressed(false)}
onMouseLeave={() => setIsPressed(false)}
>
Styled Button
</StyledButton>
<div>
<select onChange={(e) => setVariant(e.target.value)}>
<option value="primary">Primary</option>
<option value="danger">Danger</option>
<option value="default">Default</option>
</select>
</div>
</div>
);
}
// 4. CSS模块化条件样式
import styles from './Component.module.css';
function CSSModulesExample() {
const [status, setStatus] = useState('idle'); // 'idle', 'loading', 'success', 'error'
return (
<div className={styles.container}>
<div
className={classNames(styles.card, {
[styles.loading]: status === 'loading',
[styles.success]: status === 'success',
[styles.error]: status === 'error'
})}
>
<h3 className={styles.title}>状态卡片</h3>
<p className={styles.content}>当前状态: {status}</p>
</div>
</div>
);
}
// 5. 响应式条件样式Hook
function useResponsiveStyles() {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWindowWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const getResponsiveStyles = (mobileStyle, tabletStyle, desktopStyle) => {
if (windowWidth < 768) return mobileStyle;
if (windowWidth < 1024) return tabletStyle;
return desktopStyle;
};
return { windowWidth, getResponsiveStyles };
}
function ResponsiveComponent() {
const { getResponsiveStyles } = useResponsiveStyles();
return (
<div
style={getResponsiveStyles(
{ fontSize: '14px', padding: '8px' }, // mobile
{ fontSize: '16px', padding: '12px' }, // tablet
{ fontSize: '18px', padding: '16px' } // desktop
)}
>
响应式组件
</div>
);
}
// 6. 主题系统实现
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const themeStyles = {
light: {
background: '#ffffff',
color: '#333333',
border: '#e0e0e0'
},
dark: {
background: '#1a1a1a',
color: '#ffffff',
border: '#333333'
}
};
return (
<div
className={`theme-${theme}`}
style={themeStyles[theme]}
data-theme={theme}
>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
切换主题
</button>
{children}
</div>
);
};
CSS文件示例:
/* Component.module.css */
.container {
padding: 20px;
}
.card {
padding: 16px;
border-radius: 8px;
border: 1px solid #ddd;
transition: all 0.3s ease;
}
.card.loading {
background-color: #fff3cd;
border-color: #ffeaa7;
}
.card.success {
background-color: #d4edda;
border-color: #00b894;
}
.card.error {
background-color: #f8d7da;
border-color: #e74c3c;
}
/* 全局主题样式 */
.theme-light {
--bg-primary: #ffffff;
--text-primary: #333333;
}
.theme-dark {
--bg-primary: #1a1a1a;
--text-primary: #ffffff;
}
最佳实践:
How does React 18's concurrent rendering optimize user experience?
How does React 18’s concurrent rendering optimize user experience?
考察点:并发渲染价值。
答案:
React 18的并发渲染通过时间切片和优先级调度,使React能够中断低优先级的渲染工作来处理更紧急的用户交互,从而保持应用的响应性。这种机制让用户界面在处理大量计算时仍能保持流畅,显著提升了用户体验。
优化机制:
代码示例:
import {
useState,
useTransition,
useDeferredValue,
startTransition,
Suspense,
memo
} from 'react';
// 1. 基础并发渲染演示
function ConcurrentRenderingDemo() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
// 紧急更新:用户输入,立即响应
const handleInputChange = (e) => {
setQuery(e.target.value);
// 非紧急更新:搜索结果,可以被中断
startTransition(() => {
const searchResults = performExpensiveSearch(e.target.value);
setResults(searchResults);
});
};
return (
<div>
<input
value={query}
onChange={handleInputChange}
placeholder="实时搜索..."
className={isPending ? 'input-pending' : ''}
/>
<div className="search-status">
{isPending ? '搜索中...' : `找到 ${results.length} 个结果`}
</div>
<SearchResults results={results} />
</div>
);
}
// 2. useDeferredValue优化大列表渲染
function DeferredListExample() {
const [filter, setFilter] = useState('');
const [items] = useState(generateLargeItemList(10000));
// 延迟值,不会阻塞用户输入
const deferredFilter = useDeferredValue(filter);
// 基于延迟值进行昂贵的过滤操作
const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(deferredFilter.toLowerCase())
);
}, [items, deferredFilter]);
const isStale = filter !== deferredFilter;
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="过滤大列表..."
/>
<div style={{ opacity: isStale ? 0.5 : 1, transition: 'opacity 0.2s' }}>
<VirtualizedList items={filteredItems} />
</div>
</div>
);
}
// 3. 复杂交互场景优化
function ComplexInteractionExample() {
const [selectedTab, setSelectedTab] = useState('dashboard');
const [dashboardData, setDashboardData] = useState(null);
const [analyticsData, setAnalyticsData] = useState(null);
const [isPending, startTransition] = useTransition();
// 标签切换:立即更新UI,异步加载数据
const handleTabSwitch = (tabName) => {
setSelectedTab(tabName); // 立即更新,保证UI响应
startTransition(() => {
// 异步数据加载,不阻塞标签切换
loadTabData(tabName).then(data => {
if (tabName === 'dashboard') {
setDashboardData(data);
} else if (tabName === 'analytics') {
setAnalyticsData(data);
}
});
});
};
return (
<div>
<div className="tab-nav">
{['dashboard', 'analytics', 'settings'].map(tab => (
<button
key={tab}
className={`tab ${selectedTab === tab ? 'active' : ''}`}
onClick={() => handleTabSwitch(tab)}
>
{tab}
{isPending && selectedTab === tab && (
<span className="loading-indicator">⟳</span>
)}
</button>
))}
</div>
<div className="tab-content">
<Suspense fallback={<TabContentSkeleton />}>
{selectedTab === 'dashboard' && (
<Dashboard data={dashboardData} />
)}
{selectedTab === 'analytics' && (
<Analytics data={analyticsData} />
)}
{selectedTab === 'settings' && (
<Settings />
)}
</Suspense>
</div>
</div>
);
}
// 4. 渐进式加载优化
function ProgressiveLoadingExample() {
const [posts, setPosts] = useState([]);
const [page, setPage] = useState(1);
const [isPending, startTransition] = useTransition();
const loadMorePosts = () => {
startTransition(() => {
fetchPosts(page + 1).then(newPosts => {
setPosts(prevPosts => [...prevPosts, ...newPosts]);
setPage(prev => prev + 1);
});
});
};
return (
<div>
<PostList posts={posts} />
<button
onClick={loadMorePosts}
disabled={isPending}
className="load-more-btn"
>
{isPending ? '加载中...' : '加载更多'}
</button>
{isPending && (
<div className="loading-overlay">
<PostSkeleton count={3} />
</div>
)}
</div>
);
}
// 5. 性能监控和调试
function PerformanceMonitoringExample() {
const [heavyData, setHeavyData] = useState([]);
const [isPending, startTransition] = useTransition();
const handleHeavyOperation = () => {
console.time('Heavy Operation');
startTransition(() => {
// 模拟CPU密集型操作
const result = performHeavyCalculation();
setHeavyData(result);
console.timeEnd('Heavy Operation');
});
};
// 使用React DevTools Profiler监控
useEffect(() => {
if (window.performance && window.performance.mark) {
window.performance.mark('heavy-operation-start');
}
return () => {
if (window.performance && window.performance.mark) {
window.performance.mark('heavy-operation-end');
window.performance.measure(
'heavy-operation',
'heavy-operation-start',
'heavy-operation-end'
);
}
};
}, [heavyData]);
return (
<div>
<button onClick={handleHeavyOperation}>
执行重计算 {isPending && '(处理中...)'}
</button>
<HeavyDataVisualization data={heavyData} />
</div>
);
}
// 6. 优化组件渲染
const SearchResults = memo(({ results }) => {
return (
<div className="results">
{results.map(result => (
<SearchResultItem key={result.id} result={result} />
))}
</div>
);
});
const SearchResultItem = memo(({ result }) => {
return (
<div className="result-item">
<h3>{result.title}</h3>
<p>{result.description}</p>
</div>
);
});
// 辅助函数
function performExpensiveSearch(query) {
// 模拟昂贵的搜索操作
const items = Array.from({ length: 1000 }, (_, i) => ({
id: i,
title: `Item ${i}`,
description: `Description for item ${i}`
}));
return items.filter(item =>
item.title.toLowerCase().includes(query.toLowerCase())
);
}
function generateLargeItemList(count) {
return Array.from({ length: count }, (_, i) => ({
id: i,
name: `Item ${i}`,
category: `Category ${i % 10}`
}));
}
async function loadTabData(tabName) {
// 模拟异步数据加载
await new Promise(resolve => setTimeout(resolve, 1000));
return { tabName, data: `Data for ${tabName}` };
}
用户体验提升:
最佳实践:
What are the use cases and considerations for useMemo and useCallback?
What are the use cases and considerations for useMemo and useCallback?
考察点:性能优化策略。
答案:
useMemo和useCallback是React中的性能优化Hook,用于避免不必要的计算和重新渲染。useMemo缓存计算结果,useCallback缓存函数引用。正确使用它们可以显著提升应用性能,但过度使用反而可能造成性能负担。
使用场景对比:
useMemo使用场景:
useCallback使用场景:
代码示例:
import { useState, useMemo, useCallback, memo, useEffect } from 'react';
// 1. useMemo基础用法演示
function UseMemoExample() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([]);
const [filter, setFilter] = useState('');
// ❌ 错误:简单计算不需要useMemo
const simpleValue = useMemo(() => count * 2, [count]); // 过度优化
// ✅ 正确:昂贵计算使用useMemo
const expensiveValue = useMemo(() => {
console.log('执行昂贵计算...');
let result = 0;
for (let i = 0; i < count * 1000000; i++) {
result += Math.random();
}
return result;
}, [count]);
// ✅ 正确:列表过滤缓存
const filteredItems = useMemo(() => {
console.log('过滤列表...');
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// ✅ 正确:对象引用稳定化
const chartConfig = useMemo(() => ({
type: 'line',
data: filteredItems,
options: {
responsive: true,
plugins: {
legend: { position: 'top' }
}
}
}), [filteredItems]);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="过滤项目..."
/>
<p>昂贵计算结果: {expensiveValue.toFixed(2)}</p>
<Chart config={chartConfig} />
<ItemList items={filteredItems} />
</div>
);
}
// 2. useCallback基础用法演示
function UseCallbackExample() {
const [count, setCount] = useState(0);
const [users, setUsers] = useState([]);
// ❌ 错误:每次渲染都创建新函数
const handleClick = () => {
setCount(count + 1);
};
// ✅ 正确:使用useCallback缓存函数
const handleIncrement = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // 空依赖数组,函数永远不变
// ✅ 正确:带依赖的useCallback
const handleUserAction = useCallback((userId, action) => {
console.log(`User ${userId} performed ${action}`);
// 根据当前用户列表执行操作
setUsers(prevUsers =>
prevUsers.map(user =>
user.id === userId
? { ...user, lastAction: action }
: user
)
);
}, []);
// ✅ 正确:复杂依赖的useCallback
const handleSearch = useCallback(async (query) => {
const results = await searchUsers(query);
setUsers(results);
}, []); // 不依赖外部变量
return (
<div>
<button onClick={handleIncrement}>
Count: {count}
</button>
<UserList
users={users}
onUserAction={handleUserAction}
onSearch={handleSearch}
/>
</div>
);
}
// 3. 组合使用示例
function OptimizedComponent() {
const [data, setData] = useState([]);
const [sortBy, setSortBy] = useState('name');
const [searchTerm, setSearchTerm] = useState('');
// 搜索和排序的复合操作
const processedData = useMemo(() => {
let result = data.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
result.sort((a, b) => {
if (sortBy === 'name') return a.name.localeCompare(b.name);
if (sortBy === 'date') return new Date(b.date) - new Date(a.date);
return 0;
});
return result;
}, [data, searchTerm, sortBy]);
// 缓存事件处理器
const handleSort = useCallback((newSortBy) => {
setSortBy(newSortBy);
}, []);
const handleSearch = useCallback((term) => {
setSearchTerm(term);
}, []);
const handleItemClick = useCallback((item) => {
console.log('Clicked item:', item);
}, []);
return (
<div>
<SearchBar onSearch={handleSearch} />
<SortControls sortBy={sortBy} onSort={handleSort} />
<DataTable
data={processedData}
onItemClick={handleItemClick}
/>
</div>
);
}
// 4. 性能对比演示
function PerformanceComparison() {
const [count, setCount] = useState(0);
const [items, setItems] = useState(Array.from({length: 1000}, (_, i) => ({
id: i,
name: `Item ${i}`,
value: Math.random()
})));
// 未优化版本
const UnoptimizedChild = ({ items, onItemClick }) => {
console.log('UnoptimizedChild rendered');
return (
<div>
{items.slice(0, 10).map(item => (
<div key={item.id} onClick={() => onItemClick(item)}>
{item.name}
</div>
))}
</div>
);
};
// 优化版本
const OptimizedChild = memo(({ items, onItemClick }) => {
console.log('OptimizedChild rendered');
return (
<div>
{items.slice(0, 10).map(item => (
<div key={item.id} onClick={() => onItemClick(item)}>
{item.name}
</div>
))}
</div>
);
});
// 未缓存的处理函数
const handleItemClickUncached = (item) => {
console.log('Clicked:', item.name);
};
// 缓存的处理函数
const handleItemClickCached = useCallback((item) => {
console.log('Clicked:', item.name);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
<h3>未优化(每次都重新渲染):</h3>
<UnoptimizedChild
items={items}
onItemClick={handleItemClickUncached}
/>
<h3>已优化(仅在items变化时重新渲染):</h3>
<OptimizedChild
items={items}
onItemClick={handleItemClickCached}
/>
</div>
);
}
// 5. 自定义Hook中的优化
function useOptimizedData(initialData) {
const [data, setData] = useState(initialData);
const [loading, setLoading] = useState(false);
// 缓存数据获取函数
const fetchData = useCallback(async (params) => {
setLoading(true);
try {
const result = await apiCall(params);
setData(result);
} finally {
setLoading(false);
}
}, []);
// 缓存数据处理函数
const processData = useCallback((processor) => {
setData(prevData => processor(prevData));
}, []);
// 缓存返回对象
return useMemo(() => ({
data,
loading,
fetchData,
processData
}), [data, loading, fetchData, processData]);
}
// 6. 常见陷阱和解决方案
function CommonPitfalls() {
const [count, setCount] = useState(0);
// ❌ 陷阱1:依赖项不完整
const incorrectMemo = useMemo(() => {
return someExpensiveOperation(count);
}, []); // 缺少count依赖
// ✅ 修正:包含所有依赖项
const correctMemo = useMemo(() => {
return someExpensiveOperation(count);
}, [count]);
// ❌ 陷阱2:依赖项过多导致频繁更新
const overDependentMemo = useMemo(() => {
return simpleOperation();
}, [count, /* 其他很多依赖 */]);
// ✅ 修正:拆分或重构减少依赖
const betterMemo = useMemo(() => {
return simpleOperation();
}, []); // 如果真的不依赖外部状态
// ❌ 陷阱3:在依赖数组中使用对象
const config = { threshold: 10 };
const problematicCallback = useCallback(() => {
processWithConfig(config);
}, [config]); // config每次都是新对象
// ✅ 修正:使用稳定的引用或拆解依赖
const threshold = 10;
const fixedCallback = useCallback(() => {
processWithConfig({ threshold });
}, [threshold]);
return null;
}
注意事项和最佳实践:
何时使用:
何时不用:
性能考虑:
When to use startTransition? What is its working principle?
When to use startTransition? What is its working principle?
考察点:优先级调度。
答案:
startTransition是React 18引入的并发特性API,用于标记非紧急的状态更新,让React能够优先处理用户交互等紧急更新。它通过降低更新优先级来避免阻塞关键的用户交互,实现更流畅的用户体验。
使用场景:
工作原理:
startTransition将更新标记为"transition",React会以较低优先级处理这些更新,当有紧急更新(如用户输入)时,会暂停transition更新,优先处理紧急任务,确保UI响应性。
代码示例:
import { useState, startTransition, useTransition } from 'react';
// 1. 基础使用 - 搜索场景优化
function SearchExample() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleInputChange = (e) => {
const value = e.target.value;
// 紧急更新:立即更新输入框显示
setQuery(value);
// 非紧急更新:搜索结果可以延后
startTransition(() => {
// 这个更新可以被中断,不会阻塞输入
const searchResults = performSearch(value);
setResults(searchResults);
});
};
return (
<div>
<input
value={query}
onChange={handleInputChange}
placeholder="搜索..."
/>
{isPending && <div className="search-loading">搜索中...</div>}
<SearchResults results={results} />
</div>
);
}
// 2. 复杂数据处理场景
function DataVisualizationExample() {
const [rawData, setRawData] = useState([]);
const [chartType, setChartType] = useState('line');
const [processedData, setProcessedData] = useState([]);
const [isPending, startTransition] = useTransition();
const handleDataUpdate = (newData) => {
// 立即更新原始数据状态
setRawData(newData);
// 数据处理和图表更新使用transition
startTransition(() => {
const processed = processDataForChart(newData, chartType);
setProcessedData(processed);
});
};
const handleChartTypeChange = (newType) => {
// 立即更新图表类型选择
setChartType(newType);
// 重新处理数据使用transition
startTransition(() => {
const processed = processDataForChart(rawData, newType);
setProcessedData(processed);
});
};
return (
<div>
<div className="chart-controls">
<select
value={chartType}
onChange={(e) => handleChartTypeChange(e.target.value)}
>
<option value="line">折线图</option>
<option value="bar">柱状图</option>
<option value="pie">饼图</option>
</select>
{isPending && <span className="processing">处理中...</span>}
</div>
<Chart
data={processedData}
type={chartType}
loading={isPending}
/>
</div>
);
}
// 3. 列表管理优化
function ListManagementExample() {
const [items, setItems] = useState(generateLargeList(10000));
const [filter, setFilter] = useState('');
const [sortBy, setSortBy] = useState('name');
const [displayItems, setDisplayItems] = useState(items);
const [isPending, startTransition] = useTransition();
// 过滤和排序的复合操作
const updateDisplayItems = (newFilter, newSortBy) => {
startTransition(() => {
let result = items;
// 应用过滤
if (newFilter) {
result = result.filter(item =>
item.name.toLowerCase().includes(newFilter.toLowerCase())
);
}
// 应用排序
result.sort((a, b) => {
if (newSortBy === 'name') return a.name.localeCompare(b.name);
if (newSortBy === 'date') return new Date(b.date) - new Date(a.date);
if (newSortBy === 'popularity') return b.popularity - a.popularity;
return 0;
});
setDisplayItems(result);
});
};
const handleFilterChange = (e) => {
const newFilter = e.target.value;
setFilter(newFilter);
updateDisplayItems(newFilter, sortBy);
};
const handleSortChange = (newSortBy) => {
setSortBy(newSortBy);
updateDisplayItems(filter, newSortBy);
};
return (
<div>
<div className="list-controls">
<input
value={filter}
onChange={handleFilterChange}
placeholder="过滤项目..."
/>
<select value={sortBy} onChange={(e) => handleSortChange(e.target.value)}>
<option value="name">按名称排序</option>
<option value="date">按日期排序</option>
<option value="popularity">按热度排序</option>
</select>
{isPending && <div className="list-loading">更新中...</div>}
</div>
<VirtualizedList
items={displayItems}
loading={isPending}
/>
</div>
);
}
// 4. 路由切换优化
function RouterTransitionExample() {
const [currentPage, setCurrentPage] = useState('home');
const [pageData, setPageData] = useState(null);
const [isPending, startTransition] = useTransition();
const navigateToPage = (pageName) => {
// 立即更新当前页面状态(URL变化)
setCurrentPage(pageName);
// 页面数据加载使用transition
startTransition(() => {
loadPageData(pageName).then(data => {
setPageData(data);
});
});
};
return (
<div>
<nav className="navigation">
{['home', 'about', 'products', 'contact'].map(page => (
<button
key={page}
className={currentPage === page ? 'active' : ''}
onClick={() => navigateToPage(page)}
>
{page}
{isPending && currentPage === page && ' ⟳'}
</button>
))}
</nav>
<main className="page-content">
{isPending ? (
<PageSkeleton />
) : (
<PageComponent page={currentPage} data={pageData} />
)}
</main>
</div>
);
}
// 5. 优雅降级和错误处理
function TransitionWithFallback() {
const [data, setData] = useState([]);
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const [error, setError] = useState(null);
const handleSearch = (searchQuery) => {
setQuery(searchQuery);
setError(null);
startTransition(() => {
try {
// 可能失败的异步操作
performAsyncSearch(searchQuery)
.then(results => {
setData(results);
})
.catch(err => {
setError(err);
// 降级到之前的数据或空状态
setData([]);
});
} catch (err) {
setError(err);
}
});
};
return (
<div>
<SearchInput onSearch={handleSearch} />
{error && (
<div className="error-message">
搜索失败: {error.message}
</div>
)}
{isPending && (
<div className="search-pending">
正在搜索 "{query}"...
</div>
)}
<SearchResults data={data} />
</div>
);
}
// 6. 性能监控和调试
function TransitionPerformanceExample() {
const [items, setItems] = useState([]);
const [isPending, startTransition] = useTransition();
const handleExpensiveOperation = () => {
console.time('Expensive Operation');
startTransition(() => {
// 模拟CPU密集型操作
const processedItems = performExpensiveProcessing();
setItems(processedItems);
console.timeEnd('Expensive Operation');
// 使用Performance API记录
if (window.performance && window.performance.mark) {
window.performance.mark('transition-complete');
}
});
};
// 监控transition状态变化
useEffect(() => {
if (isPending) {
console.log('Transition started');
} else {
console.log('Transition completed');
}
}, [isPending]);
return (
<div>
<button onClick={handleExpensiveOperation}>
执行昂贵操作 {isPending && '(进行中...)'}
</button>
<ItemGrid items={items} />
</div>
);
}
// 辅助函数
function performSearch(query) {
// 模拟搜索操作
return Array.from({ length: 1000 }, (_, i) => ({
id: i,
title: `Result ${i}`,
matches: Math.random() > 0.5
})).filter(item => item.title.includes(query));
}
function processDataForChart(data, chartType) {
// 模拟数据处理
console.log(`Processing data for ${chartType} chart`);
return data.map(item => ({
...item,
processed: true,
chartType
}));
}
async function loadPageData(pageName) {
// 模拟异步数据加载
await new Promise(resolve => setTimeout(resolve, 1000));
return { page: pageName, content: `Content for ${pageName}` };
}
使用原则:
注意事项:
How does Suspense's fallback mechanism handle async components?
How does Suspense’s fallback mechanism handle async components?
考察点:异步边界处理。
答案:
Suspense的fallback机制是React处理异步组件加载的核心特性,它通过捕获Promise抛出来识别异步操作,在异步内容加载期间显示fallback UI,加载完成后自动切换到实际内容。这种机制让开发者能够优雅地处理代码分割、数据获取等异步场景,提供更好的用户体验。
工作原理:
主要应用场景:
代码分割加载:
import { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
);
}
数据获取场景:
function ProfilePage({ userId }) {
return (
<Suspense fallback={<ProfileSkeleton />}>
<ProfileDetails userId={userId} />
<Suspense fallback={<PostsLoading />}>
<ProfileTimeline userId={userId} />
</Suspense>
</Suspense>
);
}
嵌套Suspense边界:
function AppLayout() {
return (
<Suspense fallback={<AppSkeleton />}>
<Header />
<Suspense fallback={<ContentSkeleton />}>
<MainContent />
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</Suspense>
</Suspense>
);
}
最佳实践:
What problems does useId solve in SSR scenarios?
What problems does useId solve in SSR scenarios?
考察点:服务端渲染兼容。
答案:
useId是React 18引入的Hook,专门用于生成稳定的唯一标识符,主要解决SSR场景下客户端与服务端ID不一致导致的hydration错误问题。它确保服务端渲染和客户端hydration过程中使用相同的ID,避免因随机ID导致的DOM结构不匹配。
核心问题解决:
Hydration ID不匹配:
// 问题代码 - 每次渲染生成不同ID
function BadExample() {
const id = Math.random().toString(36); // 服务端和客户端不一致
return <input id={id} />;
}
// 解决方案 - 使用useId
function GoodExample() {
const id = useId(); // 服务端和客户端保持一致
return <input id={id} />;
}
表单标签关联:
function FormField({ label, children }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
{React.cloneElement(children, { id })}
</div>
);
}
使用场景:
无障碍性支持:
function AccessibleInput({ label, description }) {
const inputId = useId();
const descriptionId = useId();
return (
<div>
<label htmlFor={inputId}>{label}</label>
<input
id={inputId}
aria-describedby={descriptionId}
/>
<p id={descriptionId}>{description}</p>
</div>
);
}
组件库开发:
function Modal({ children }) {
const titleId = useId();
const descriptionId = useId();
return (
<div
role="dialog"
aria-labelledby={titleId}
aria-describedby={descriptionId}
>
<h2 id={titleId}>标题</h2>
<div id={descriptionId}>{children}</div>
</div>
);
}
与传统方案对比:
最佳实践:
What impact does React 18's automatic batching have on performance?
What impact does React 18’s automatic batching have on performance?
考察点:批处理性能优化。
答案:
React 18的自动批处理显著提升了应用性能,通过将多个状态更新合并为单次渲染来减少不必要的重渲染次数。相比React 17只在事件处理器中批处理,React 18扩展到Promise、setTimeout等异步操作中,实现了更全面的性能优化。
性能提升机制:
减少渲染次数:
// React 17 - 会触发3次渲染
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
setName('new name');
}
// React 18 - 只触发1次渲染(自动批处理)
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
setName('new name');
// 三个更新会被批处理为一次渲染
}
异步操作中的批处理:
// React 17 - 异步操作中每次都会重渲染
function handleAsync() {
setTimeout(() => {
setCount(c => c + 1); // 触发渲染
setFlag(f => !f); // 再次触发渲染
}, 1000);
}
// React 18 - 异步操作中也会批处理
function handleAsync() {
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// 两次更新批处理为一次渲染
}, 1000);
}
性能对比分析:
渲染性能提升:
function PerformanceComparison() {
const [state1, setState1] = useState(0);
const [state2, setState2] = useState(0);
const [state3, setState3] = useState(0);
// 性能监控
useEffect(() => {
console.log('Component rendered');
});
const handleMultipleUpdates = () => {
// React 18: 只会打印一次 "Component rendered"
setState1(prev => prev + 1);
setState2(prev => prev + 1);
setState3(prev => prev + 1);
};
return (
<div onClick={handleMultipleUpdates}>
{state1} - {state2} - {state3}
</div>
);
}
Promise链中的批处理:
function AsyncBatchingExample() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const fetchData = async () => {
try {
setLoading(true);
const result = await api.getData();
// React 18: 以下三个更新会被批处理
setLoading(false);
setData(result);
setError(null);
} catch (err) {
setLoading(false);
setError(err);
setData(null);
}
};
return (
<div>
{loading && <Spinner />}
{error && <Error message={error.message} />}
{data && <DataDisplay data={data} />}
</div>
);
}
控制批处理行为:
强制同步更新:
import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCount(c => c + 1);
});
// 强制立即渲染
flushSync(() => {
setFlag(f => !f);
});
// 再次强制立即渲染
}
批处理与并发特性结合:
function ConcurrentBatching() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleSearch = (value) => {
setQuery(value); // 紧急更新
startTransition(() => {
// 非紧急更新,会与其他transition更新批处理
setResults(performSearch(value));
});
};
return (
<div>
<input onChange={(e) => handleSearch(e.target.value)} />
{isPending ? <Loading /> : <Results data={results} />}
</div>
);
}
性能收益:
注意事项:
Which third-party state libraries is useSyncExternalStore suitable for?
Which third-party state libraries is useSyncExternalStore suitable for?
考察点:外部状态集成。
答案:
useSyncExternalStore是React 18专门为第三方状态管理库设计的Hook,用于安全地订阅外部数据源。它主要适用于需要与React并发特性兼容的状态库,解决了外部状态在并发渲染中可能出现的撕裂(tearing)问题。
适用的状态库类型:
Redux及其生态:
import { useSyncExternalStore } from 'react';
import { store } from './redux-store';
function useReduxState(selector) {
return useSyncExternalStore(
store.subscribe, // subscribe函数
() => selector(store.getState()), // getSnapshot
() => selector(store.getState()) // getServerSnapshot (SSR)
);
}
function Counter() {
const count = useReduxState(state => state.counter.value);
return <div>{count}</div>;
}
Zustand状态库:
import { create } from 'zustand';
import { useSyncExternalStore } from 'react';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
function useZustandWithSyncStore(selector) {
return useSyncExternalStore(
useStore.subscribe,
() => selector(useStore.getState()),
() => selector(useStore.getState())
);
}
MobX状态库:
import { observable } from 'mobx';
import { useSyncExternalStore } from 'react';
const store = observable({
count: 0,
increment() { this.count++; }
});
function useMobXStore() {
return useSyncExternalStore(
(callback) => {
const dispose = store.observe(callback);
return dispose;
},
() => store.count,
() => store.count
);
}
Valtio状态库:
import { proxy, subscribe, snapshot } from 'valtio';
import { useSyncExternalStore } from 'react';
const state = proxy({ count: 0 });
function useValtioState() {
return useSyncExternalStore(
(callback) => subscribe(state, callback),
() => snapshot(state),
() => snapshot(state)
);
}
自定义状态库实现:
// 创建一个简单的状态管理器
function createStore(initialState) {
let state = initialState;
const listeners = new Set();
return {
getState: () => state,
setState: (newState) => {
state = { ...state, ...newState };
listeners.forEach(listener => listener());
},
subscribe: (callback) => {
listeners.add(callback);
return () => listeners.delete(callback);
}
};
}
// 使用useSyncExternalStore连接自定义状态库
function useCustomStore(store, selector) {
return useSyncExternalStore(
store.subscribe,
() => selector(store.getState()),
() => selector(store.getState()) // SSR support
);
}
// 实际使用
const userStore = createStore({ name: 'John', age: 30 });
function UserProfile() {
const userName = useCustomStore(userStore, state => state.name);
const userAge = useCustomStore(userStore, state => state.age);
return (
<div>
<p>Name: {userName}</p>
<p>Age: {userAge}</p>
</div>
);
}
与浏览器API集成:
// localStorage状态同步
function useLocalStorageState(key, defaultValue) {
const subscribe = (callback) => {
window.addEventListener('storage', callback);
return () => window.removeEventListener('storage', callback);
};
const getSnapshot = () => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : defaultValue;
};
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
}
// 网络状态监听
function useOnlineStatus() {
const subscribe = (callback) => {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
};
return useSyncExternalStore(
subscribe,
() => navigator.onLine,
() => true // SSR默认为在线状态
);
}
主要优势:
使用注意事项:
How to use React.memo for precise component optimization?
How to use React.memo for precise component optimization?
考察点:精确优化策略。
答案:
React.memo是一个高阶组件,通过浅比较props来决定是否重新渲染组件,实现精确的性能优化。正确使用React.memo需要理解其比较机制、适用场景,并配合自定义比较函数来实现精确控制,避免不必要的重渲染。
基本用法和原理:
// 基础使用
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data, count }) {
console.log('ExpensiveComponent rendered');
return (
<div>
<h3>Count: {count}</h3>
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
});
// 父组件中使用
function Parent() {
const [count, setCount] = useState(0);
const [unrelatedState, setUnrelatedState] = useState(0);
const data = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
];
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
Increment Count: {count}
</button>
<button onClick={() => setUnrelatedState(s => s + 1)}>
Unrelated: {unrelatedState}
</button>
{/* 只有count变化时才重新渲染 */}
<ExpensiveComponent data={data} count={count} />
</div>
);
}
自定义比较函数实现精确控制:
// 1. 深度比较某个特定属性
const DeepCompareComponent = React.memo(
function DeepCompareComponent({ config, items }) {
return (
<div>
<h4>Config: {config.theme}</h4>
{items.map(item => <div key={item.id}>{item.name}</div>)}
</div>
);
},
(prevProps, nextProps) => {
// 自定义比较逻辑
if (prevProps.config.theme !== nextProps.config.theme) {
return false; // 需要重新渲染
}
// 深度比较items数组
if (prevProps.items.length !== nextProps.items.length) {
return false;
}
return prevProps.items.every((item, index) =>
item.id === nextProps.items[index].id &&
item.name === nextProps.items[index].name
);
}
);
// 2. 基于特定字段的比较
const UserCard = React.memo(
function UserCard({ user, onEdit, theme }) {
return (
<div className={`user-card ${theme}`}>
<h3>{user.name}</h3>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
);
},
(prevProps, nextProps) => {
// 只关心用户核心信息,忽略其他字段变化
return (
prevProps.user.id === nextProps.user.id &&
prevProps.user.name === nextProps.user.name &&
prevProps.user.email === nextProps.user.email &&
prevProps.theme === nextProps.theme
// 忽略onEdit函数引用的变化
);
}
);
与hooks结合的优化策略:
// 1. 结合useCallback优化函数props
function OptimizedParent() {
const [users, setUsers] = useState([]);
const [filter, setFilter] = useState('');
// 使用useCallback稳定函数引用
const handleEdit = useCallback((userId) => {
console.log('Editing user:', userId);
// 编辑逻辑
}, []);
const handleDelete = useCallback((userId) => {
setUsers(users => users.filter(u => u.id !== userId));
}, []);
const filteredUsers = useMemo(() => {
return users.filter(user =>
user.name.toLowerCase().includes(filter.toLowerCase())
);
}, [users, filter]);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter users..."
/>
{filteredUsers.map(user => (
<UserCard
key={user.id}
user={user}
onEdit={handleEdit}
onDelete={handleDelete}
/>
))}
</div>
);
}
// 2. 复杂组件的精确优化
const ComplexListItem = React.memo(
function ComplexListItem({
item,
isSelected,
onSelect,
onToggle,
renderActions
}) {
console.log(`Rendering item ${item.id}`);
return (
<div
className={`list-item ${isSelected ? 'selected' : ''}`}
onClick={() => onSelect(item.id)}
>
<div className="item-content">
<h4>{item.title}</h4>
<p>{item.description}</p>
<div className="item-metadata">
<span>Created: {item.createdAt}</span>
<span>Status: {item.status}</span>
</div>
</div>
<div className="item-actions">
<button onClick={(e) => {
e.stopPropagation();
onToggle(item.id);
}}>
{item.isActive ? 'Deactivate' : 'Activate'}
</button>
{renderActions && renderActions(item)}
</div>
</div>
);
},
(prevProps, nextProps) => {
// 精确比较需要关心的属性
const itemChanged =
prevProps.item.id !== nextProps.item.id ||
prevProps.item.title !== nextProps.item.title ||
prevProps.item.description !== nextProps.item.description ||
prevProps.item.status !== nextProps.item.status ||
prevProps.item.isActive !== nextProps.item.isActive ||
prevProps.item.createdAt !== nextProps.item.createdAt;
const selectionChanged = prevProps.isSelected !== nextProps.isSelected;
// 注意:这里不比较函数props,因为它们通常用useCallback优化
return !itemChanged && !selectionChanged;
}
);
性能监控和调试:
// 性能监控HOC
function withPerformanceMonitoring(Component, componentName) {
return React.memo(
function MonitoredComponent(props) {
const renderStartTime = performance.now();
useEffect(() => {
const renderEndTime = performance.now();
console.log(
`${componentName} render time: ${renderEndTime - renderStartTime}ms`
);
});
return <Component {...props} />;
},
(prevProps, nextProps) => {
const hasChanged = Object.keys(nextProps).some(key =>
prevProps[key] !== nextProps[key]
);
if (hasChanged) {
console.log(`${componentName} will re-render due to props change`);
}
return !hasChanged;
}
);
}
// 使用示例
const MonitoredExpensiveComponent = withPerformanceMonitoring(
ExpensiveComponent,
'ExpensiveComponent'
);
最佳实践指南:
常见陷阱和解决方案:
How to solve Context performance problems?
How to solve Context performance problems?
考察点:Context性能优化。
答案:
Context的主要性能问题是当Context value变化时,所有使用该Context的组件都会重新渲染,即使它们只使用了Context中的一小部分数据。解决方案包括Context分割、使用useMemo优化value、实现订阅模式以及合理的组件结构设计。
核心性能问题分析:
// 问题示例 - Context变化导致所有消费者重渲染
const AppContext = createContext();
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [settings, setSettings] = useState({});
// 每次渲染都创建新对象,导致所有消费者重渲染
const value = {
user, setUser,
theme, setTheme,
settings, setSettings
};
return (
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
);
}
// 所有使用Context的组件都会在任何值变化时重渲染
function UserProfile() {
const { user } = useContext(AppContext); // 即使只用user,theme变化也会重渲染
return <div>{user?.name}</div>;
}
解决方案1:Context分割策略
// 将大Context分割为多个小Context
const UserContext = createContext();
const ThemeContext = createContext();
const SettingsContext = createContext();
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({
user, setUser
}), [user]);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
}
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const value = useMemo(() => ({
theme, setTheme
}), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
// 组合多个Provider
function AppProviders({ children }) {
return (
<UserProvider>
<ThemeProvider>
<SettingsProvider>
{children}
</SettingsProvider>
</ThemeProvider>
</UserProvider>
);
}
解决方案2:使用useMemo优化Context value
function OptimizedAppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [settings, setSettings] = useState({});
// 分别缓存不同部分的值
const userValue = useMemo(() => ({
user, setUser
}), [user]);
const themeValue = useMemo(() => ({
theme, setTheme
}), [theme]);
const settingsValue = useMemo(() => ({
settings, setSettings
}), [settings]);
// 整体value也要缓存
const contextValue = useMemo(() => ({
...userValue,
...themeValue,
...settingsValue
}), [userValue, themeValue, settingsValue]);
return (
<AppContext.Provider value={contextValue}>
{children}
</AppContext.Provider>
);
}
解决方案3:选择性订阅模式
// 创建支持选择性订阅的Context
function createSelectableContext(initialState) {
const Context = createContext();
const listeners = new Set();
function Provider({ children }) {
const [state, setState] = useState(initialState);
const subscribe = useCallback((listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
}, []);
const updateState = useCallback((updates) => {
setState(prev => {
const newState = { ...prev, ...updates };
listeners.forEach(listener => listener(newState, prev));
return newState;
});
}, []);
const value = useMemo(() => ({
state,
setState: updateState,
subscribe
}), [state, updateState, subscribe]);
return <Context.Provider value={value}>{children}</Context.Provider>;
}
function useSelector(selector) {
const context = useContext(Context);
if (!context) throw new Error('useSelector must be used within Provider');
const [selectedValue, setSelectedValue] = useState(() =>
selector(context.state)
);
useEffect(() => {
return context.subscribe((newState, prevState) => {
const newSelected = selector(newState);
const prevSelected = selector(prevState);
if (newSelected !== prevSelected) {
setSelectedValue(newSelected);
}
});
}, [context.subscribe, selector]);
return selectedValue;
}
return { Provider, useSelector, Context };
}
// 使用选择性订阅
const { Provider: AppProvider, useSelector } = createSelectableContext({
user: null,
theme: 'light',
settings: {}
});
function UserProfile() {
// 只订阅user变化
const user = useSelector(state => state.user);
return <div>{user?.name}</div>;
}
function ThemeToggle() {
// 只订阅theme变化
const theme = useSelector(state => state.theme);
return <button>{theme}</button>;
}
解决方案4:使用React.memo隔离重渲染
// 将Context消费者包装为memo组件
const UserDisplay = React.memo(function UserDisplay() {
const { user } = useContext(UserContext);
console.log('UserDisplay rendered');
return (
<div>
<h3>{user?.name}</h3>
<p>{user?.email}</p>
</div>
);
});
const ThemeButton = React.memo(function ThemeButton() {
const { theme, setTheme } = useContext(ThemeContext);
console.log('ThemeButton rendered');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Current theme: {theme}
</button>
);
});
// 主组件结构优化
function App() {
return (
<AppProviders>
<div>
<UserDisplay /> {/* 只在user变化时重渲染 */}
<ThemeButton /> {/* 只在theme变化时重渲染 */}
<Settings /> {/* 只在settings变化时重渲染 */}
</div>
</AppProviders>
);
}
解决方案5:组件结构优化
// 将静态内容从Context Provider中分离
function AppLayout({ children }) {
return (
<div className="app-layout">
<Header /> {/* 静态组件,不需要Context */}
<main>
<AppProviders>
{children} {/* 只有需要Context的部分被包装 */}
</AppProviders>
</main>
<Footer /> {/* 静态组件,不需要Context */}
</div>
);
}
// 懒加载Context消费者
const LazyUserProfile = lazy(() => import('./UserProfile'));
function App() {
return (
<AppLayout>
<Suspense fallback={<div>Loading...</div>}>
<LazyUserProfile />
</Suspense>
</AppLayout>
);
}
性能监控和调试:
// Context性能监控Hook
function useContextPerformance(contextName, contextValue) {
const prevValue = useRef(contextValue);
const renderCount = useRef(0);
useEffect(() => {
renderCount.current++;
if (prevValue.current !== contextValue) {
console.log(
`${contextName} changed, render count: ${renderCount.current}`
);
}
prevValue.current = contextValue;
});
}
// 在Provider中使用
function MonitoredProvider({ children }) {
const [state, setState] = useState(initialState);
useContextPerformance('AppContext', state);
return (
<AppContext.Provider value={state}>
{children}
</AppContext.Provider>
);
}
最佳实践总结:
What are the best practices for React error boundaries?
What are the best practices for React error boundaries?
考察点:错误处理策略。
答案:
React错误边界是用于捕获组件树中JavaScript错误的特殊组件,防止整个应用崩溃。最佳实践包括合理的边界划分、完善的错误处理策略、用户友好的降级UI、错误监控与上报,以及与现代React特性的结合使用。
错误边界基础实现:
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// 更新state以显示降级UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 记录错误详情
this.setState({
error,
errorInfo
});
// 错误上报
this.logErrorToService(error, errorInfo);
}
logErrorToService(error, errorInfo) {
// 发送错误信息到监控服务
console.error('Error caught by boundary:', error, errorInfo);
// 实际项目中的错误上报
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', 'exception', {
description: error.toString(),
fatal: false
});
}
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className="error-boundary">
<h2>Something went wrong!</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
<summary>Error Details</summary>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
分级错误边界策略:
// 1. 应用级错误边界 - 最外层兜底
class AppErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, errorId: null };
}
static getDerivedStateFromError(error) {
return {
hasError: true,
errorId: Date.now().toString(36)
};
}
componentDidCatch(error, errorInfo) {
// 应用级错误 - 严重错误上报
this.reportCriticalError(error, errorInfo, this.state.errorId);
}
reportCriticalError(error, errorInfo, errorId) {
const errorReport = {
errorId,
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
timestamp: new Date().toISOString(),
url: window.location.href,
userAgent: navigator.userAgent
};
// 发送到错误监控服务
fetch('/api/errors/critical', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorReport)
}).catch(console.error);
}
render() {
if (this.state.hasError) {
return (
<div className="app-error">
<h1>应用遇到了问题</h1>
<p>错误ID: {this.state.errorId}</p>
<button onClick={() => window.location.reload()}>
重新加载页面
</button>
</div>
);
}
return this.props.children;
}
}
// 2. 页面级错误边界
class PageErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 页面级错误 - 中等严重度
console.error('Page Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="page-error">
<h2>页面加载失败</h2>
<p>请尝试刷新页面或返回上一页</p>
<button onClick={() => this.setState({ hasError: false })}>
重试
</button>
</div>
);
}
return this.props.children;
}
}
// 3. 组件级错误边界
class ComponentErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 组件级错误 - 较低严重度
console.warn('Component Error:', error);
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className="component-error">
<p>组件加载失败</p>
</div>
);
}
return this.props.children;
}
}
Hook版本的错误边界(使用第三方库):
// 使用react-error-boundary库
import { ErrorBoundary, useErrorHandler } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div className="error-fallback">
<h2>Something went wrong:</h2>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
function MyComponent() {
const handleError = useErrorHandler();
const handleAsyncError = async () => {
try {
await riskyAsyncOperation();
} catch (error) {
handleError(error); // 手动触发错误边界
}
};
return (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={(error, errorInfo) => {
console.error('Logged by ErrorBoundary:', error, errorInfo);
}}
onReset={() => {
// 重置相关状态
window.location.reload();
}}
>
<AsyncComponent onAsyncError={handleAsyncError} />
</ErrorBoundary>
);
}
与Suspense结合使用:
function AppWithErrorHandling() {
return (
<AppErrorBoundary>
<Router>
<Routes>
<Route path="/" element={
<PageErrorBoundary>
<Suspense fallback={<PageLoading />}>
<HomePage />
</Suspense>
</PageErrorBoundary>
} />
<Route path="/profile" element={
<PageErrorBoundary>
<Suspense fallback={<PageLoading />}>
<ProfilePage />
</Suspense>
</PageErrorBoundary>
} />
</Routes>
</Router>
</AppErrorBoundary>
);
}
错误恢复和重试机制:
class RetryErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
retryCount: 0
};
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Retry Error Boundary:', error);
}
handleRetry = () => {
if (this.state.retryCount < 3) {
this.setState(prevState => ({
hasError: false,
retryCount: prevState.retryCount + 1
}));
} else {
// 达到最大重试次数,显示联系支持的信息
this.setState({ maxRetriesReached: true });
}
};
render() {
if (this.state.hasError) {
if (this.state.maxRetriesReached) {
return (
<div className="max-retry-error">
<h3>多次尝试后仍然失败</h3>
<p>请联系技术支持</p>
</div>
);
}
return (
<div className="retry-error">
<h3>出现错误</h3>
<p>重试次数: {this.state.retryCount}/3</p>
<button onClick={this.handleRetry}>
重试
</button>
</div>
);
}
return this.props.children;
}
}
错误监控和分析:
class MonitoringErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 详细的错误监控
const errorData = {
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
timestamp: new Date().toISOString(),
url: window.location.href,
userId: this.getUserId(),
sessionId: this.getSessionId(),
browserInfo: {
userAgent: navigator.userAgent,
viewport: `${window.innerWidth}x${window.innerHeight}`,
language: navigator.language
},
buildInfo: {
version: process.env.REACT_APP_VERSION,
environment: process.env.NODE_ENV
}
};
// 发送到多个监控服务
this.sendToSentry(errorData);
this.sendToCustomService(errorData);
this.logToConsole(errorData);
}
getUserId() {
// 获取用户ID的逻辑
return localStorage.getItem('userId');
}
getSessionId() {
// 获取会话ID的逻辑
return sessionStorage.getItem('sessionId');
}
sendToSentry(errorData) {
// Sentry错误上报
if (window.Sentry) {
window.Sentry.captureException(new Error(errorData.message), {
extra: errorData
});
}
}
sendToCustomService(errorData) {
// 自定义错误服务
fetch('/api/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorData)
}).catch(console.error);
}
logToConsole(errorData) {
console.group('Error Boundary Caught Error');
console.error('Error:', errorData.message);
console.error('Stack:', errorData.stack);
console.error('Component Stack:', errorData.componentStack);
console.table(errorData.browserInfo);
console.groupEnd();
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultErrorFallback />;
}
return this.props.children;
}
}
最佳实践总结:
注意事项:
What is the principle of combining lazy and Suspense for code splitting?
What is the principle of combining lazy and Suspense for code splitting?
考察点:懒加载机制。
答案:
React.lazy和Suspense结合实现代码分割的原理是:lazy创建一个特殊的组件,该组件在首次渲染时动态导入模块并抛出Promise,Suspense捕获这个Promise并显示fallback,当模块加载完成后重新渲染实际组件。这种机制实现了组件级别的按需加载,有效减少初始包体积。
基本工作流程:
实现原理解析:
// 1. lazy的基本实现原理(简化版)
function createLazy(loader) {
let status = 'pending';
let result = null;
let promise = null;
return function LazyComponent(props) {
switch (status) {
case 'pending':
// 创建加载Promise
if (!promise) {
promise = loader()
.then(module => {
status = 'resolved';
result = module.default || module;
return result;
})
.catch(error => {
status = 'rejected';
result = error;
throw error;
});
}
// 抛出Promise,让Suspense捕获
throw promise;
case 'resolved':
// 模块加载完成,渲染组件
const Component = result;
return <Component {...props} />;
case 'rejected':
// 加载失败,抛出错误
throw result;
default:
throw new Error('Invalid lazy component status');
}
};
}
// 2. 实际使用示例
const LazyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
高级代码分割策略:
// 1. 路由级别的代码分割
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// 懒加载页面组件
const HomePage = lazy(() => import('./pages/HomePage'));
const ProfilePage = lazy(() => import('./pages/ProfilePage'));
const SettingsPage = lazy(() => import('./pages/SettingsPage'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<PageLoading />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
// 2. 条件性代码分割
function ConditionalLazyLoading() {
const [showAdvanced, setShowAdvanced] = useState(false);
// 只有需要时才加载高级组件
const AdvancedComponent = useMemo(
() => showAdvanced ? lazy(() => import('./AdvancedComponent')) : null,
[showAdvanced]
);
return (
<div>
<button onClick={() => setShowAdvanced(!showAdvanced)}>
{showAdvanced ? 'Hide' : 'Show'} Advanced Features
</button>
{AdvancedComponent && (
<Suspense fallback={<div>Loading advanced features...</div>}>
<AdvancedComponent />
</Suspense>
)}
</div>
);
}
// 3. 预加载策略
function PreloadingExample() {
const [showModal, setShowModal] = useState(false);
// 预加载模态框组件
const preloadModal = useCallback(() => {
import('./Modal').then(module => {
// 预加载完成,缓存在模块系统中
console.log('Modal preloaded');
});
}, []);
useEffect(() => {
// 在空闲时预加载
const timer = setTimeout(preloadModal, 2000);
return () => clearTimeout(timer);
}, [preloadModal]);
const Modal = lazy(() => import('./Modal'));
return (
<div>
<button
onMouseEnter={preloadModal} // 鼠标悬停时预加载
onClick={() => setShowModal(true)}
>
Show Modal
</button>
{showModal && (
<Suspense fallback={<div>Loading modal...</div>}>
<Modal onClose={() => setShowModal(false)} />
</Suspense>
)}
</div>
);
}
嵌套Suspense和错误处理:
// 1. 嵌套Suspense实现渐进式加载
function NestedSuspenseExample() {
return (
<div className="app">
<Header />
<Suspense fallback={<AppLoading />}>
<MainContent>
<Suspense fallback={<SidebarLoading />}>
<Sidebar />
</Suspense>
<Suspense fallback={<ContentLoading />}>
<LazyContent />
<Suspense fallback={<WidgetLoading />}>
<LazyWidget />
</Suspense>
</Suspense>
</MainContent>
</Suspense>
</div>
);
}
// 2. 错误边界与懒加载结合
class LazyErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, retryCount: 0 };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Lazy loading error:', error);
// 网络错误时自动重试
if (error.name === 'ChunkLoadError' && this.state.retryCount < 3) {
setTimeout(() => {
this.setState(prevState => ({
hasError: false,
retryCount: prevState.retryCount + 1
}));
}, 1000);
}
}
render() {
if (this.state.hasError) {
return (
<div className="lazy-error">
<p>组件加载失败</p>
<button onClick={() => window.location.reload()}>
重新加载页面
</button>
</div>
);
}
return this.props.children;
}
}
// 使用示例
function App() {
return (
<LazyErrorBoundary>
<Suspense fallback={<ComponentLoading />}>
<LazyComponent />
</Suspense>
</LazyErrorBoundary>
);
}
性能优化和监控:
// 1. 懒加载性能监控
function createMonitoredLazy(importFunc, componentName) {
return lazy(() => {
const startTime = performance.now();
return importFunc().then(module => {
const endTime = performance.now();
const loadTime = endTime - startTime;
// 记录加载时间
console.log(`${componentName} loaded in ${loadTime}ms`);
// 发送性能数据
if (window.gtag) {
window.gtag('event', 'lazy_load_timing', {
event_category: 'Performance',
name: componentName,
value: Math.round(loadTime)
});
}
return module;
});
});
}
// 2. 自适应加载策略
function AdaptiveLazyLoading({ children }) {
const [connection, setConnection] = useState(null);
useEffect(() => {
// 检测网络连接质量
if ('connection' in navigator) {
setConnection(navigator.connection);
const updateConnection = () => {
setConnection(navigator.connection);
};
navigator.connection.addEventListener('change', updateConnection);
return () => {
navigator.connection.removeEventListener('change', updateConnection);
};
}
}, []);
// 根据网络状况调整加载策略
const shouldPreload = useMemo(() => {
if (!connection) return true;
// 4G或更好的网络才预加载
return connection.effectiveType === '4g';
}, [connection]);
useEffect(() => {
if (shouldPreload) {
// 预加载关键组件
import('./CriticalComponent');
import('./ImportantComponent');
}
}, [shouldPreload]);
return children;
}
Webpack配置优化:
// webpack.config.js 代码分割优化
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true
}
}
}
}
};
// 预取和预加载注释
const LazyComponent = lazy(() =>
import(
/* webpackChunkName: "lazy-component" */
/* webpackPreload: true */
'./LazyComponent'
)
);
const PreloadComponent = lazy(() =>
import(
/* webpackChunkName: "preload-component" */
/* webpackPrefetch: true */
'./PreloadComponent'
)
);
最佳实践:
How does React 18's rendering priority work?
How does React 18’s rendering priority work?
考察点:渲染优先级机制。
答案:
React 18的渲染优先级机制通过Lane模型实现多级优先级调度,将更新分为不同优先级(同步、用户交互、正常、低优先级等),高优先级更新可以中断低优先级渲染,确保重要的用户交互(如输入、点击)能够及时响应,从而提升用户体验。
优先级分类体系:
// React内部优先级常量(简化版)
const PRIORITY_LEVELS = {
ImmediatePriority: 1, // 立即执行(同步)
UserBlockingPriority: 2, // 用户交互(250ms)
NormalPriority: 3, // 正常优先级(5s)
LowPriority: 4, // 低优先级(10s)
IdlePriority: 5 // 空闲时执行
};
// 不同类型更新的优先级映射
function getUpdatePriority(eventType) {
switch (eventType) {
case 'click':
case 'keydown':
case 'input':
return UserBlockingPriority; // 用户交互高优先级
case 'scroll':
case 'mouseover':
return NormalPriority; // 正常优先级
case 'animation':
return LowPriority; // 动画低优先级
default:
return NormalPriority;
}
}
优先级调度示例:
import { useState, useTransition, useDeferredValue, startTransition } from 'react';
function PriorityDemo() {
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [isPending, startTransition] = useTransition();
// 高优先级:用户输入立即响应
const handleInputChange = (e) => {
setInput(e.target.value); // 同步更新,最高优先级
// 低优先级:搜索结果可以延后
startTransition(() => {
const results = performSearch(e.target.value);
setList(results); // 可被中断的低优先级更新
});
};
return (
<div>
{/* 用户输入框 - 高优先级更新 */}
<input
value={input}
onChange={handleInputChange}
placeholder="搜索..."
/>
{/* 搜索结果 - 低优先级更新 */}
<div className={isPending ? 'loading' : ''}>
{list.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
// useDeferredValue 实现优先级延迟
function DeferredValueExample() {
const [query, setQuery] = useState('');
const [items] = useState(generateLargeList(10000));
// 延迟值,不会阻塞输入
const deferredQuery = useDeferredValue(query);
// 基于延迟值进行昂贵计算
const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(deferredQuery.toLowerCase())
);
}, [items, deferredQuery]);
const isStale = query !== deferredQuery;
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<div style={{ opacity: isStale ? 0.5 : 1 }}>
{filteredItems.slice(0, 100).map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
</div>
);
}
Lane模型工作原理:
// Lane模型简化实现
class LaneScheduler {
constructor() {
this.pendingLanes = 0; // 等待执行的lanes
this.expiredLanes = 0; // 过期的lanes
this.currentRenderLanes = 0; // 当前渲染的lanes
}
// 标记更新的lane
markUpdateLane(lane) {
this.pendingLanes |= lane;
}
// 获取下一个要处理的lanes
getNextLanes() {
if (this.expiredLanes !== 0) {
// 过期的lanes优先级最高
return this.expiredLanes;
}
// 选择优先级最高的pending lanes
return this.getHighestPriorityLanes(this.pendingLanes);
}
getHighestPriorityLanes(lanes) {
// 位运算找到最高优先级的lane
return lanes & -lanes;
}
// 标记lanes为过期
markExpiredLanes(currentTime) {
const expiredLanes = this.computeExpiredLanes(currentTime);
this.expiredLanes |= expiredLanes;
}
computeExpiredLanes(currentTime) {
// 根据时间判断哪些lanes已过期
// 用户交互类更新如果超过250ms就标记为过期
return this.pendingLanes; // 简化实现
}
}
// 使用Lane调度更新
function scheduleUpdate(update) {
const lane = computeLaneForUpdate(update);
const scheduler = new LaneScheduler();
scheduler.markUpdateLane(lane);
// 如果有更高优先级的工作,中断当前工作
if (scheduler.shouldYieldToHigherPriority()) {
interruptCurrentWork();
}
scheduleWork(scheduler.getNextLanes());
}
时间切片与优先级结合:
function TimeSlicingWithPriority() {
const [urgentCount, setUrgentCount] = useState(0);
const [backgroundTasks, setBackgroundTasks] = useState([]);
const [isPending, startTransition] = useTransition();
// 紧急任务:用户点击立即响应
const handleUrgentTask = () => {
setUrgentCount(count => count + 1); // 同步高优先级
};
// 后台任务:可被中断的低优先级工作
const handleBackgroundTask = () => {
startTransition(() => {
// 模拟大量计算工作
const tasks = [];
for (let i = 0; i < 1000; i++) {
tasks.push({
id: i,
result: performExpensiveCalculation(i)
});
// 每100次计算检查是否需要让出控制权
if (i % 100 === 0) {
scheduler.postTask(() => {
// 让出控制权给更高优先级任务
}, { priority: 'background' });
}
}
setBackgroundTasks(tasks);
});
};
return (
<div>
<button onClick={handleUrgentTask}>
紧急任务 (Count: {urgentCount})
</button>
<button onClick={handleBackgroundTask}>
后台任务 {isPending && '(进行中...)'}
</button>
<div>
后台任务结果: {backgroundTasks.length} 个完成
</div>
</div>
);
}
自定义优先级调度:
// 使用Scheduler包实现自定义优先级
import {
unstable_scheduleCallback as scheduleCallback,
unstable_ImmediatePriority as ImmediatePriority,
unstable_UserBlockingPriority as UserBlockingPriority,
unstable_NormalPriority as NormalPriority,
unstable_LowPriority as LowPriority
} from 'scheduler';
function CustomPriorityScheduler() {
const [status, setStatus] = useState('idle');
const scheduleWork = (priority, task, description) => {
setStatus(`Scheduling ${description}...`);
scheduleCallback(priority, () => {
console.log(`Executing ${description}`);
task();
setStatus('idle');
});
};
// 不同优先级的任务
const immediateTask = () => {
scheduleWork(ImmediatePriority, () => {
console.log('Critical UI update');
}, 'immediate task');
};
const userInteractionTask = () => {
scheduleWork(UserBlockingPriority, () => {
console.log('User interaction response');
}, 'user interaction');
};
const normalTask = () => {
scheduleWork(NormalPriority, () => {
console.log('Normal background work');
}, 'normal task');
};
const lowPriorityTask = () => {
scheduleWork(LowPriority, () => {
console.log('Analytics or prefetching');
}, 'low priority task');
};
return (
<div>
<p>Status: {status}</p>
<button onClick={immediateTask}>Immediate Priority</button>
<button onClick={userInteractionTask}>User Blocking</button>
<button onClick={normalTask}>Normal Priority</button>
<button onClick={lowPriorityTask}>Low Priority</button>
</div>
);
}
性能监控和调试:
// 优先级性能监控
function PriorityPerformanceMonitor() {
const [metrics, setMetrics] = useState({});
useEffect(() => {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
if (entry.name.includes('React')) {
setMetrics(prev => ({
...prev,
[entry.name]: entry.duration
}));
}
});
});
observer.observe({ entryTypes: ['measure'] });
return () => observer.disconnect();
}, []);
// 监控用户输入响应时间
const monitorInputResponse = useCallback((callback) => {
return (event) => {
const startTime = performance.now();
callback(event);
// 使用MessageChannel测量实际响应时间
const { port1, port2 } = new MessageChannel();
port1.onmessage = () => {
const endTime = performance.now();
const responseTime = endTime - startTime;
console.log(`Input response time: ${responseTime}ms`);
if (responseTime > 16) { // 超过一帧时间
console.warn('Input response is slower than 16ms');
}
};
port2.postMessage(null);
};
}, []);
return (
<div>
<h3>Performance Metrics</h3>
{Object.entries(metrics).map(([name, duration]) => (
<div key={name}>
{name}: {duration.toFixed(2)}ms
</div>
))}
</div>
);
}
优先级最佳实践:
注意事项:
What is the difference in execution timing between useLayoutEffect and useEffect?
What is the difference in execution timing between useLayoutEffect and useEffect?
考察点:副作用执行时机。
答案:
useLayoutEffect和useEffect的核心区别在于执行时机:useLayoutEffect在DOM更新后、浏览器绘制前同步执行,而useEffect在浏览器绘制后异步执行。这种时机差异使得useLayoutEffect适合需要读取DOM或避免视觉闪烁的场景,而useEffect适合大部分副作用操作。
执行时机对比:
// React渲染生命周期中的执行顺序
function LifecycleDemo() {
const [count, setCount] = useState(0);
console.log('1. Component render');
useLayoutEffect(() => {
console.log('2. useLayoutEffect - DOM已更新,浏览器未绘制');
// 此时可以同步读取DOM,不会产生视觉闪烁
const element = document.getElementById('counter');
if (element) {
console.log('DOM content:', element.textContent);
}
}, [count]);
useEffect(() => {
console.log('4. useEffect - 浏览器绘制完成后异步执行');
// 此时用户已经看到了更新后的界面
}, [count]);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
点击查看执行顺序
</button>
<div id="counter">Count: {count}</div>
{/* 3. 浏览器绘制 - 用户看到界面更新 */}
</div>
);
}
// 执行顺序:
// 1. Component render
// 2. DOM 更新
// 3. useLayoutEffect 执行
// 4. 浏览器绘制
// 5. useEffect 执行
典型使用场景对比:
// 1. useLayoutEffect - DOM测量和同步更新
function TooltipWithLayoutEffect({ children, tooltip }) {
const [position, setPosition] = useState({ top: 0, left: 0 });
const [showTooltip, setShowTooltip] = useState(false);
const triggerRef = useRef();
const tooltipRef = useRef();
useLayoutEffect(() => {
if (showTooltip && triggerRef.current && tooltipRef.current) {
// 同步计算位置,避免闪烁
const triggerRect = triggerRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
let top = triggerRect.top - tooltipRect.height - 10;
let left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
// 边界检测
if (top < 0) {
top = triggerRect.bottom + 10;
}
if (left < 0) {
left = 10;
}
setPosition({ top, left });
}
}, [showTooltip]);
return (
<div>
<span
ref={triggerRef}
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
{children}
</span>
{showTooltip && (
<div
ref={tooltipRef}
style={{
position: 'fixed',
top: position.top,
left: position.left,
backgroundColor: '#333',
color: 'white',
padding: '8px',
borderRadius: '4px',
zIndex: 1000
}}
>
{tooltip}
</div>
)}
</div>
);
}
// 2. useEffect - 数据获取和异步操作
function DataFetchingWithEffect({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 异步数据获取,不阻塞渲染
let cancelled = false;
const fetchUser = async () => {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
if (!cancelled) {
setUser(userData);
}
} catch (error) {
if (!cancelled) {
console.error('Failed to fetch user:', error);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
};
fetchUser();
return () => {
cancelled = true;
};
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>User: {user?.name}</div>;
}
避免视觉闪烁的实际应用:
// 1. 动态高度调整
function AutoResizeTextarea({ value, onChange }) {
const textareaRef = useRef();
useLayoutEffect(() => {
if (textareaRef.current) {
// 重置高度后重新计算,避免高度闪烁
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height =
textareaRef.current.scrollHeight + 'px';
}
}, [value]);
return (
<textarea
ref={textareaRef}
value={value}
onChange={onChange}
style={{
resize: 'none',
overflow: 'hidden',
minHeight: '100px'
}}
/>
);
}
// 2. 滚动位置恢复
function ScrollPositionRestore({ children }) {
const containerRef = useRef();
const [savedPosition, setSavedPosition] = useState(0);
useLayoutEffect(() => {
// 在DOM更新后立即恢复滚动位置,避免跳动
if (containerRef.current && savedPosition > 0) {
containerRef.current.scrollTop = savedPosition;
}
});
const handleScroll = useCallback(() => {
if (containerRef.current) {
setSavedPosition(containerRef.current.scrollTop);
}
}, []);
return (
<div
ref={containerRef}
onScroll={handleScroll}
style={{ height: '400px', overflow: 'auto' }}
>
{children}
</div>
);
}
// 3. 主题切换时的即时样式更新
function ThemeAwareComponent() {
const [theme, setTheme] = useState('light');
const elementRef = useRef();
useLayoutEffect(() => {
// 立即应用主题样式,避免闪烁
if (elementRef.current) {
const isDark = theme === 'dark';
elementRef.current.style.backgroundColor = isDark ? '#333' : '#fff';
elementRef.current.style.color = isDark ? '#fff' : '#333';
// 更新CSS变量
document.documentElement.style.setProperty(
'--theme-bg',
isDark ? '#333' : '#fff'
);
}
}, [theme]);
return (
<div ref={elementRef}>
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
切换主题
</button>
<p>当前主题: {theme}</p>
</div>
);
}
性能考虑和最佳实践:
// 1. 性能监控对比
function PerformanceComparison() {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
const startTime = performance.now();
// 模拟同步DOM操作
document.body.style.backgroundColor = count % 2 ? '#f0f0f0' : '#ffffff';
const endTime = performance.now();
console.log(`useLayoutEffect took ${endTime - startTime}ms`);
}, [count]);
useEffect(() => {
const startTime = performance.now();
// 异步操作不阻塞渲染
setTimeout(() => {
const endTime = performance.now();
console.log(`useEffect took ${endTime - startTime}ms`);
}, 0);
}, [count]);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
// 2. 条件性使用
function ConditionalEffectUsage({ needsSyncUpdate, data }) {
const elementRef = useRef();
// 根据需要选择合适的Effect
const effectHook = needsSyncUpdate ? useLayoutEffect : useEffect;
effectHook(() => {
if (elementRef.current) {
if (needsSyncUpdate) {
// 需要同步更新时使用useLayoutEffect
elementRef.current.style.transform = `scale(${data.scale})`;
} else {
// 异步更新使用useEffect
setTimeout(() => {
elementRef.current.style.opacity = data.opacity;
}, 0);
}
}
}, [data, needsSyncUpdate]);
return <div ref={elementRef}>Content</div>;
}
// 3. 避免不必要的同步操作
function OptimizedComponent({ items }) {
const [selectedId, setSelectedId] = useState(null);
// ❌ 不必要的useLayoutEffect
// useLayoutEffect(() => {
// console.log('Selection changed'); // 不需要DOM操作
// }, [selectedId]);
// ✅ 使用useEffect即可
useEffect(() => {
console.log('Selection changed');
// 异步操作,如数据获取或分析
analytics.track('item_selected', { selectedId });
}, [selectedId]);
// ✅ 需要DOM测量时才使用useLayoutEffect
useLayoutEffect(() => {
if (selectedId) {
const element = document.getElementById(`item-${selectedId}`);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}
}, [selectedId]);
return (
<div>
{items.map(item => (
<div
key={item.id}
id={`item-${item.id}`}
onClick={() => setSelectedId(item.id)}
style={{
backgroundColor: selectedId === item.id ? '#e0e0e0' : 'transparent'
}}
>
{item.name}
</div>
))}
</div>
);
}
选择指南:
使用useLayoutEffect的场景:
使用useEffect的场景:
性能注意事项:
What advantages does React 19's use hook have over existing hooks?
What advantages does React 19’s use hook have over existing hooks?
考察点:React 19新特性。
答案:
React 19的use hook是一个革命性的新特性,它可以在组件中读取Promise和Context,突破了传统Hook的限制。相比现有hook,use的主要优势包括:可以在条件语句中使用、支持Promise的直接消费、简化异步状态管理、更好的Suspense集成,以及更直观的数据获取模式。
核心优势对比:
// 传统方式 vs use hook 对比
import { use, useState, useEffect, Suspense } from 'react';
// 1. 传统异步数据获取方式
function TraditionalDataFetching({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('Failed to fetch');
const userData = await response.json();
if (!cancelled) {
setUser(userData);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
};
fetchUser();
return () => { cancelled = true; };
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>User: {user?.name}</div>;
}
// 2. React 19 use hook 方式
function ModernDataFetching({ userId }) {
// 直接消费Promise,自动处理loading和error状态
const user = use(fetchUser(userId));
return <div>User: {user.name}</div>;
}
// 需要配合Suspense和ErrorBoundary使用
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<ModernDataFetching userId={123} />
</Suspense>
</ErrorBoundary>
);
}
条件性使用的突破:
// 传统hook不能在条件中使用
function ConditionalDataTraditional({ shouldFetch, userId }) {
const [data, setData] = useState(null);
// ❌ 不能这样做
// if (shouldFetch) {
// const result = useEffect(() => {
// // 违反Hook规则
// }, []);
// }
useEffect(() => {
if (shouldFetch) {
// 必须在effect内部处理条件
fetchData(userId).then(setData);
}
}, [shouldFetch, userId]);
return data ? <div>{data.name}</div> : null;
}
// use hook 可以在条件中使用
function ConditionalDataModern({ shouldFetch, userId, cachedData }) {
// ✅ 可以在条件语句中使用
const data = shouldFetch
? use(fetchUser(userId))
: cachedData;
return data ? <div>{data.name}</div> : null;
}
// 更复杂的条件逻辑
function SmartDataLoader({ user, preferences }) {
let profileData;
if (user.isVip) {
// VIP用户获取详细资料
profileData = use(fetchVipProfile(user.id));
} else if (preferences.showExtended) {
// 普通用户的扩展资料
profileData = use(fetchExtendedProfile(user.id));
} else {
// 基础资料
profileData = use(fetchBasicProfile(user.id));
}
return <ProfileCard data={profileData} />;
}
Promise缓存和重用:
// 创建可缓存的Promise
const createCachedPromise = () => {
const cache = new Map();
return (key, fetcher) => {
if (!cache.has(key)) {
cache.set(key, fetcher());
}
return cache.get(key);
};
};
const cachedFetch = createCachedPromise();
// 多个组件可以共享同一个Promise
function UserProfile({ userId }) {
const user = use(cachedFetch(`user-${userId}`, () => fetchUser(userId)));
return <div>Profile: {user.name}</div>;
}
function UserAvatar({ userId }) {
const user = use(cachedFetch(`user-${userId}`, () => fetchUser(userId)));
return <img src={user.avatar} alt={user.name} />;
}
// 两个组件会复用同一个网络请求
function UserCard({ userId }) {
return (
<div>
<UserAvatar userId={userId} />
<UserProfile userId={userId} />
</div>
);
}
Context消费的简化:
// 传统Context使用
function TraditionalContextConsumer() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
const settings = useContext(SettingsContext);
// 需要多个useContext调用
return (
<div className={theme.mode}>
Welcome {user.name}
</div>
);
}
// use hook 消费Context
function ModernContextConsumer() {
// 更简洁的语法,可以在条件中使用
const theme = use(ThemeContext);
const user = use(UserContext);
return (
<div className={theme.mode}>
Welcome {user.name}
</div>
);
}
// 条件性Context消费
function ConditionalContextUsage({ showUserInfo, showTheme }) {
return (
<div>
{showTheme && (
<div>
Current theme: {use(ThemeContext).mode}
</div>
)}
{showUserInfo && (
<div>
User: {use(UserContext).name}
</div>
)}
</div>
);
}
与Suspense的深度集成:
// 渐进式数据加载
function ProgressiveDataLoading({ userId }) {
return (
<div>
{/* 基础信息立即显示 */}
<Suspense fallback={<UserSkeleton />}>
<UserBasicInfo userId={userId} />
{/* 详细信息延迟加载 */}
<Suspense fallback={<DetailsSkeleton />}>
<UserDetails userId={userId} />
{/* 相关数据进一步延迟 */}
<Suspense fallback={<RelatedSkeleton />}>
<RelatedUsers userId={userId} />
</Suspense>
</Suspense>
</Suspense>
</div>
);
}
function UserBasicInfo({ userId }) {
const basicInfo = use(fetchBasicInfo(userId));
return <h2>{basicInfo.name}</h2>;
}
function UserDetails({ userId }) {
const details = use(fetchUserDetails(userId));
return <div>{details.bio}</div>;
}
function RelatedUsers({ userId }) {
const related = use(fetchRelatedUsers(userId));
return (
<div>
{related.map(user => <UserCard key={user.id} user={user} />)}
</div>
);
}
并行数据获取优化:
// 传统方式 - 串行获取
function SerialDataFetching({ userId }) {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
useEffect(() => {
const loadData = async () => {
const userData = await fetchUser(userId);
setUser(userData);
const userPosts = await fetchUserPosts(userId);
setPosts(userPosts);
const userComments = await fetchUserComments(userId);
setComments(userComments);
};
loadData();
}, [userId]);
// 需要处理各种loading状态
}
// use hook - 并行获取
function ParallelDataFetching({ userId }) {
// 并行发起请求
const userPromise = fetchUser(userId);
const postsPromise = fetchUserPosts(userId);
const commentsPromise = fetchUserComments(userId);
// 按需消费,自动并行处理
const user = use(userPromise);
return (
<div>
<h2>{user.name}</h2>
<Suspense fallback={<div>Loading posts...</div>}>
<UserPosts promise={postsPromise} />
</Suspense>
<Suspense fallback={<div>Loading comments...</div>}>
<UserComments promise={commentsPromise} />
</Suspense>
</div>
);
}
function UserPosts({ promise }) {
const posts = use(promise);
return (
<div>
{posts.map(post => <PostCard key={post.id} post={post} />)}
</div>
);
}
function UserComments({ promise }) {
const comments = use(promise);
return (
<div>
{comments.map(comment => <CommentCard key={comment.id} comment={comment} />)}
</div>
);
}
错误处理的改进:
// 传统错误处理
function TraditionalErrorHandling({ userId }) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [retryCount, setRetryCount] = useState(0);
useEffect(() => {
const fetchData = async () => {
try {
setError(null);
const result = await fetchUser(userId);
setData(result);
} catch (err) {
setError(err);
// 自动重试逻辑
if (retryCount < 3) {
setTimeout(() => {
setRetryCount(c => c + 1);
}, 1000 * Math.pow(2, retryCount));
}
}
};
fetchData();
}, [userId, retryCount]);
}
// use hook 错误处理
function ModernErrorHandling({ userId }) {
// 错误由Error Boundary自动处理
const data = use(createRetryablePromise(
() => fetchUser(userId),
{ maxRetries: 3, backoff: 'exponential' }
));
return <UserDisplay data={data} />;
}
// 可重试的Promise工厂
function createRetryablePromise(fetcher, options = {}) {
const { maxRetries = 0, backoff = 'linear' } = options;
let retryCount = 0;
const attemptFetch = async () => {
try {
return await fetcher();
} catch (error) {
if (retryCount < maxRetries) {
retryCount++;
const delay = backoff === 'exponential'
? 1000 * Math.pow(2, retryCount - 1)
: 1000 * retryCount;
await new Promise(resolve => setTimeout(resolve, delay));
return attemptFetch();
}
throw error;
}
};
return attemptFetch();
}
主要优势总结:
注意事项:
How to balance performance and user experience in complex forms?
How to balance performance and user experience in complex forms?
考察点:复杂场景优化。
答案:
复杂表单的性能与用户体验平衡需要综合考虑渲染优化、状态管理、验证策略、交互反馈等多个方面。核心策略包括:字段级别的状态隔离、延迟验证、虚拟化长列表、智能缓存、渐进式加载,以及合理的用户反馈机制,既保证流畅的交互体验,又避免性能瓶颈。
表单架构设计策略:
// 1. 字段级状态管理 - 避免整个表单重渲染
import { useState, useCallback, useMemo, memo } from 'react';
// 单个表单字段组件 - 独立状态管理
const FormField = memo(function FormField({
name,
value,
onChange,
onBlur,
validator,
label,
required
}) {
const [localValue, setLocalValue] = useState(value || '');
const [error, setError] = useState('');
const [touched, setTouched] = useState(false);
// 防抖验证
const debouncedValidation = useMemo(() => {
return debounce(async (val) => {
if (validator) {
try {
await validator(val);
setError('');
} catch (err) {
setError(err.message);
}
}
}, 300);
}, [validator]);
const handleChange = useCallback((e) => {
const newValue = e.target.value;
setLocalValue(newValue);
onChange(name, newValue);
// 只在用户交互过后才实时验证
if (touched) {
debouncedValidation(newValue);
}
}, [name, onChange, touched, debouncedValidation]);
const handleBlur = useCallback(() => {
setTouched(true);
debouncedValidation(localValue);
onBlur?.(name, localValue);
}, [name, localValue, onBlur, debouncedValidation]);
return (
<div className="form-field">
<label>{label} {required && '*'}</label>
<input
value={localValue}
onChange={handleChange}
onBlur={handleBlur}
className={error ? 'error' : ''}
/>
{error && <span className="error-message">{error}</span>}
</div>
);
});
// 2. 高性能表单容器
function PerformantForm({ schema, initialData, onSubmit }) {
const [formData, setFormData] = useState(initialData || {});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
// 字段更新回调 - 使用useCallback避免子组件重渲染
const handleFieldChange = useCallback((fieldName, value) => {
setFormData(prev => ({
...prev,
[fieldName]: value
}));
}, []);
const handleFieldBlur = useCallback((fieldName, value) => {
// 字段级验证逻辑
const fieldSchema = schema.fields[fieldName];
if (fieldSchema?.validate) {
fieldSchema.validate(value)
.then(() => {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[fieldName];
return newErrors;
});
})
.catch(error => {
setErrors(prev => ({
...prev,
[fieldName]: error.message
}));
});
}
}, [schema]);
// 渲染字段 - 使用React.memo优化
const renderFields = useMemo(() => {
return schema.fields.map(field => (
<FormField
key={field.name}
{...field}
value={formData[field.name]}
onChange={handleFieldChange}
onBlur={handleFieldBlur}
/>
));
}, [schema.fields, formData, handleFieldChange, handleFieldBlur]);
return (
<form className="performant-form">
{renderFields}
<button
type="submit"
disabled={isSubmitting || Object.keys(errors).length > 0}
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
大型表单虚拟化策略:
// 3. 虚拟化长表单 - 只渲染可见区域
import { FixedSizeList as List } from 'react-window';
function VirtualizedForm({ fields, onFieldChange }) {
const [visibleRange, setVisibleRange] = useState({ start: 0, end: 10 });
// 字段渲染组件
const FieldRenderer = memo(({ index, style }) => {
const field = fields[index];
return (
<div style={style} className="virtual-field">
<FormField
{...field}
onChange={onFieldChange}
/>
</div>
);
});
return (
<div className="virtualized-form">
<List
height={600}
itemCount={fields.length}
itemSize={80}
onItemsRendered={({ visibleStartIndex, visibleStopIndex }) => {
setVisibleRange({
start: visibleStartIndex,
end: visibleStopIndex
});
}}
>
{FieldRenderer}
</List>
</div>
);
}
// 4. 分步骤表单 - 按需加载和渲染
function SteppedForm({ steps, onComplete }) {
const [currentStep, setCurrentStep] = useState(0);
const [stepData, setStepData] = useState({});
const [completedSteps, setCompletedSteps] = useState(new Set());
// 懒加载步骤组件
const StepComponent = useMemo(() => {
return lazy(() => import(`./steps/Step${currentStep}`));
}, [currentStep]);
const handleStepComplete = useCallback((data) => {
setStepData(prev => ({
...prev,
[currentStep]: data
}));
setCompletedSteps(prev => new Set([...prev, currentStep]));
if (currentStep < steps.length - 1) {
setCurrentStep(prev => prev + 1);
} else {
onComplete(stepData);
}
}, [currentStep, steps.length, stepData, onComplete]);
const handleStepBack = useCallback(() => {
if (currentStep > 0) {
setCurrentStep(prev => prev - 1);
}
}, [currentStep]);
return (
<div className="stepped-form">
<div className="step-indicator">
{steps.map((step, index) => (
<div
key={index}
className={`step ${index === currentStep ? 'active' : ''} ${
completedSteps.has(index) ? 'completed' : ''
}`}
>
{step.title}
</div>
))}
</div>
<Suspense fallback={<div>Loading step...</div>}>
<StepComponent
data={stepData[currentStep] || {}}
onComplete={handleStepComplete}
onBack={handleStepBack}
canGoBack={currentStep > 0}
/>
</Suspense>
</div>
);
}
智能验证和用户反馈:
// 5. 智能验证策略
class FormValidator {
constructor() {
this.cache = new Map();
this.pendingValidations = new Map();
}
async validateField(fieldName, value, rules) {
const cacheKey = `${fieldName}-${value}`;
// 缓存验证结果
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
// 防止重复验证
if (this.pendingValidations.has(cacheKey)) {
return this.pendingValidations.get(cacheKey);
}
const validationPromise = this._performValidation(value, rules);
this.pendingValidations.set(cacheKey, validationPromise);
try {
const result = await validationPromise;
this.cache.set(cacheKey, result);
this.pendingValidations.delete(cacheKey);
return result;
} catch (error) {
this.pendingValidations.delete(cacheKey);
throw error;
}
}
async _performValidation(value, rules) {
for (const rule of rules) {
if (rule.async) {
// 异步验证(如检查用户名是否存在)
await rule.validator(value);
} else {
// 同步验证
if (!rule.validator(value)) {
throw new Error(rule.message);
}
}
}
return { valid: true };
}
}
// 6. 用户体验增强组件
function EnhancedFormField({
name,
validator,
onAsyncValidationStart,
onAsyncValidationEnd
}) {
const [value, setValue] = useState('');
const [error, setError] = useState('');
const [isValidating, setIsValidating] = useState(false);
const [validationTimer, setValidationTimer] = useState(null);
const formValidator = useMemo(() => new FormValidator(), []);
const handleValidation = useCallback(async (newValue) => {
if (!validator) return;
setIsValidating(true);
onAsyncValidationStart?.(name);
try {
await formValidator.validateField(name, newValue, validator);
setError('');
} catch (err) {
setError(err.message);
} finally {
setIsValidating(false);
onAsyncValidationEnd?.(name);
}
}, [name, validator, formValidator, onAsyncValidationStart, onAsyncValidationEnd]);
const handleChange = useCallback((e) => {
const newValue = e.target.value;
setValue(newValue);
// 清除之前的定时器
if (validationTimer) {
clearTimeout(validationTimer);
}
// 设置新的验证定时器(防抖)
const timer = setTimeout(() => {
handleValidation(newValue);
}, 500);
setValidationTimer(timer);
}, [validationTimer, handleValidation]);
return (
<div className="enhanced-field">
<input
value={value}
onChange={handleChange}
className={`
${error ? 'error' : ''}
${isValidating ? 'validating' : ''}
`}
/>
<div className="field-status">
{isValidating && (
<span className="validating-indicator">
<Spinner size="small" /> Validating...
</span>
)}
{error && (
<span className="error-message">
<ErrorIcon /> {error}
</span>
)}
{!isValidating && !error && value && (
<span className="success-indicator">
<CheckIcon />
</span>
)}
</div>
</div>
);
}
性能监控和优化:
// 7. 表单性能监控
function FormPerformanceMonitor({ children }) {
const [metrics, setMetrics] = useState({});
const renderCount = useRef(0);
const lastRenderTime = useRef(Date.now());
useEffect(() => {
renderCount.current++;
const currentTime = Date.now();
const timeSinceLastRender = currentTime - lastRenderTime.current;
setMetrics(prev => ({
...prev,
renderCount: renderCount.current,
averageRenderTime: prev.averageRenderTime
? (prev.averageRenderTime + timeSinceLastRender) / 2
: timeSinceLastRender,
lastRenderTime: currentTime
}));
lastRenderTime.current = currentTime;
// 性能告警
if (timeSinceLastRender > 100) {
console.warn(`Slow form render detected: ${timeSinceLastRender}ms`);
}
});
return (
<div>
<div className="performance-metrics">
<small>
Renders: {metrics.renderCount} |
Avg: {metrics.averageRenderTime?.toFixed(2)}ms
</small>
</div>
{children}
</div>
);
}
// 8. 自适应性能策略
function AdaptiveFormRenderer({ fields, deviceCapability }) {
const [renderingStrategy, setRenderingStrategy] = useState('normal');
useEffect(() => {
// 根据设备性能调整渲染策略
if (deviceCapability.memory < 1000) {
setRenderingStrategy('lightweight');
} else if (fields.length > 100) {
setRenderingStrategy('virtualized');
} else {
setRenderingStrategy('normal');
}
}, [deviceCapability, fields.length]);
switch (renderingStrategy) {
case 'lightweight':
return <LightweightForm fields={fields} />;
case 'virtualized':
return <VirtualizedForm fields={fields} />;
default:
return <NormalForm fields={fields} />;
}
}
最佳实践总结:
性能优化策略:
用户体验提升:
平衡点选择:
How does React DevTools Profiler analyze performance bottlenecks?
How does React DevTools Profiler analyze performance bottlenecks?
考察点:性能调试工具。
答案:
React DevTools Profiler是专门用于React应用性能分析的强大工具,通过记录组件渲染时间、识别性能瓶颈、分析渲染原因等功能,帮助开发者系统性地优化React应用性能。它提供了火焰图、排序视图、交互追踪等多种分析维度,让性能问题无所遁形。
Profiler核心功能使用:
// 1. 使用Profiler API进行代码级性能监控
import { Profiler, useState } from 'react';
function App() {
const [count, setCount] = useState(0);
// onRender回调函数,记录性能数据
const onRenderCallback = (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
console.group(`Profiler: ${id}`);
console.log('Phase:', phase); // 'mount' 或 'update'
console.log('Actual duration:', actualDuration); // 实际渲染时间
console.log('Base duration:', baseDuration); // 估计渲染时间(无memo优化)
console.log('Start time:', startTime); // 开始渲染时间
console.log('Commit time:', commitTime); // 提交时间
console.groupEnd();
// 发送性能数据到分析服务
if (actualDuration > 16) { // 超过一帧时间
sendPerformanceData({
component: id,
duration: actualDuration,
phase,
timestamp: Date.now()
});
}
};
return (
<div>
<Profiler id="App" onRender={onRenderCallback}>
<Header />
<Profiler id="Counter" onRender={onRenderCallback}>
<Counter count={count} setCount={setCount} />
</Profiler>
<Profiler id="UserList" onRender={onRenderCallback}>
<UserList />
</Profiler>
</Profiler>
</div>
);
}
// 2. 自定义性能监控Hook
function usePerformanceMonitoring(componentName) {
const renderStartTime = useRef();
const renderCount = useRef(0);
const [performanceData, setPerformanceData] = useState({});
// 渲染开始时记录时间
renderStartTime.current = performance.now();
renderCount.current++;
useEffect(() => {
// 渲染完成后计算耗时
const renderEndTime = performance.now();
const renderDuration = renderEndTime - renderStartTime.current;
setPerformanceData(prev => ({
...prev,
lastRenderTime: renderDuration,
averageRenderTime: prev.averageRenderTime
? (prev.averageRenderTime + renderDuration) / 2
: renderDuration,
renderCount: renderCount.current,
slowRenders: renderDuration > 16 ? (prev.slowRenders || 0) + 1 : prev.slowRenders || 0
}));
// 记录到Performance API
performance.mark(`${componentName}-render-end`);
performance.measure(
`${componentName}-render`,
`${componentName}-render-start`,
`${componentName}-render-end`
);
});
useEffect(() => {
performance.mark(`${componentName}-render-start`);
}, [componentName]);
return performanceData;
}
// 使用性能监控
function MonitoredComponent() {
const perfData = usePerformanceMonitoring('MonitoredComponent');
return (
<div>
<div className="perf-info">
Renders: {perfData.renderCount} |
Avg: {perfData.averageRenderTime?.toFixed(2)}ms |
Slow: {perfData.slowRenders || 0}
</div>
{/* 组件内容 */}
</div>
);
}
性能瓶颈识别策略:
// 3. 组件渲染原因分析
function useWhyDidYouUpdate(name, props) {
const previous = useRef();
useEffect(() => {
if (previous.current) {
const allKeys = Object.keys({...previous.current, ...props});
const changedProps = {};
allKeys.forEach(key => {
if (previous.current[key] !== props[key]) {
changedProps[key] = {
from: previous.current[key],
to: props[key]
};
}
});
if (Object.keys(changedProps).length) {
console.log('[why-did-you-update]', name, changedProps);
}
}
previous.current = props;
});
}
// 使用渲染原因分析
function ExpensiveComponent({ data, filter, onSelect }) {
useWhyDidYouUpdate('ExpensiveComponent', { data, filter, onSelect });
const processedData = useMemo(() => {
console.log('Processing data...'); // 监控是否频繁执行
return data.filter(item => item.name.includes(filter));
}, [data, filter]);
return (
<div>
{processedData.map(item => (
<div key={item.id} onClick={() => onSelect(item)}>
{item.name}
</div>
))}
</div>
);
}
// 4. 自动性能预算监控
class PerformanceBudgetMonitor {
constructor(budgets) {
this.budgets = budgets; // { component: maxRenderTime }
this.violations = [];
this.observer = new PerformanceObserver(this.handleEntries.bind(this));
this.observer.observe({ entryTypes: ['measure'] });
}
handleEntries(list) {
const entries = list.getEntries();
entries.forEach(entry => {
const componentName = entry.name.replace('-render', '');
const budget = this.budgets[componentName];
if (budget && entry.duration > budget) {
const violation = {
component: componentName,
actualTime: entry.duration,
budgetTime: budget,
timestamp: Date.now(),
overage: entry.duration - budget
};
this.violations.push(violation);
console.warn('Performance budget violation:', violation);
// 自动上报性能问题
this.reportViolation(violation);
}
});
}
reportViolation(violation) {
// 发送到监控服务
fetch('/api/performance-violations', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(violation)
});
}
getViolationReport() {
return {
totalViolations: this.violations.length,
mostProblematicComponents: this.getMostProblematicComponents(),
averageOverage: this.calculateAverageOverage()
};
}
getMostProblematicComponents() {
const componentCounts = {};
this.violations.forEach(v => {
componentCounts[v.component] = (componentCounts[v.component] || 0) + 1;
});
return Object.entries(componentCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, 5);
}
calculateAverageOverage() {
const totalOverage = this.violations.reduce((sum, v) => sum + v.overage, 0);
return totalOverage / this.violations.length || 0;
}
}
// 使用性能预算监控
const performanceMonitor = new PerformanceBudgetMonitor({
'UserList': 50, // UserList组件不应超过50ms
'Dashboard': 100, // Dashboard组件不应超过100ms
'DataVisualization': 200 // 数据可视化组件不应超过200ms
});
Profiler数据可视化分析:
// 5. 性能数据收集和分析
class ReactPerformanceAnalyzer {
constructor() {
this.renderData = [];
this.componentStats = new Map();
this.interactionTimings = [];
}
recordRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {
const renderInfo = {
componentId: id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
timestamp: Date.now()
};
this.renderData.push(renderInfo);
this.updateComponentStats(renderInfo);
// 保留最近1000条记录
if (this.renderData.length > 1000) {
this.renderData.shift();
}
}
updateComponentStats(renderInfo) {
const { componentId, actualDuration, phase } = renderInfo;
if (!this.componentStats.has(componentId)) {
this.componentStats.set(componentId, {
totalRenders: 0,
totalTime: 0,
averageTime: 0,
maxTime: 0,
minTime: Infinity,
mountTime: 0,
updateTime: 0
});
}
const stats = this.componentStats.get(componentId);
stats.totalRenders++;
stats.totalTime += actualDuration;
stats.averageTime = stats.totalTime / stats.totalRenders;
stats.maxTime = Math.max(stats.maxTime, actualDuration);
stats.minTime = Math.min(stats.minTime, actualDuration);
if (phase === 'mount') {
stats.mountTime += actualDuration;
} else {
stats.updateTime += actualDuration;
}
}
getSlowComponents(threshold = 16) {
return Array.from(this.componentStats.entries())
.filter(([, stats]) => stats.averageTime > threshold)
.sort((a, b) => b[1].averageTime - a[1].averageTime);
}
getFrequentlyUpdatingComponents(threshold = 10) {
return Array.from(this.componentStats.entries())
.filter(([, stats]) => stats.totalRenders > threshold)
.sort((a, b) => b[1].totalRenders - a[1].totalRenders);
}
generatePerformanceReport() {
const slowComponents = this.getSlowComponents();
const frequentComponents = this.getFrequentlyUpdatingComponents();
return {
summary: {
totalRenders: this.renderData.length,
uniqueComponents: this.componentStats.size,
averageRenderTime: this.calculateAverageRenderTime(),
performanceScore: this.calculatePerformanceScore()
},
issues: {
slowComponents: slowComponents.slice(0, 5),
frequentlyUpdating: frequentComponents.slice(0, 5),
renderingTrends: this.analyzeRenderingTrends()
},
recommendations: this.generateRecommendations(slowComponents, frequentComponents)
};
}
calculateAverageRenderTime() {
const totalTime = this.renderData.reduce((sum, render) => sum + render.actualDuration, 0);
return totalTime / this.renderData.length || 0;
}
calculatePerformanceScore() {
const avgTime = this.calculateAverageRenderTime();
if (avgTime < 5) return 'Excellent';
if (avgTime < 10) return 'Good';
if (avgTime < 16) return 'Fair';
return 'Poor';
}
analyzeRenderingTrends() {
const recentRenders = this.renderData.slice(-100); // 最近100次渲染
const oldRenders = this.renderData.slice(-200, -100); // 之前100次渲染
const recentAvg = recentRenders.reduce((sum, r) => sum + r.actualDuration, 0) / recentRenders.length;
const oldAvg = oldRenders.reduce((sum, r) => sum + r.actualDuration, 0) / oldRenders.length;
return {
trend: recentAvg > oldAvg ? 'worsening' : 'improving',
changePercentage: ((recentAvg - oldAvg) / oldAvg * 100).toFixed(2)
};
}
generateRecommendations(slowComponents, frequentComponents) {
const recommendations = [];
slowComponents.slice(0, 3).forEach(([componentId, stats]) => {
recommendations.push({
type: 'performance',
component: componentId,
issue: `Average render time: ${stats.averageTime.toFixed(2)}ms`,
suggestion: 'Consider using React.memo, useMemo, or useCallback to optimize this component'
});
});
frequentComponents.slice(0, 3).forEach(([componentId, stats]) => {
recommendations.push({
type: 'frequency',
component: componentId,
issue: `High render frequency: ${stats.totalRenders} renders`,
suggestion: 'Investigate why this component renders frequently. Check for unnecessary prop changes or missing dependencies'
});
});
return recommendations;
}
}
// 6. 集成到React应用中
function PerformanceAwareApp() {
const analyzer = useMemo(() => new ReactPerformanceAnalyzer(), []);
const onRenderCallback = useCallback((id, phase, actualDuration, baseDuration, startTime, commitTime) => {
analyzer.recordRender(id, phase, actualDuration, baseDuration, startTime, commitTime);
}, [analyzer]);
// 定期生成性能报告
useEffect(() => {
const interval = setInterval(() => {
const report = analyzer.generatePerformanceReport();
console.log('Performance Report:', report);
// 发送到监控服务
if (report.summary.performanceScore === 'Poor') {
sendPerformanceAlert(report);
}
}, 30000); // 每30秒检查一次
return () => clearInterval(interval);
}, [analyzer]);
return (
<Profiler id="App" onRender={onRenderCallback}>
<Router>
<Routes>
<Route path="/" element={
<Profiler id="HomePage" onRender={onRenderCallback}>
<HomePage />
</Profiler>
} />
<Route path="/dashboard" element={
<Profiler id="Dashboard" onRender={onRenderCallback}>
<Dashboard />
</Profiler>
} />
</Routes>
</Router>
</Profiler>
);
}
最佳实践指南:
使用Profiler分析步骤:
关键指标监控:
常见性能问题及解决方案:
How does React Fiber implement time slicing and task scheduling?
How does React Fiber implement time slicing and task scheduling?
考察点:Fiber调度算法。
答案:
React Fiber通过将渲染工作拆分成可中断的小单元,结合requestIdleCallback和MessageChannel等浏览器API实现时间切片,使用Lane模型进行优先级调度。核心思想是将单一的同步渲染过程改造为可暂停、可恢复的异步调度系统,确保高优先级任务能够及时执行,避免长时间阻塞主线程。
Fiber架构核心概念:
// 1. Fiber节点结构(简化版)
class FiberNode {
constructor(tag, pendingProps, key, mode) {
// 节点基本信息
this.tag = tag; // 节点类型(FunctionComponent、ClassComponent等)
this.key = key; // React key
this.elementType = null; // 元素类型
this.type = null; // 组件类型
// Fiber链表结构
this.return = null; // 父节点
this.child = null; // 第一个子节点
this.sibling = null; // 下一个兄弟节点
this.index = 0; // 在兄弟节点中的索引
// 状态相关
this.pendingProps = pendingProps; // 新的props
this.memoizedProps = null; // 上次渲染的props
this.updateQueue = null; // 更新队列
this.memoizedState = null; // 上次渲染的state
// 副作用相关
this.flags = NoFlags; // 副作用标记
this.subtreeFlags = NoFlags; // 子树副作用标记
this.deletions = null; // 要删除的子节点
// 调度相关
this.lanes = NoLanes; // 当前节点的lanes
this.childLanes = NoLanes; // 子树的lanes
// 双缓冲相关
this.alternate = null; // 对应的另一个Fiber节点(current/workInProgress)
}
}
// 2. 工作循环实现
function workLoopConcurrent() {
// 并发模式下的工作循环
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
function workLoopSync() {
// 同步模式下的工作循环
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// 执行单元工作
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// 开始处理当前节点
let next = beginWork(current, unitOfWork, subtreeRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// 没有子节点,完成当前节点的工作
completeUnitOfWork(unitOfWork);
} else {
// 有子节点,继续处理子节点
workInProgress = next;
}
}
时间切片实现机制:
// 3. 时间切片调度器实现
class TimeSliceScheduler {
constructor() {
this.frameInterval = 5; // 5ms为一个时间片
this.startTime = 0;
this.isMessageLoopRunning = false;
this.scheduledCallback = null;
this.taskQueue = [];
this.timerQueue = [];
// 使用MessageChannel实现调度
this.channel = new MessageChannel();
this.port1 = this.channel.port1;
this.port2 = this.channel.port2;
this.port1.onmessage = this.performWorkUntilDeadline.bind(this);
}
// 检查是否应该让出控制权
shouldYield() {
const currentTime = getCurrentTime();
return currentTime >= this.startTime + this.frameInterval;
}
// 调度回调函数
scheduleCallback(priorityLevel, callback, options = {}) {
const currentTime = getCurrentTime();
const { delay = 0 } = options;
const startTime = currentTime + delay;
const timeout = this.getTimeoutByPriority(priorityLevel);
const expirationTime = startTime + timeout;
const newTask = {
id: this.generateId(),
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: delay > 0 ? startTime : expirationTime
};
if (delay > 0) {
// 延迟任务放入定时器队列
this.timerQueue.push(newTask);
this.timerQueue.sort((a, b) => a.sortIndex - b.sortIndex);
if (!this.isHostTimeoutScheduled && newTask === this.timerQueue[0]) {
this.requestHostTimeout(this.handleTimeout.bind(this), delay);
}
} else {
// 立即任务放入任务队列
this.taskQueue.push(newTask);
this.taskQueue.sort((a, b) => a.sortIndex - b.sortIndex);
if (!this.isMessageLoopRunning) {
this.schedulePerformWorkUntilDeadline();
}
}
return newTask;
}
// 执行工作直到截止时间
performWorkUntilDeadline() {
if (this.scheduledCallback !== null) {
const currentTime = getCurrentTime();
this.startTime = currentTime;
let hasTimeRemaining = true;
let hasMoreWork = true;
try {
hasMoreWork = this.scheduledCallback(hasTimeRemaining, currentTime);
} finally {
if (hasMoreWork) {
// 还有工作要做,继续调度
this.schedulePerformWorkUntilDeadline();
} else {
this.isMessageLoopRunning = false;
this.scheduledCallback = null;
}
}
} else {
this.isMessageLoopRunning = false;
}
}
// 使用MessageChannel调度
schedulePerformWorkUntilDeadline() {
this.port2.postMessage(null);
this.isMessageLoopRunning = true;
}
// 根据优先级获取超时时间
getTimeoutByPriority(priorityLevel) {
switch (priorityLevel) {
case ImmediatePriority:
return -1; // 立即执行
case UserBlockingPriority:
return 250; // 250ms
case NormalPriority:
return 5000; // 5s
case LowPriority:
return 10000; // 10s
case IdlePriority:
return maxSigned31BitInt; // 永不过期
default:
return 5000;
}
}
}
Lane模型优先级调度:
// 4. Lane模型实现
const TotalLanes = 31;
// Lane常量定义
const NoLanes = 0b0000000000000000000000000000000;
const NoLane = 0b0000000000000000000000000000000;
const SyncLane = 0b0000000000000000000000000000001;
const InputContinuousLane = 0b0000000000000000000000000000010;
const DefaultLane = 0b0000000000000000000000000010000;
const TransitionLane1 = 0b0000000000000000000000001000000;
const RetryLane1 = 0b0000000000000000001000000000000;
const SelectiveHydrationLane = 0b0001000000000000000000000000000;
const IdleLane = 0b0100000000000000000000000000000;
const OffscreenLane = 0b1000000000000000000000000000000;
class LaneScheduler {
constructor() {
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = new Array(TotalLanes).fill(NoLanes);
// 过期时间数组
this.expirationTimes = new Array(TotalLanes).fill(-1);
}
// 获取下一个要处理的lanes
getNextLanes(wipLanes) {
// 如果没有待处理的lanes,返回NoLanes
if (this.pendingLanes === NoLanes) {
return NoLanes;
}
let nextLanes = NoLanes;
const expiredLanes = this.getExpiredLanes();
if (expiredLanes !== NoLanes) {
// 有过期的lanes,优先处理
nextLanes = expiredLanes;
} else {
// 选择优先级最高的非suspended lanes
const nonIdleUnblockedLanes = this.pendingLanes & ~this.suspendedLanes;
if (nonIdleUnblockedLanes !== NoLanes) {
nextLanes = this.getHighestPriorityLanes(nonIdleUnblockedLanes);
} else {
// 所有lanes都被suspended,选择被ping的lanes
const unblockedLanes = this.pendingLanes & ~this.suspendedLanes;
if (unblockedLanes !== NoLanes) {
nextLanes = this.getHighestPriorityLanes(unblockedLanes);
}
}
}
if (nextLanes === NoLanes) {
return NoLanes;
}
// 包含相关联的lanes
nextLanes = this.addEntangledLanes(nextLanes);
return nextLanes;
}
// 获取最高优先级的lanes
getHighestPriorityLanes(lanes) {
// 使用位运算快速找到最高优先级的lane
return lanes & -lanes;
}
// 标记更新的lane
markUpdateLane(lane) {
this.pendingLanes |= lane;
// 设置过期时间
const index = this.laneToIndex(lane);
const expirationTime = this.computeExpirationTime(lane);
this.expirationTimes[index] = expirationTime;
}
// 计算过期时间
computeExpirationTime(lane) {
const currentTime = getCurrentTime();
if ((lane & SyncLane) !== NoLanes) {
return currentTime; // 同步任务立即过期
} else if ((lane & InputContinuousLane) !== NoLanes) {
return currentTime + 250; // 用户输入250ms后过期
} else if ((lane & DefaultLane) !== NoLanes) {
return currentTime + 5000; // 默认任务5秒后过期
} else if ((lane & TransitionLane1) !== NoLanes) {
return currentTime + 5000; // Transition任务5秒后过期
} else if ((lane & IdleLane) !== NoLanes) {
return maxSigned31BitInt; // 空闲任务永不过期
}
return currentTime + 5000;
}
// 获取过期的lanes
getExpiredLanes() {
const currentTime = getCurrentTime();
let expiredLanes = NoLanes;
for (let i = 0; i < TotalLanes; i++) {
const expirationTime = this.expirationTimes[i];
if (expirationTime !== -1 && expirationTime <= currentTime) {
const lane = 1 << i;
if ((this.pendingLanes & lane) !== NoLanes) {
expiredLanes |= lane;
}
}
}
this.expiredLanes = expiredLanes;
return expiredLanes;
}
// 完成lanes的处理
finishLanes(lanes) {
this.pendingLanes &= ~lanes;
this.suspendedLanes &= ~lanes;
this.pingedLanes &= ~lanes;
this.expiredLanes &= ~lanes;
// 清除过期时间
const remainingLanes = this.pendingLanes;
for (let i = 0; i < TotalLanes; i++) {
const lane = 1 << i;
if ((remainingLanes & lane) === NoLanes) {
this.expirationTimes[i] = -1;
}
}
}
}
并发渲染工作流程:
// 5. 并发渲染调度流程
function renderRootConcurrent(root, lanes) {
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
// 如果这是新的render,重置工作进度
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
prepareFreshStack(root, lanes);
}
do {
try {
workLoopConcurrent();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
resetContextDependencies();
executionContext = prevExecutionContext;
if (workInProgress !== null) {
// 工作被中断,返回进行中状态
return RootInProgress;
} else {
// 工作完成
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
return workInProgressRootExitStatus;
}
}
// 6. 任务调度集成
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
// 检查是否是无限循环更新
checkForNestedUpdates();
// 标记从当前fiber到root的更新lane
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (root === null) {
return null;
}
// 标记root有待处理的更新
markRootUpdated(root, lane, eventTime);
if (lane === SyncLane) {
if ((executionContext & LegacyUnbatchedContext) !== NoContext &&
(executionContext & (RenderContext | CommitContext)) === NoContext) {
// 同步渲染
performSyncWorkOnRoot(root);
} else {
// 批量更新
ensureRootIsScheduled(root, eventTime);
}
} else {
// 异步渲染
ensureRootIsScheduled(root, eventTime);
}
return root;
}
// 确保root被调度
function ensureRootIsScheduled(root, currentTime) {
const existingCallbackNode = root.callbackNode;
// 标记过期的lanes
markStarvedLanesAsExpired(root, currentTime);
// 获取下一个要处理的lanes
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
if (nextLanes === NoLanes) {
// 没有工作要做,取消现有的回调
if (existingCallbackNode !== null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLane;
return;
}
// 获取新任务的优先级
const newCallbackPriority = getHighestPriorityLane(nextLanes);
// 如果优先级没有变化,继续现有的任务
const existingCallbackPriority = root.callbackPriority;
if (existingCallbackPriority === newCallbackPriority) {
return;
}
// 取消现有的回调,安排新的回调
if (existingCallbackNode != null) {
cancelCallback(existingCallbackNode);
}
let newCallbackNode;
if (newCallbackPriority === SyncLane) {
// 同步优先级,同步调度
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root)
);
} else {
// 异步优先级,使用Scheduler调度
const schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;
}
时间切片优化策略:
核心优势:
What are the underlying implementation principles and optimization strategies of React 18's concurrent rendering?
What are the underlying implementation principles and optimization strategies of React 18’s concurrent rendering?
考察点:并发渲染深度。
答案:
React 18并发渲染的底层原理基于Fiber架构的时间切片、Lane模型的优先级调度、以及新的并发特性API。核心实现包括可中断的渲染过程、双缓冲机制、优先级队列管理、以及智能的任务调度算法。优化策略涵盖自动批处理、Suspense边界优化、startTransition降级处理等,实现了在不阻塞用户交互的前提下进行高效渲染。
并发渲染核心架构:
// 1. 双缓冲Fiber树实现
class FiberRootNode {
constructor() {
// 当前显示的Fiber树
this.current = null;
// 正在构建的Fiber树
this.workInProgress = null;
// 待处理的lanes
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
// 调度相关
this.callbackNode = null;
this.callbackPriority = NoLane;
this.eventTimes = new Array(TotalLanes).fill(NoTimestamp);
this.expirationTimes = new Array(TotalLanes).fill(NoTimestamp);
// 并发特性
this.hydrationCallbacks = null;
this.isDehydrated = false;
this.pooledCache = null;
this.incompleteSegments = null;
}
}
// 创建工作进度树
function createWorkInProgress(current, pendingProps) {
let workInProgress = current.alternate;
if (workInProgress === null) {
// 创建新的工作进度节点
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode
);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
// 建立双缓冲连接
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
// 复用现有的工作进度节点
workInProgress.pendingProps = pendingProps;
workInProgress.type = current.type;
// 清除副作用
workInProgress.flags = NoFlags;
workInProgress.subtreeFlags = NoFlags;
workInProgress.deletions = null;
}
// 复制其他属性
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
return workInProgress;
}
并发渲染调度实现:
// 2. 并发渲染主流程
function performConcurrentWorkOnRoot(root, didTimeout) {
// 获取当前时间和下一个要处理的lanes
const originalCallbackNode = root.callbackNode;
const currentTime = getCurrentTime();
// 标记过期的lanes
markStarvedLanesAsExpired(root, currentTime);
// 获取要渲染的lanes
let lanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
if (lanes === NoLanes) {
return null;
}
// 检查是否包含阻塞的lanes
const includesNonIdleWork = includesNonIdleLanes(lanes);
const includesOnlyRetries = includesOnlyRetryLanes(lanes);
const includesOnlyTransitions = includesOnlyTransitionLanes(lanes);
// 决定是否应该并发渲染
const shouldTimeSlice =
!includesBlockingLane(root, lanes) &&
!includesExpiredLane(root, lanes) &&
!didTimeout;
let exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes)
: renderRootSync(root, lanes);
if (exitStatus !== RootInProgress) {
// 渲染完成或出错
if (exitStatus === RootErrored) {
// 处理错误情况
const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
if (errorRetryLanes !== NoLanes) {
lanes = errorRetryLanes;
exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
}
}
if (exitStatus === RootFatalErrored) {
const fatalError = workInProgressRootFatalError;
prepareFreshStack(root, NoLanes);
markRootSuspended(root, lanes);
ensureRootIsScheduled(root, currentTime);
throw fatalError;
}
// 检查是否需要重新调度
if (root.callbackNode === originalCallbackNode) {
ensureRootIsScheduled(root, currentTime);
}
// 提交结果
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
finishConcurrentRender(root, exitStatus, lanes);
}
ensureRootIsScheduled(root, currentTime);
if (root.callbackNode === originalCallbackNode) {
// 任务还没完成,返回continuation
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
}
// 3. 可中断的渲染循环
function renderRootConcurrent(root, lanes) {
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
// 如果是新的渲染或lanes发生变化,准备新的stack
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
if (enableUpdaterTracking) {
if (isDevToolsPresent) {
const memoizedUpdaters = root.memoizedUpdaters;
if (memoizedUpdaters.size > 0) {
restorePendingUpdaters(root, workInProgressRootRenderLanes);
memoizedUpdaters.clear();
}
movePendingFibersToMemoized(root, lanes);
}
}
workInProgressTransitions = getTransitionsForLanes(root, lanes);
prepareFreshStack(root, lanes);
}
do {
try {
workLoopConcurrent();
break;
} catch (thrownValue) {
handleError(root, thrownValue);
}
} while (true);
resetContextDependencies();
executionContext = prevExecutionContext;
if (workInProgress !== null) {
// 还有未完成的工作
return RootInProgress;
} else {
// 工作完成
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
// 返回退出状态
return workInProgressRootExitStatus;
}
}
function workLoopConcurrent() {
// 并发工作循环 - 检查是否需要让出控制权
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
Lane模型优化策略:
// 4. 高级Lane调度算法
class AdvancedLaneScheduler {
constructor() {
this.laneHistory = [];
this.performanceMetrics = new Map();
this.adaptiveThresholds = {
userBlocking: 250,
normal: 5000,
transition: 5000
};
}
// 动态调整优先级
getAdaptivePriority(lane, context) {
const history = this.getLaneHistory(lane);
const avgExecutionTime = this.calculateAverageExecutionTime(lane);
const cpuLoad = this.estimateCPULoad();
// 根据历史表现和当前负载调整优先级
if (avgExecutionTime > this.adaptiveThresholds.userBlocking &&
cpuLoad > 0.8) {
// 如果任务耗时且CPU负载高,降低优先级
return this.downgradePriority(lane);
} else if (avgExecutionTime < 16 && cpuLoad < 0.3) {
// 如果任务轻量且CPU空闲,可以提升优先级
return this.upgradePriority(lane);
}
return lane;
}
// 智能批处理策略
shouldBatchUpdates(updates) {
// 分析更新的相关性
const relatedUpdates = this.analyzeUpdateRelation(updates);
const totalPriority = this.calculateCombinedPriority(updates);
// 如果更新相关且总优先级适中,进行批处理
return relatedUpdates.length > 1 &&
totalPriority <= NormalPriority &&
this.estimatedBatchTime(updates) < 50;
}
// 预测性调度
predictNextScheduling(currentLanes, pendingWork) {
const predictedLanes = this.analyzeTrendingPatterns(currentLanes);
const resourceAvailability = this.estimateResourceAvailability();
// 根据预测结果预调度资源
if (resourceAvailability.memory > 0.7 &&
resourceAvailability.cpu < 0.5) {
this.preWarmScheduling(predictedLanes);
}
return {
recommendedLanes: predictedLanes,
schedulingStrategy: this.getOptimalStrategy(predictedLanes),
estimatedCompletionTime: this.estimateCompletionTime(predictedLanes)
};
}
// 自适应时间切片
getAdaptiveTimeSlice(lane, context) {
const baseTimeSlice = 5; // 基础5ms
const devicePerformance = this.getDevicePerformance();
const taskComplexity = this.estimateTaskComplexity(lane);
let adaptedTimeSlice = baseTimeSlice;
// 根据设备性能调整
if (devicePerformance.tier === 'high') {
adaptedTimeSlice *= 1.5;
} else if (devicePerformance.tier === 'low') {
adaptedTimeSlice *= 0.7;
}
// 根据任务复杂度调整
if (taskComplexity > 0.8) {
adaptedTimeSlice *= 0.8; // 复杂任务使用较短时间片
}
return Math.max(1, Math.min(adaptedTimeSlice, 16)); // 限制在1-16ms范围内
}
}
并发特性优化实现:
// 5. Suspense边界优化
class OptimizedSuspenseBoundary {
constructor() {
this.suspenseState = {
isLoading: false,
hasError: false,
retryCount: 0,
lastRetryTime: 0
};
this.loadingCache = new Map();
this.preloadQueue = new Set();
}
// 智能预加载
intelligentPreload(dependencies) {
const priorityDeps = this.analyzeDependencyPriority(dependencies);
priorityDeps.forEach(dep => {
if (!this.loadingCache.has(dep.id)) {
const loadPromise = this.createOptimizedLoader(dep);
this.loadingCache.set(dep.id, loadPromise);
// 预加载高优先级依赖
if (dep.priority === 'high') {
this.preloadQueue.add(loadPromise);
}
}
});
}
// 渐进式hydration
progressiveHydration(root, strategy = 'user-visible-first') {
const hydrationPlan = this.createHydrationPlan(root, strategy);
return new Promise((resolve) => {
const hydrateNextChunk = () => {
const chunk = hydrationPlan.getNextChunk();
if (chunk) {
startTransition(() => {
this.hydrateChunk(chunk).then(() => {
// 使用调度器安排下一个chunk
scheduleCallback(NormalPriority, hydrateNextChunk);
});
});
} else {
resolve();
}
};
hydrateNextChunk();
});
}
createHydrationPlan(root, strategy) {
const visibilityObserver = new IntersectionObserver(
this.handleVisibilityChange.bind(this)
);
return {
chunks: this.analyzeComponentTree(root, strategy),
currentIndex: 0,
visibilityObserver,
getNextChunk() {
if (this.currentIndex < this.chunks.length) {
return this.chunks[this.currentIndex++];
}
return null;
}
};
}
}
// 6. startTransition优化实现
function optimizedStartTransition(callback, options = {}) {
const {
priority = 'normal',
timeout = 5000,
onTimeout,
retryCount = 0
} = options;
const transitionId = generateTransitionId();
const startTime = getCurrentTime();
// 记录transition开始
markTransitionStart(transitionId, priority);
const [isPending, setIsPending] = useState(false);
const enhancedCallback = useCallback(() => {
setIsPending(true);
const transitionCallback = () => {
try {
callback();
markTransitionComplete(transitionId);
} catch (error) {
markTransitionFailed(transitionId, error);
// 自动重试机制
if (retryCount > 0) {
const delay = Math.min(1000 * Math.pow(2, retryCount), 5000);
setTimeout(() => {
optimizedStartTransition(callback, {
...options,
retryCount: retryCount - 1
});
}, delay);
}
} finally {
setIsPending(false);
}
};
// 设置超时处理
const timeoutId = setTimeout(() => {
if (onTimeout) {
onTimeout();
}
markTransitionTimeout(transitionId);
}, timeout);
// 执行transition
startTransition(() => {
transitionCallback();
clearTimeout(timeoutId);
});
}, [callback, priority, timeout, retryCount]);
return [isPending, enhancedCallback];
}
性能监控与自适应优化:
// 7. 并发渲染性能监控
class ConcurrentRenderingMonitor {
constructor() {
this.metrics = {
renderTimes: [],
interactionTimes: [],
suspenseEvents: [],
transitionMetrics: new Map()
};
this.thresholds = {
slowRender: 16,
slowInteraction: 100,
suspenseTimeout: 1000
};
this.setupObservers();
}
setupObservers() {
// 监控Long Tasks
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'longtask') {
this.recordLongTask(entry);
} else if (entry.entryType === 'measure' &&
entry.name.includes('React')) {
this.recordReactMeasure(entry);
}
});
});
observer.observe({ entryTypes: ['longtask', 'measure'] });
}
// 监控用户交互
this.setupInteractionTracking();
}
recordConcurrentRender(componentName, renderTime, wasInterrupted) {
this.metrics.renderTimes.push({
component: componentName,
duration: renderTime,
interrupted: wasInterrupted,
timestamp: Date.now()
});
// 触发性能告警
if (renderTime > this.thresholds.slowRender) {
this.reportPerformanceIssue('slow-render', {
component: componentName,
duration: renderTime
});
}
// 自动优化建议
if (wasInterrupted && renderTime > 50) {
this.suggestOptimization(componentName, 'consider-memo-or-virtualization');
}
}
// 分析渲染模式
analyzeRenderingPatterns() {
const recentRenders = this.metrics.renderTimes.slice(-100);
const patterns = {
averageRenderTime: this.calculateAverage(recentRenders.map(r => r.duration)),
interruptionRate: recentRenders.filter(r => r.interrupted).length / recentRenders.length,
slowRenderComponents: this.identifySlowComponents(recentRenders),
renderingTrend: this.calculateTrend(recentRenders)
};
return patterns;
}
// 自适应优化建议
generateOptimizationRecommendations() {
const patterns = this.analyzeRenderingPatterns();
const recommendations = [];
if (patterns.averageRenderTime > 20) {
recommendations.push({
type: 'performance',
priority: 'high',
suggestion: 'Consider using React.memo for frequently rendering components',
components: patterns.slowRenderComponents.slice(0, 5)
});
}
if (patterns.interruptionRate > 0.3) {
recommendations.push({
type: 'concurrency',
priority: 'medium',
suggestion: 'High interruption rate detected. Consider using startTransition for non-urgent updates',
affectedPattern: 'frequent-interruptions'
});
}
return recommendations;
}
// 实时性能调优
autoTunePerformance() {
const patterns = this.analyzeRenderingPatterns();
// 动态调整时间切片大小
if (patterns.averageRenderTime > 25) {
// 减少时间切片,增加响应性
this.adjustTimeSlicing('reduce', 0.8);
} else if (patterns.averageRenderTime < 8) {
// 增加时间切片,减少调度开销
this.adjustTimeSlicing('increase', 1.2);
}
// 动态调整批处理策略
if (patterns.interruptionRate > 0.4) {
this.adjustBatchingStrategy('aggressive');
}
}
}
核心优化策略:
性能收益:
How does React 19's compiler optimization reduce runtime overhead?
How does React 19’s compiler optimization reduce runtime overhead?
考察点:编译器优化。
答案:
React 19的编译器优化通过在构建时进行静态分析和代码转换,将原本需要在运行时执行的工作前移到编译阶段,包括自动memorization、依赖项优化、静态提升、死代码消除等。核心目标是减少运行时的计算开销、内存分配和重复渲染,从而显著提升应用性能。
编译器核心优化策略:
// 1. 自动memorization优化
// 编译前代码
function ExpensiveComponent({ data, filter }) {
const processedData = data.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
const handleClick = (item) => {
console.log('Clicked:', item.id);
};
return (
<div>
{processedData.map(item => (
<ItemComponent
key={item.id}
item={item}
onClick={handleClick}
/>
))}
</div>
);
}
// 编译器自动优化后的等效代码
function ExpensiveComponent({ data, filter }) {
// 编译器自动插入useMemo
const processedData = useMemo(() =>
data.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
), [data, filter]);
// 编译器自动插入useCallback
const handleClick = useCallback((item) => {
console.log('Clicked:', item.id);
}, []);
// 编译器识别出ItemComponent可以使用memo优化
const MemoizedItemComponent = memo(ItemComponent);
return (
<div>
{processedData.map(item => (
<MemoizedItemComponent
key={item.id}
item={item}
onClick={handleClick}
/>
))}
</div>
);
}
静态分析和优化转换:
// 2. 静态提升优化
// 编译前代码
function MyComponent({ userList }) {
const staticConfig = {
theme: 'light',
locale: 'en-US',
features: ['search', 'sort', 'filter']
};
const defaultOptions = {
pageSize: 10,
sortBy: 'name',
direction: 'asc'
};
return (
<div>
<UserTable
users={userList}
config={staticConfig}
options={defaultOptions}
/>
</div>
);
}
// 编译器优化:静态对象提升到组件外部
const STATIC_CONFIG = {
theme: 'light',
locale: 'en-US',
features: ['search', 'sort', 'filter']
};
const DEFAULT_OPTIONS = {
pageSize: 10,
sortBy: 'name',
direction: 'asc'
};
function MyComponent({ userList }) {
// 静态对象不再在每次渲染时重新创建
return (
<div>
<UserTable
users={userList}
config={STATIC_CONFIG}
options={DEFAULT_OPTIONS}
/>
</div>
);
}
// 3. 条件渲染优化
// 编译前代码
function ConditionalComponent({ isVisible, data, mode }) {
if (!isVisible) return null;
const processedData = expensiveDataProcessing(data);
const config = getConfigForMode(mode);
return (
<div>
<DataVisualization data={processedData} config={config} />
</div>
);
}
// 编译器优化:条件计算延迟
function ConditionalComponent({ isVisible, data, mode }) {
// 编译器将昂贵计算移到条件检查之后
if (!isVisible) return null;
// 使用缓存避免重复计算
const processedData = useMemo(() =>
expensiveDataProcessing(data), [data]);
const config = useMemo(() =>
getConfigForMode(mode), [mode]);
return (
<div>
<DataVisualization data={processedData} config={config} />
</div>
);
}
依赖项自动优化:
// 4. 智能依赖项分析
class DependencyAnalyzer {
analyzeComponent(componentAST) {
const analysis = {
staticValues: new Set(),
dynamicDeps: new Map(),
computationCosts: new Map(),
optimizationOpportunities: []
};
// 分析JSX中的表达式
this.analyzeJSXExpressions(componentAST, analysis);
// 分析hooks的依赖关系
this.analyzeHookDependencies(componentAST, analysis);
// 生成优化建议
return this.generateOptimizations(analysis);
}
analyzeJSXExpressions(ast, analysis) {
ast.body.forEach(node => {
if (node.type === 'JSXElement') {
node.attributes.forEach(attr => {
if (attr.value && attr.value.type === 'JSXExpressionContainer') {
const expr = attr.value.expression;
// 识别可以提升的静态表达式
if (this.isStaticExpression(expr)) {
analysis.staticValues.add(expr);
}
// 分析复杂计算
if (this.isExpensiveComputation(expr)) {
analysis.computationCosts.set(expr, this.estimateCost(expr));
analysis.optimizationOpportunities.push({
type: 'memoization',
expression: expr,
estimatedSaving: this.estimateSaving(expr)
});
}
}
});
}
});
}
generateOptimizations(analysis) {
const optimizations = [];
// 生成静态提升优化
analysis.staticValues.forEach(value => {
optimizations.push({
type: 'static-hoisting',
target: value,
transformation: this.generateHoistingCode(value)
});
});
// 生成memorization优化
analysis.computationCosts.forEach((cost, expr) => {
if (cost > this.MEMOIZATION_THRESHOLD) {
optimizations.push({
type: 'auto-memoization',
target: expr,
transformation: this.generateMemoizationCode(expr)
});
}
});
return optimizations;
}
}
// 5. 编译时组件优化
function compileTimeOptimization(ComponentCode) {
const analyzer = new DependencyAnalyzer();
const ast = parseComponent(ComponentCode);
const optimizations = analyzer.analyzeComponent(ast);
let optimizedCode = ComponentCode;
optimizations.forEach(optimization => {
switch (optimization.type) {
case 'static-hoisting':
optimizedCode = this.applyStaticHoisting(optimizedCode, optimization);
break;
case 'auto-memoization':
optimizedCode = this.applyAutoMemoization(optimizedCode, optimization);
break;
case 'dependency-optimization':
optimizedCode = this.optimizeDependencies(optimizedCode, optimization);
break;
}
});
return optimizedCode;
}
运行时性能监控集成:
// 6. 编译器集成的性能监控
function createOptimizedComponent(originalComponent, optimizationMetadata) {
return function OptimizedComponent(props) {
// 编译器注入的性能监控代码
const renderStartTime = performance.now();
const componentName = originalComponent.name || 'Anonymous';
// 执行优化后的组件逻辑
const result = originalComponent(props);
const renderEndTime = performance.now();
const renderDuration = renderEndTime - renderStartTime;
// 记录性能数据
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined') {
__REACT_DEVTOOLS_GLOBAL_HOOK__.onCommitFiberRoot(
null,
{
componentName,
renderDuration,
optimizations: optimizationMetadata.appliedOptimizations,
estimatedSavings: optimizationMetadata.estimatedSavings
}
);
}
// 开发环境下的优化建议
if (process.env.NODE_ENV === 'development') {
if (renderDuration > 16) {
console.warn(
`Slow render detected in ${componentName}: ${renderDuration}ms`,
optimizationMetadata.suggestions
);
}
}
return result;
};
}
// 7. Bundle大小优化
class BundleOptimizer {
constructor() {
this.deadCodeMap = new Map();
this.usageStats = new Map();
this.importGraph = new Map();
}
analyzeBundleSize(componentTree) {
// 分析组件树,识别未使用的代码
this.buildImportGraph(componentTree);
this.trackComponentUsage(componentTree);
this.identifyDeadCode();
return {
unusedComponents: this.getUnusedComponents(),
optimizationOpportunities: this.getOptimizationOpportunities(),
estimatedSavings: this.calculateSavings()
};
}
generateTreeShakingHints(analysis) {
const hints = [];
// 生成tree-shaking友好的导出
analysis.unusedComponents.forEach(component => {
hints.push({
type: 'unused-export',
component: component.name,
suggestion: 'Mark as side-effect free for tree-shaking'
});
});
// 建议代码分割点
analysis.largeComponents.forEach(component => {
if (component.size > this.CODE_SPLITTING_THRESHOLD) {
hints.push({
type: 'code-splitting',
component: component.name,
suggestion: 'Consider lazy loading this component'
});
}
});
return hints;
}
}
高级编译优化技术:
// 8. 常量折叠和内联优化
class AdvancedOptimizer {
constantFolding(expression) {
// 编译时计算常量表达式
if (this.isConstantExpression(expression)) {
try {
const result = this.evaluateConstant(expression);
return this.createLiteralNode(result);
} catch (e) {
return expression; // 保持原样如果计算失败
}
}
return expression;
}
functionInlining(callExpression) {
const callee = callExpression.callee;
// 内联小型纯函数
if (this.isInlinableFunction(callee)) {
const inlinedCode = this.inlineFunction(callee, callExpression.arguments);
return inlinedCode;
}
return callExpression;
}
loopOptimization(loopNode) {
// 循环展开优化
if (this.canUnrollLoop(loopNode)) {
return this.unrollLoop(loopNode);
}
// 循环提升优化
if (this.hasInvariantComputations(loopNode)) {
return this.hoistInvariants(loopNode);
}
return loopNode;
}
// JSX优化
optimizeJSX(jsxElement) {
const optimizations = [];
// 静态子元素优化
if (this.hasOnlyStaticChildren(jsxElement)) {
optimizations.push(this.markAsStatic(jsxElement));
}
// 键值优化
if (this.isInLoop(jsxElement) && !this.hasOptimalKey(jsxElement)) {
optimizations.push(this.optimizeKey(jsxElement));
}
// 属性优化
jsxElement.attributes.forEach(attr => {
if (this.canOptimizeAttribute(attr)) {
optimizations.push(this.optimizeAttribute(attr));
}
});
return this.applyOptimizations(jsxElement, optimizations);
}
}
// 9. 编译器优化配置
const compilerConfig = {
optimizations: {
autoMemoization: {
enabled: true,
threshold: 10, // ms
excludePatterns: ['test/**', '**/*.test.js']
},
staticHoisting: {
enabled: true,
hoistLevel: 'component' // 'component' | 'module' | 'global'
},
deadCodeElimination: {
enabled: true,
aggressive: false
},
constantFolding: {
enabled: true,
mathOptimizations: true
},
bundleOptimization: {
treeshaking: true,
minification: true,
codeSplitting: 'automatic'
}
},
performance: {
budgets: {
maxRenderTime: 16,
maxBundleSize: '500kb',
maxComponentSize: '50kb'
},
monitoring: {
enabled: true,
reportThreshold: 50 // ms
}
}
};
编译器优化收益:
性能提升指标:
具体优化效果:
开发体验改善:
How to design a virtual scrolling component that supports tens of millions of data?
How to design a virtual scrolling component that supports tens of millions of data?
考察点:大数据处理。
答案:
支持千万级数据的虚拟滚动组件需要综合考虑内存管理、渲染优化、数据分片、智能预加载等多个方面。核心思路是只渲染可视区域内的元素,通过高效的索引计算、内存回收、异步数据加载等技术,实现流畅的大数据列表滚动体验。
核心架构设计:
// 1. 高性能虚拟滚动核心实现
class VirtualScrollEngine {
constructor(options = {}) {
this.itemHeight = options.itemHeight || 50;
this.containerHeight = options.containerHeight || 400;
this.overscan = options.overscan || 5;
this.totalItems = 0;
this.scrollTop = 0;
// 性能优化相关
this.renderBuffer = new Map();
this.recyclePool = [];
this.visibleRange = { start: 0, end: 0 };
// 内存管理
this.maxCacheSize = 1000;
this.memoryThreshold = 100 * 1024 * 1024; // 100MB
this.initializeEngine();
}
// 计算可见范围的核心算法
calculateVisibleRange(scrollTop, containerHeight) {
const startIndex = Math.floor(scrollTop / this.itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / this.itemHeight) + this.overscan,
this.totalItems - 1
);
return {
start: Math.max(0, startIndex - this.overscan),
end: endIndex,
visibleStart: startIndex,
visibleEnd: Math.min(
startIndex + Math.ceil(containerHeight / this.itemHeight),
this.totalItems - 1
)
};
}
// 高效的DOM节点回收机制
recycleNodes(oldRange, newRange) {
const recyclable = [];
// 标记不再需要的节点
for (let i = oldRange.start; i < newRange.start; i++) {
if (this.renderBuffer.has(i)) {
recyclable.push(this.renderBuffer.get(i));
this.renderBuffer.delete(i);
}
}
for (let i = newRange.end + 1; i <= oldRange.end; i++) {
if (this.renderBuffer.has(i)) {
recyclable.push(this.renderBuffer.get(i));
this.renderBuffer.delete(i);
}
}
// 将节点放入回收池
recyclable.forEach(node => {
this.recyclePool.push(node);
});
// 控制回收池大小
if (this.recyclePool.length > this.maxCacheSize) {
this.recyclePool.splice(0, this.recyclePool.length - this.maxCacheSize);
}
}
// 内存使用监控和管理
monitorMemoryUsage() {
if (performance.memory) {
const memoryUsage = performance.memory.usedJSHeapSize;
if (memoryUsage > this.memoryThreshold) {
this.triggerMemoryCleanup();
}
return {
used: memoryUsage,
total: performance.memory.totalJSHeapSize,
percentage: (memoryUsage / performance.memory.totalJSHeapSize) * 100
};
}
return null;
}
triggerMemoryCleanup() {
// 清理渲染缓冲
const currentRange = this.visibleRange;
const keepRange = {
start: currentRange.start - 20,
end: currentRange.end + 20
};
for (const [index, node] of this.renderBuffer) {
if (index < keepRange.start || index > keepRange.end) {
this.renderBuffer.delete(index);
}
}
// 清理回收池
this.recyclePool.length = Math.min(this.recyclePool.length, 50);
// 强制垃圾回收(如果可用)
if (window.gc) {
window.gc();
}
}
}
React组件实现:
// 2. React虚拟滚动组件
function VirtualList({
items,
itemHeight = 50,
height = 400,
width = '100%',
renderItem,
onScroll,
loadMoreItems,
hasNextPage = false
}) {
const [scrollTop, setScrollTop] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const containerRef = useRef(null);
const scrollElementRef = useRef(null);
const engine = useMemo(() => new VirtualScrollEngine({
itemHeight,
containerHeight: height,
overscan: 5
}), [itemHeight, height]);
// 计算可见范围
const visibleRange = useMemo(() => {
return engine.calculateVisibleRange(scrollTop, height);
}, [scrollTop, height, engine]);
// 可见项目列表
const visibleItems = useMemo(() => {
const result = [];
for (let i = visibleRange.start; i <= visibleRange.end; i++) {
if (items[i]) {
result.push({
index: i,
item: items[i],
top: i * itemHeight
});
}
}
return result;
}, [items, visibleRange, itemHeight]);
// 滚动处理(防抖优化)
const handleScroll = useCallback(
throttle((event) => {
const newScrollTop = event.target.scrollTop;
setScrollTop(newScrollTop);
onScroll?.(event);
// 检查是否需要加载更多数据
const scrollPercentage = newScrollTop / (items.length * itemHeight - height);
if (scrollPercentage > 0.8 && hasNextPage && !isLoading) {
setIsLoading(true);
loadMoreItems?.().finally(() => setIsLoading(false));
}
}, 16), // 60fps
[items.length, itemHeight, height, hasNextPage, isLoading, onScroll, loadMoreItems]
);
// 监控性能
useEffect(() => {
const interval = setInterval(() => {
const memoryInfo = engine.monitorMemoryUsage();
if (memoryInfo && memoryInfo.percentage > 80) {
console.warn('High memory usage detected:', memoryInfo);
}
}, 5000);
return () => clearInterval(interval);
}, [engine]);
return (
<div
ref={containerRef}
style={{
height,
width,
overflow: 'auto',
position: 'relative'
}}
onScroll={handleScroll}
>
{/* 总高度占位器 */}
<div
style={{
height: items.length * itemHeight,
position: 'relative'
}}
>
{/* 渲染可见项目 */}
{visibleItems.map(({ index, item, top }) => (
<div
key={index}
style={{
position: 'absolute',
top,
left: 0,
right: 0,
height: itemHeight
}}
>
{renderItem({ item, index })}
</div>
))}
{/* 加载指示器 */}
{isLoading && (
<div
style={{
position: 'absolute',
top: items.length * itemHeight,
left: 0,
right: 0,
height: 50,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
Loading more items...
</div>
)}
</div>
</div>
);
}
// 3. 数据分片和异步加载
class DataChunkManager {
constructor(options = {}) {
this.chunkSize = options.chunkSize || 1000;
this.prefetchSize = options.prefetchSize || 2;
this.cache = new Map();
this.loadingChunks = new Set();
this.totalItems = 0;
}
async getItem(index) {
const chunkIndex = Math.floor(index / this.chunkSize);
const itemIndex = index % this.chunkSize;
// 检查缓存
if (this.cache.has(chunkIndex)) {
const chunk = this.cache.get(chunkIndex);
return chunk[itemIndex];
}
// 加载chunk
await this.loadChunk(chunkIndex);
const chunk = this.cache.get(chunkIndex);
return chunk ? chunk[itemIndex] : null;
}
async loadChunk(chunkIndex) {
if (this.loadingChunks.has(chunkIndex)) {
// 等待已在加载的chunk
return new Promise(resolve => {
const checkLoaded = () => {
if (!this.loadingChunks.has(chunkIndex)) {
resolve();
} else {
setTimeout(checkLoaded, 10);
}
};
checkLoaded();
});
}
this.loadingChunks.add(chunkIndex);
try {
const startIndex = chunkIndex * this.chunkSize;
const endIndex = Math.min(startIndex + this.chunkSize, this.totalItems);
// 模拟异步数据加载
const chunkData = await this.fetchChunkData(startIndex, endIndex);
this.cache.set(chunkIndex, chunkData);
// 预加载相邻chunk
this.prefetchAdjacentChunks(chunkIndex);
} finally {
this.loadingChunks.delete(chunkIndex);
}
}
prefetchAdjacentChunks(centerChunkIndex) {
for (let i = 1; i <= this.prefetchSize; i++) {
const prevChunk = centerChunkIndex - i;
const nextChunk = centerChunkIndex + i;
if (prevChunk >= 0 && !this.cache.has(prevChunk) && !this.loadingChunks.has(prevChunk)) {
this.loadChunk(prevChunk);
}
if (nextChunk * this.chunkSize < this.totalItems && !this.cache.has(nextChunk) && !this.loadingChunks.has(nextChunk)) {
this.loadChunk(nextChunk);
}
}
}
async fetchChunkData(start, end) {
// 实际的数据获取逻辑
const response = await fetch(`/api/data?start=${start}&end=${end}`);
return response.json();
}
// 内存管理:清理远程chunk
cleanupDistantChunks(currentChunkIndex) {
const keepRange = 10; // 保持当前chunk前后10个chunk
for (const [chunkIndex] of this.cache) {
if (Math.abs(chunkIndex - currentChunkIndex) > keepRange) {
this.cache.delete(chunkIndex);
}
}
}
}
高级优化策略:
// 4. 智能预测和预加载
class IntelligentPreloader {
constructor() {
this.scrollHistory = [];
this.scrollVelocity = 0;
this.scrollDirection = 0; // 1: down, -1: up, 0: stopped
this.predictionAccuracy = 0.85;
}
analyzeScrollPattern(currentScrollTop, timestamp) {
this.scrollHistory.push({ scrollTop: currentScrollTop, timestamp });
// 保持最近100个滚动记录
if (this.scrollHistory.length > 100) {
this.scrollHistory.shift();
}
if (this.scrollHistory.length >= 2) {
const current = this.scrollHistory[this.scrollHistory.length - 1];
const previous = this.scrollHistory[this.scrollHistory.length - 2];
const deltaScroll = current.scrollTop - previous.scrollTop;
const deltaTime = current.timestamp - previous.timestamp;
this.scrollVelocity = deltaTime > 0 ? deltaScroll / deltaTime : 0;
this.scrollDirection = deltaScroll > 0 ? 1 : deltaScroll < 0 ? -1 : 0;
}
}
predictNextVisibleRange(currentRange, itemHeight) {
const velocityBasedOffset = this.scrollVelocity * 200; // 预测200ms后的位置
const predictedScrollTop = currentRange.start * itemHeight + velocityBasedOffset;
const predictedStartIndex = Math.max(0, Math.floor(predictedScrollTop / itemHeight));
const predictedEndIndex = predictedStartIndex + (currentRange.end - currentRange.start);
return {
start: predictedStartIndex,
end: predictedEndIndex,
confidence: this.calculatePredictionConfidence()
};
}
calculatePredictionConfidence() {
if (this.scrollHistory.length < 10) return 0;
// 基于滚动历史计算预测置信度
const recentVelocities = this.scrollHistory.slice(-10).map((record, index, arr) => {
if (index === 0) return 0;
const prev = arr[index - 1];
return (record.scrollTop - prev.scrollTop) / (record.timestamp - prev.timestamp);
});
const velocityVariance = this.calculateVariance(recentVelocities);
return Math.max(0, 1 - velocityVariance / 1000); // 归一化到0-1
}
}
// 5. 多列虚拟网格支持
function VirtualGrid({
items,
itemWidth = 200,
itemHeight = 150,
columns = 'auto',
gap = 10,
height = 400,
width = '100%',
renderItem
}) {
const [scrollTop, setScrollTop] = useState(0);
const [containerWidth, setContainerWidth] = useState(0);
const containerRef = useRef(null);
// 计算实际列数
const actualColumns = useMemo(() => {
if (columns === 'auto') {
return Math.floor((containerWidth + gap) / (itemWidth + gap)) || 1;
}
return columns;
}, [columns, containerWidth, itemWidth, gap]);
// 计算行数
const totalRows = Math.ceil(items.length / actualColumns);
// 计算可见范围
const visibleRange = useMemo(() => {
const startRow = Math.floor(scrollTop / (itemHeight + gap));
const endRow = Math.min(
startRow + Math.ceil(height / (itemHeight + gap)) + 2,
totalRows - 1
);
return {
startRow: Math.max(0, startRow - 2),
endRow,
startIndex: Math.max(0, startRow - 2) * actualColumns,
endIndex: Math.min((endRow + 1) * actualColumns - 1, items.length - 1)
};
}, [scrollTop, height, itemHeight, gap, totalRows, actualColumns, items.length]);
// 可见项目
const visibleItems = useMemo(() => {
const result = [];
for (let i = visibleRange.startIndex; i <= visibleRange.endIndex; i++) {
if (items[i]) {
const row = Math.floor(i / actualColumns);
const col = i % actualColumns;
result.push({
index: i,
item: items[i],
top: row * (itemHeight + gap),
left: col * (itemWidth + gap)
});
}
}
return result;
}, [items, visibleRange, actualColumns, itemHeight, itemWidth, gap]);
// 监听容器宽度变化
useEffect(() => {
const resizeObserver = new ResizeObserver(entries => {
const { width } = entries[0].contentRect;
setContainerWidth(width);
});
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
return () => resizeObserver.disconnect();
}, []);
return (
<div
ref={containerRef}
style={{
height,
width,
overflow: 'auto',
position: 'relative'
}}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div
style={{
height: totalRows * (itemHeight + gap),
position: 'relative'
}}
>
{visibleItems.map(({ index, item, top, left }) => (
<div
key={index}
style={{
position: 'absolute',
top,
left,
width: itemWidth,
height: itemHeight
}}
>
{renderItem({ item, index })}
</div>
))}
</div>
</div>
);
}
性能监控和优化:
// 6. 性能监控仪表板
class VirtualScrollPerformanceMonitor {
constructor() {
this.metrics = {
renderTimes: [],
scrollEvents: 0,
memoryUsage: [],
frameDrops: 0
};
this.rafId = null;
this.lastFrameTime = performance.now();
this.startMonitoring();
}
startMonitoring() {
const checkFrame = () => {
const now = performance.now();
const frameTime = now - this.lastFrameTime;
if (frameTime > 16.67) { // 超过60fps
this.metrics.frameDrops++;
}
this.lastFrameTime = now;
this.rafId = requestAnimationFrame(checkFrame);
};
checkFrame();
}
recordRenderTime(startTime, endTime) {
const renderTime = endTime - startTime;
this.metrics.renderTimes.push(renderTime);
// 保持最近1000次记录
if (this.metrics.renderTimes.length > 1000) {
this.metrics.renderTimes.shift();
}
}
generatePerformanceReport() {
const avgRenderTime = this.metrics.renderTimes.reduce((a, b) => a + b, 0) / this.metrics.renderTimes.length;
const maxRenderTime = Math.max(...this.metrics.renderTimes);
return {
averageRenderTime: avgRenderTime.toFixed(2),
maxRenderTime: maxRenderTime.toFixed(2),
totalScrollEvents: this.metrics.scrollEvents,
frameDropRate: (this.metrics.frameDrops / (this.metrics.renderTimes.length || 1) * 100).toFixed(2),
recommendations: this.generateRecommendations(avgRenderTime, maxRenderTime)
};
}
generateRecommendations(avgTime, maxTime) {
const recommendations = [];
if (avgTime > 5) {
recommendations.push('Consider reducing item rendering complexity');
}
if (maxTime > 50) {
recommendations.push('Implement progressive rendering for complex items');
}
if (this.metrics.frameDrops > 10) {
recommendations.push('Increase overscan or reduce scroll sensitivity');
}
return recommendations;
}
}
核心优化收益:
What is the streaming rendering mechanism of React Server Components?
What is the streaming rendering mechanism of React Server Components?
考察点:流式渲染原理。
答案:
React Server Components的流式渲染机制通过将服务端渲染的结果以流的形式分块传输到客户端,实现渐进式页面加载。核心包括HTML流输出、Suspense边界分割、并行组件渲染、客户端增量hydration等,让用户能够更快看到页面内容,提升感知性能。
核心实现原理:
// 服务端流式渲染
async function renderToStream(element) {
const stream = new ReadableStream({
async start(controller) {
const renderer = createServerRenderer();
// 渐进式渲染组件树
for await (const chunk of renderer.renderToChunks(element)) {
controller.enqueue(chunk);
}
controller.close();
}
});
return stream;
}
// 客户端增量hydration
function progressiveHydration(root, serverData) {
const hydrationQueue = new PriorityQueue();
// 按优先级hydration可见组件
serverData.components.forEach(component => {
if (isInViewport(component.selector)) {
hydrationQueue.enqueue(component, 'high');
} else {
hydrationQueue.enqueue(component, 'low');
}
});
}
What are the isolation and communication strategies for React applications in micro-frontend architecture?
What are the isolation and communication strategies for React applications in micro-frontend architecture?
考察点:微前端架构。
答案:
微前端架构中React应用需要实现样式隔离、JavaScript隔离、状态隔离,同时提供高效的通信机制。主要策略包括Shadow DOM隔离、沙盒容器、事件总线通信、共享状态管理等,确保各应用独立运行且能协同工作。
隔离策略实现:
// 应用隔离容器
class MicroAppContainer {
constructor(appConfig) {
this.appId = appConfig.id;
this.shadowRoot = this.createShadowDOM();
this.sandbox = this.createSandbox();
this.eventBus = new EventBus();
}
createSandbox() {
return new Proxy(window, {
get(target, prop) {
// 隔离全局变量访问
if (prop in this.isolatedGlobals) {
return this.isolatedGlobals[prop];
}
return target[prop];
}
});
}
loadApp(entry) {
return this.sandbox.eval(entry).then(app => {
ReactDOM.render(app, this.shadowRoot);
});
}
}
// 跨应用通信
class MicroFrontendBridge {
constructor() {
this.eventBus = new EventBus();
this.sharedStore = new SharedStateManager();
}
// 事件通信
publishEvent(event, data) {
this.eventBus.emit(`micro:${event}`, data);
}
// 共享状态
shareState(key, value) {
this.sharedStore.set(key, value);
this.eventBus.emit('state:change', { key, value });
}
}
How is the priority scheduling of the Lane model implemented in React source code?
How is the priority scheduling of the Lane model implemented in React source code?
考察点:源码级调度。
答案:
React Lane模型使用31位二进制数表示不同优先级的更新通道,通过位运算实现高效的优先级计算和调度。包括Lane分配、合并、优先级计算、过期处理等核心算法,实现了灵活的并发渲染调度。
Lane调度核心实现:
// Lane常量定义
const NoLanes = 0b0000000000000000000000000000000;
const SyncLane = 0b0000000000000000000000000000001;
const InputContinuousLane = 0b0000000000000000000000000000010;
const DefaultLane = 0b0000000000000000000000000010000;
// Lane优先级调度
function getNextLanes(root, wipLanes) {
const pendingLanes = root.pendingLanes;
if (pendingLanes === NoLanes) return NoLanes;
// 获取最高优先级的lanes
const highestPriorityLanes = getHighestPriorityLanes(pendingLanes);
// 处理过期的lanes
const expiredLanes = getExpiredLanes(root);
if (expiredLanes !== NoLanes) {
return expiredLanes;
}
return highestPriorityLanes;
}
// 位运算优先级计算
function getHighestPriorityLanes(lanes) {
// 使用位运算快速找到最高优先级
return lanes & -lanes;
}
What are the on-demand loading and tree-shaking strategies for component libraries in large applications?
What are the on-demand loading and tree-shaking strategies for component libraries in large applications?
考察点:模块化架构。
答案:
大型应用组件库优化需要实现精确的按需加载和tree-shaking,包括ESM模块化设计、sideEffects标记、动态导入、webpack优化配置等策略,最大化减少bundle大小。
按需加载实现:
// 组件库ESM结构设计
// components/index.js
export { default as Button } from './Button';
export { default as Modal } from './Modal';
export { default as Table } from './Table';
// 按需导入配置
// babel-plugin-import配置
{
"libraryName": "my-ui-lib",
"libraryDirectory": "es",
"style": true
}
// Webpack tree-shaking优化
module.exports = {
optimization: {
usedExports: true,
sideEffects: false
},
resolve: {
mainFields: ['module', 'main']
}
};
// 动态组件加载
const LazyTable = lazy(() =>
import('./components/Table').then(module => ({
default: module.Table
}))
);
What are the performance optimization solutions for React and WebAssembly integration?
What are the performance optimization solutions for React and WebAssembly integration?
考察点:高性能集成。
答案:
React与WebAssembly集成通过将计算密集型任务迁移到WASM模块,实现性能提升。关键优化包括内存管理、数据传输优化、Worker线程集成、缓存策略等。
WASM集成优化:
// WASM模块封装
class WASMProcessor {
constructor() {
this.module = null;
this.memory = null;
this.worker = new Worker('/wasm-worker.js');
}
async initialize() {
const wasmModule = await WebAssembly.instantiateStreaming(
fetch('/processor.wasm')
);
this.module = wasmModule.instance;
this.memory = this.module.exports.memory;
}
// 优化的数据传输
processLargeDataset(data) {
return new Promise((resolve) => {
// 使用SharedArrayBuffer避免数据拷贝
const sharedBuffer = new SharedArrayBuffer(data.byteLength);
const sharedView = new Float32Array(sharedBuffer);
sharedView.set(new Float32Array(data));
this.worker.postMessage({
command: 'process',
buffer: sharedBuffer
});
this.worker.onmessage = (e) => {
resolve(e.data.result);
};
});
}
}
// React组件集成
function DataVisualization({ dataset }) {
const [processedData, setProcessedData] = useState(null);
const wasmProcessor = useRef(new WASMProcessor());
useEffect(() => {
wasmProcessor.current.initialize();
}, []);
const processData = useCallback(async () => {
const result = await wasmProcessor.current.processLargeDataset(dataset);
setProcessedData(result);
}, [dataset]);
return (
<div>
<button onClick={processData}>Process with WASM</button>
{processedData && <Chart data={processedData} />}
</div>
);
}
How do React 19's Server Actions change data flow architecture?
How do React 19’s Server Actions change data flow architecture?
考察点:数据流演进。
答案:
React 19 Server Actions通过直接在组件中定义服务端函数,简化了客户端-服务端数据交互,改变传统的API调用模式,实现了更直观的数据流架构。
Server Actions实现:
// 服务端Action定义
'use server';
async function updateUser(formData) {
const userId = formData.get('userId');
const name = formData.get('name');
// 直接数据库操作
await db.user.update({
where: { id: userId },
data: { name }
});
revalidatePath('/users');
}
// 客户端使用
function UserForm({ user }) {
const [pending, startTransition] = useTransition();
return (
<form action={updateUser}>
<input name="userId" value={user.id} type="hidden" />
<input name="name" defaultValue={user.name} />
<button type="submit" disabled={pending}>
{pending ? 'Updating...' : 'Update'}
</button>
</form>
);
}
How to implement a pluggable React component system architecture?
How to implement a pluggable React component system architecture?
考察点:插件化架构。
答案:
可插拔React组件系统需要实现插件注册机制、组件动态加载、扩展点定义、依赖管理等核心功能,支持运行时组件扩展和定制。
插件系统实现:
class PluginSystem {
constructor() {
this.plugins = new Map();
this.extensionPoints = new Map();
this.components = new Map();
}
registerPlugin(plugin) {
this.plugins.set(plugin.id, plugin);
// 注册组件扩展
plugin.extensions?.forEach(ext => {
this.registerExtension(ext.point, ext.component);
});
}
getExtensions(point) {
return this.extensionPoints.get(point) || [];
}
}
// 插件定义
const chartPlugin = {
id: 'chart-plugin',
extensions: [
{
point: 'dashboard.widgets',
component: lazy(() => import('./ChartWidget'))
}
]
};
// 动态组件渲染
function ExtensibleDashboard() {
const extensions = pluginSystem.getExtensions('dashboard.widgets');
return (
<div>
{extensions.map(Extension => (
<Suspense fallback={<Loading />}>
<Extension key={Extension.id} />
</Suspense>
))}
</div>
);
}
What are the memory leak troubleshooting and performance monitoring systems for React applications?
What are the memory leak troubleshooting and performance monitoring systems for React applications?
考察点:性能监控体系。
答案:
React应用内存泄漏排查需要建立完整的监控体系,包括内存使用追踪、组件生命周期监控、事件监听器检查、定时器管理等,配合工具进行自动化检测和告警。
监控体系实现:
class MemoryLeakDetector {
constructor() {
this.componentInstances = new WeakSet();
this.eventListeners = new Map();
this.timers = new Set();
this.observers = new Set();
}
trackComponent(component) {
this.componentInstances.add(component);
// Hook组件清理检查
const originalUseEffect = component.useEffect;
component.useEffect = (effect, deps) => {
const cleanup = originalUseEffect(effect, deps);
// 检查是否返回清理函数
if (typeof effect === 'function' && !cleanup) {
console.warn('Potential memory leak: useEffect without cleanup');
}
return cleanup;
};
}
detectLeaks() {
return {
memoryUsage: performance.memory?.usedJSHeapSize || 0,
unclearedTimers: this.timers.size,
activeListeners: this.eventListeners.size,
recommendations: this.generateRecommendations()
};
}
}
What are the rendering optimization strategies for React and native app hybrid development?
What are the rendering optimization strategies for React and native app hybrid development?
考察点:混合开发优化。
答案:
React混合应用需要优化WebView渲染性能、原生-Web通信效率、资源加载策略等。核心策略包括预渲染、增量更新、原生组件桥接、离线缓存等。
混合渲染优化:
// WebView预渲染
class HybridRenderer {
constructor() {
this.preloadedViews = new Map();
this.nativeBridge = new NativeBridge();
}
preloadReactView(route) {
const webview = this.createWebView();
webview.loadURL(`/preload/${route}`);
this.preloadedViews.set(route, webview);
}
showReactView(route, data) {
let webview = this.preloadedViews.get(route);
if (webview) {
// 使用预加载的视图
webview.postMessage({ type: 'updateData', data });
this.nativeBridge.showWebView(webview);
} else {
// 动态加载
webview = this.createWebView();
webview.loadURL(`/app/${route}?data=${JSON.stringify(data)}`);
}
}
}
// 原生组件桥接
const NativeButton = React.forwardRef((props, ref) => {
useEffect(() => {
// 创建原生按钮实例
const nativeButton = NativeBridge.createButton(props);
return () => {
NativeBridge.destroyButton(nativeButton);
};
}, []);
return <div ref={ref} className="native-button-placeholder" />;
});
How does React 19's Asset Loading API optimize resource loading?
How does React 19’s Asset Loading API optimize resource loading?
考察点:资源加载优化。
答案:
React 19 Asset Loading API通过声明式的资源预加载、优先级管理、缓存策略等优化资源加载性能。支持图片、字体、脚本等多种资源类型的智能加载和管理。
Asset Loading实现:
// 资源预加载Hook
function useAssetLoading() {
const preloadImage = useCallback((src, priority = 'normal') => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = resolve;
img.onerror = reject;
img.loading = priority === 'high' ? 'eager' : 'lazy';
img.src = src;
});
}, []);
const preloadFont = useCallback((fontFamily, src) => {
const font = new FontFace(fontFamily, `url(${src})`);
return font.load().then(() => {
document.fonts.add(font);
});
}, []);
return { preloadImage, preloadFont };
}
// 智能资源管理器
class AssetManager {
constructor() {
this.cache = new Map();
this.loadingQueue = new PriorityQueue();
this.observer = new IntersectionObserver(this.handleVisibility.bind(this));
}
loadAsset(url, options = {}) {
if (this.cache.has(url)) {
return Promise.resolve(this.cache.get(url));
}
const { priority = 'normal', preload = false } = options;
const loadPromise = this.createLoadPromise(url, priority);
this.cache.set(url, loadPromise);
return loadPromise;
}
preloadCriticalAssets(assets) {
assets.forEach(asset => {
this.loadingQueue.enqueue({
url: asset.url,
priority: 'high',
type: asset.type
});
});
this.processQueue();
}
}
// 组件中使用
function OptimizedImageGallery({ images }) {
const { preloadImage } = useAssetLoading();
const [visibleImages, setVisibleImages] = useState(new Set());
useEffect(() => {
// 预加载首屏图片
const firstScreenImages = images.slice(0, 6);
firstScreenImages.forEach(img => {
preloadImage(img.src, 'high');
});
}, [images, preloadImage]);
return (
<div className="image-gallery">
{images.map((img, index) => (
<LazyImage
key={img.id}
src={img.src}
priority={index < 6 ? 'high' : 'normal'}
/>
))}
</div>
);
}