Этот вопрос проверяет знание различных техник и подходов для улучшения производительности React-приложений на разных уровнях архитектуры.
Способы оптимизации React-приложений включают: мемоизацию компонентов (React.memo, useMemo, useCallback), правильное использование ключей, код-сплиттинг, ленивую загрузку, виртуализацию списков, оптимизацию состояния, использование PureComponent, и устранение лишних ререндеров. Также важна оптимизация сборки и использование инструментов профилирования.
Оптимизация React-приложений охватывает различные аспекты от рендеринга компонентов до загрузки приложения.
javascript
// React.memo для функциональных компонентов
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
return <div>{data}</div>;
});
// useMemo для мемоизации значений
function Component({ items }) {
const computedValue = useMemo(() => {
return items.filter(item => item.active).length;
}, [items]); // Пересчитывается только при изменении items
return <div>{computedValue}</div>;
}
// useCallback для мемоизации функций
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []); // Стабильная ссылка на функцию
return <Child onClick={handleClick} />;
}javascript
// Плохо - индекс как ключ
{items.map((item, index) => (
<ListItem key={index} item={item} />
))}
// Хорошо - уникальный ID
{items.map(item => (
<ListItem key={item.id} item={item} />
))}javascript
// Плохо - состояние в родителе
function Parent() {
const [inputValue, setInputValue] = useState('');
return (
<div>
<Input value={inputValue} onChange={setInputValue} />
<OtherComponent />
</div>
);
}
// Хорошо - состояние в компоненте
function Input() {
const [value, setValue] = useState('');
return <input value={value} onChange={e => setValue(e.target.value)} />;
}javascript
// Плохо - вложенные объекты
const [user, setUser] = useState({
profile: { name: '', age: 0 },
settings: { theme: 'light' }
});
// Лучше - нормализованное состояние
const [userName, setUserName] = useState('');
const [userAge, setUserAge] = useState(0);
const [theme, setTheme] = useState('light');javascript
// Динамический импорт
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<LazyComponent />
</Suspense>
);
}
// Разделение по маршрутам
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));javascript
// Предзагрузка при наведении
function Navigation() {
const preloadAbout = () => {
import('./About');
};
return (
<nav>
<Link to="/about" onMouseEnter={preloadAbout}>
О нас
</Link>
</nav>
);
}javascript
import { FixedSizeList as List } from 'react-window';
function VirtualizedList({ items }) {
return (
<List
height={400}
itemCount={items.length}
itemSize={50}
>
{({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
)}
</List>
);
}javascript
function ItemList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const loadMore = useCallback(() => {
fetchItems(page).then(newItems => {
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
});
}, [page]);
return (
<div>
{items.map(item => <Item key={item.id} item={item} />)}
<button onClick={loadMore}>Загрузить еще</button>
</div>
);
}javascript
// React DevTools Profiler
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<MyComponent />
</Profiler>
);
}
function onRenderCallback(
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime
) {
console.log('Время рендеринга:', actualDuration);
}javascript
function useWhyDidYouUpdate(name, props) {
const previousProps = useRef();
useEffect(() => {
if (previousProps.current) {
const allKeys = Object.keys({ ...previousProps.current, ...props });
const changes = {};
allKeys.forEach(key => {
if (previousProps.current[key] !== props[key]) {
changes[key] = {
from: previousProps.current[key],
to: props[key]
};
}
});
if (Object.keys(changes).length) {
console.log('[why-did-you-update]', name, changes);
}
}
previousProps.current = props;
});
}javascript
// package.json
{
"scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'",
"build": "react-scripts build"
}
}javascript
// Tree shaking работает лучше с ES6 импортами
import { Button } from 'ui-library'; // Хорошо
// import * as UI from 'ui-library'; // Плохо для tree shakingВывод: Оптимизация React-приложений требует комплексного подхода, включающего мемоизацию, правильную работу с состоянием, код-сплиттинг, виртуализацию и использование инструментов профилирования. Каждое приложение требует индивидуального подхода в зависимости от его специфики и bottlenecks.