Π ΠΎΠ»Ρ: Π’Ρ β Senior Frontend Developer, ΡΠΏΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΡΡΠΈΠΉΡΡ Π½Π° ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΠΈΠΌΠΌΠ΅ΡΡΠΈΠ²Π½ΡΡ Π²Π΅Π±-ΠΏΡΠ΅Π·Π΅Π½ΡΠ°ΡΠΈΠΉ Ρ Π°ΠΊΡΠ΅Π½ΡΠΎΠΌ Π½Π° ΠΌΠΈΠΊΡΠΎ-Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ ΠΈ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ.
Π¦Π΅Π»Ρ: Π Π°Π·ΡΠ°Π±ΠΎΡΠ°ΡΡ ΠΎΠ΄Π½ΠΎΡΡΡΠ°Π½ΠΈΡΠ½ΡΠΉ (ΠΈΠ»ΠΈ ΠΌΠ½ΠΎΠ³ΠΎΡΡΡΠ°Π½ΠΈΡΠ½ΡΠΉ Ρ ΠΏΠ»Π°Π²Π½ΡΠΌ ΡΠΊΡΠΎΠ»Π»ΠΎΠΌ) ΠΏΡΠ΅Π·Π΅Π½ΡΠ°ΡΠΈΠΎΠ½Π½ΡΠΉ ΡΠ°ΠΉΡ Π΄Π»Ρ Π²ΡΠΌΡΡΠ»Π΅Π½Π½ΠΎΠ³ΠΎ Π±ΡΠ΅Π½Π΄Π° "NEXUS VISION". ΠΡΠ½ΠΎΠ²Π½Π°Ρ Π·Π°Π΄Π°ΡΠ° β Π²ΡΠ·Π²Π°ΡΡ Π²Π°Ρ-ΡΡΡΠ΅ΠΊΡ Π·Π° ΡΡΠ΅Ρ ΠΏΠ»Π°Π²Π½ΠΎΠΉ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ ΠΈ ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠ³ΠΎ 3D-ΠΊΠΎΠ½ΡΠ΅Π½ΡΠ°.
React Router v6 (Π΄Π»Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ ΠΏΠΎ ΡΠ΅ΠΊΡΠΈΡΠΌ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ HashRouter ΠΈΠ»ΠΈ BrowserRouter) ΠΈΠ»ΠΈ React Scroll Π΄Π»Ρ ΠΏΠ»Π°Π²Π½ΠΎΠΉ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ ΠΏΠΎ ΡΠΊΠΎΡΡΠΌanime.js (Π΄Π»Ρ SVG-Π°Π½ΠΈΠΌΠ°ΡΠΈΠΉ, ΡΠ΅ΠΊΡΡΠ°, ΠΌΠΎΡΡΠΈΠ½Π³Π° ΠΈ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ ΠΏΠΎ ΡΠΊΡΠΎΠ»Π»Ρ)Three.js + @react-three/fiber + @react-three/drei (Π΄Π»Ρ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ 3D-ΡΡΠ΅Π½)Tailwind CSS (Π΄Π»Ρ Π±ΡΡΡΡΠΎΠΉ Π²Π΅ΡΡΡΠΊΠΈ) ΠΈΠ»ΠΈ CSS ModulesΠ Π΅Π°Π»ΠΈΠ·ΡΠΉ ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΡ Ρ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΠΌΠΈ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄Π°ΠΌΠΈ:
Π€ΠΈΠΊΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅ Π³ΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΌΠ΅Π½Ρ Π² Π²Π΅ΡΡ Π½Π΅ΠΉ ΡΠ°ΡΡΠΈ ΡΠΊΡΠ°Π½Π° Ρ ΡΡΡΠ΅ΠΊΡΠΎΠΌ ΡΠ°Π·ΠΌΡΡΠΈΡ ΡΡΠ΅ΠΊΠ»Π° (Glassmorphism).
ΠΠ½Π΄ΠΈΠΊΠ°ΡΠΎΡ ΠΏΡΠΎΠ³ΡΠ΅ΡΡΠ° ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ ΡΡΡΠ°Π½ΠΈΡΡ, ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ Ρ anime.js.
ΠΡΠΈ ΠΊΠ»ΠΈΠΊΠ΅ Π½Π° ΠΏΡΠ½ΠΊΡ ΠΌΠ΅Π½Ρ ΠΏΡΠΎΠΈΡΡ
ΠΎΠ΄ΠΈΡ ΠΏΠ»Π°Π²Π½ΡΠΉ ΡΠΊΡΠΎΠ»Π» ΠΊ ΡΠ΅ΠΊΡΠΈΠΈ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ anime.js (easing: easeInOutQuad) Π²ΠΌΠ΅ΡΡΠΎ ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΠΎΠ³ΠΎ scrollIntoView.
ΠΡΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ React Router, ΡΠ΅Π°Π»ΠΈΠ·ΡΠΉ Π°Π½ΠΈΠΌΠ°ΡΠΈΡ ΠΏΠΎΡΠ²Π»Π΅Π½ΠΈΡ/ΠΈΡΡΠ΅Π·Π½ΠΎΠ²Π΅Π½ΠΈΡ ΡΡΡΠ°Π½ΠΈΡ (AnimatePresence-ΠΏΠΎΠ΄ΠΎΠ±Π½ΡΠΉ ΠΏΠΎΠ΄Ρ
ΠΎΠ΄, Π½ΠΎ Π½Π° anime.js).
ΠΠ½ΡΠ΅Π³ΡΠΈΡΡΠΉ 3D-ΡΡΠ΅Π½Ρ Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΡΠΎΠ½Π° ΠΈΠ»ΠΈ ΡΠ΅Π½ΡΡΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°:
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉ Canvas ΠΎΡ @react-three/fiber, ΡΠ°ΡΡΡΠ½ΡΡΡΠΉ Π½Π° Π²ΡΡ ΡΠΈΡΠΈΠ½Ρ ΡΠΊΡΠ°Π½Π° (Ρ pointer-events: none Π΄Π»Ρ ΡΠΎΠ½Π° ΠΈΠ»ΠΈ auto Π΄Π»Ρ ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π°).
ΠΠ°ΡΠ°Π»Π»Π°ΠΊΡ-ΡΡΡΠ΅ΠΊΡ β ΠΏΡΠΈ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠΈ ΠΌΡΡΠΈ 3D-ΠΎΠ±ΡΠ΅ΠΊΡΡ ΡΠ»Π΅Π³ΠΊΠ° ΡΠΌΠ΅ΡΠ°ΡΡΡΡ ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΊΡΡΡΠΎΡΠ° (ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ useFrame Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΏΠΎΠ²ΠΎΡΠΎΡΠ°).
Π Π°Π·Π΄Π΅Π»ΠΈ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ Π½Π° Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠΎΠ²Π½Π΅ΠΉ:
anime.js Ρ strokeDashoffset)anime.js Π² ΡΠ²ΡΠ·ΠΊΠ΅ Ρ Intersection Observerscale: [0.8, 1]) Ρ ΡΡΡΠ΅ΠΊΡΠΎΠΌ ΠΏΡΡΠΆΠΈΠ½Ρ (elasticity: 400)Π£ ΠΌΠ΅Π½Ρ Π΅ΡΡΡ PNG-ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ° Ρ ΠΏΡΠΎΠ·ΡΠ°ΡΠ½ΡΠΌ ΡΠΎΠ½ΠΎΠΌ. Π’Π²ΠΎΡ Π·Π°Π΄Π°ΡΠ°:
Π Π°Π·ΠΌΠ΅ΡΡΠΈ ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ° Π² ΠΏΡΠ°Π²ΠΎΠΉ Π½ΠΈΠΆΠ½Π΅ΠΉ ΡΠ°ΡΡΠΈ ΡΠΊΡΠ°Π½Π° ΠΏΠΎΠ²Π΅ΡΡ 3D-ΡΠΎΠ½Π°, Π½ΠΎ ΠΏΠΎΠ·Π°Π΄ΠΈ ΡΠ΅ΠΊΡΡΠΎΠ²ΡΡ ΡΠ»ΠΎΠ΅Π² (z-index: 10).
Π‘Π΄Π΅Π»Π°ΠΉ ΡΠ°ΠΊ, ΡΡΠΎΠ±Ρ ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆ ΠΏΠ»Π°Π²Π½ΠΎ ΠΏΠΎΠΊΠ°ΡΠΈΠ²Π°Π»ΡΡ (Π»Π΅Π³ΠΊΠΈΠΉ Π½Π°ΠΊΠ»ΠΎΠ½ Π²Π»Π΅Π²ΠΎ-Π²ΠΏΡΠ°Π²ΠΎ) ΠΈ ΡΠ»Π΅Π³ΠΊΠ° ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π»ΡΡ ΠΏΡΠΈ "Π΄ΡΡ
Π°Π½ΠΈΠΈ" (ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉ anime.js ΡΠΈΠΊΠ»Ρ loop: true).
ΠΡΠΈ Π½Π°Π²Π΅Π΄Π΅Π½ΠΈΠΈ Π½Π° ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ°, ΠΎΠ½ Π΄ΠΎΠ»ΠΆΠ΅Π½ "ΡΠΌΠΎΡΡΠ΅ΡΡ" Π½Π° ΠΊΡΡΡΠΎΡ (ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°ΡΡΡΡ) ΠΈΠ»ΠΈ ΠΌΠ΅Π½ΡΡΡ ΡΠΌΠΎΡΠΈΡ (Π·Π°ΠΌΠ΅Π½ΠΈΡΡ PNG Π½Π° Π΄ΡΡΠ³ΠΎΠΉ Π²Π°ΡΠΈΠ°Π½Ρ, Π΅ΡΠ»ΠΈ ΠΈΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ).
ΠΡΠΈ ΡΠΊΡΠΎΠ»Π»Π΅, ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ°ΡΡΡΡ ΠΏΠΎ ΠΎΡΠΈ Y ΠΌΠ΅Π΄Π»Π΅Π½Π½Π΅Π΅, ΡΠ΅ΠΌ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΊΠΎΠ½ΡΠ΅Π½Ρ (ΡΡΡΠ΅ΠΊΡ Π³Π»ΡΠ±ΠΈΠ½Ρ).
Π‘Π²Π΅ΡΡΡΠ°ΠΉ 5 ΠΎΡΠ½ΠΎΠ²Π½ΡΡ Π±Π»ΠΎΠΊΠΎΠ²:
React.memo Π΄Π»Ρ ΡΡΠΆΠ΅Π»ΡΡ
ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² ΠΈ lazy + Suspense Π΄Π»Ρ Π·Π°Π³ΡΡΠ·ΠΊΠΈ 3D-ΠΌΠΎΠ΄ΡΠ»Π΅ΠΉuseHeroAnimation, useParallax)// ΠΡΠΈΠΌΠ΅ΡΠ½ΡΠΉ ΠΊΠΎΠ΄ Π΄Π»Ρ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ "Π΄ΡΡ
Π°Π½ΠΈΡ" ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ°
useEffect(() => {
const animation = anime({
targets: '.character-img',
translateY: ['-5px', '5px'],
scale: ['1', '1.02'],
direction: 'alternate',
loop: true,
easing: 'easeInOutSine',
duration: 1500
});
return () => animation.pause();
}, []);
ΠΡΠΈΠΌΠ΅Ρ 3D ΡΡΠ΅Π½Ρ Ρ ΡΠ°ΡΡΠΈΡΠ°ΠΌΠΈ
javascript
import { Canvas } from '@react-three/fiber';
import { OrbitControls, Stars } from '@react-three/drei';
function Scene3D() {
return (
<Canvas camera={{ position: [0, 0, 5] }}>
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} />
<Stars radius={10} depth={50} count={2000} factor={4} />
<mesh rotation={[0.5, 0.5, 0]}>
<torusGeometry args={[1.5, 0.3, 16, 100]} />
<meshStandardMaterial color="#6c63ff" wireframe />
</mesh>
<OrbitControls enableZoom={false} />
</Canvas>
);
}
ΠΡΠΈΠΌΠ΅Ρ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ Ρ React Router ΠΈ anime.js
javascript
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import { useEffect, useRef } from 'react';
import anime from 'animejs';
function Navigation() {
const menuRef = useRef(null);
useEffect(() => {
anime({
targets: menuRef.current,
translateY: [-50, 0],
opacity: [0, 1],
easing: 'easeOutQuad',
duration: 800
});
}, []);
return (
<nav ref={menuRef} className="fixed top-0 w-full z-50 bg-white/80 backdrop-blur-md">
<ul className="flex justify-center space-x-8 p-4">
<li><Link to="/" className="hover:text-indigo-600 transition">ΠΠ»Π°Π²Π½Π°Ρ</Link></li>
<li><Link to="/about" className="hover:text-indigo-600 transition">Π Π½Π°Ρ</Link></li>
<li><Link to="/portfolio" className="hover:text-indigo-600 transition">ΠΠΎΡΡΡΠΎΠ»ΠΈΠΎ</Link></li>
<li><Link to="/contact" className="hover:text-indigo-600 transition">ΠΠΎΠ½ΡΠ°ΠΊΡΡ</Link></li>
</ul>
</nav>
);
}
ΠΡΠΈΠΌΠ΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Intersection Observer Ρ anime.js
javascript
import { useEffect, useRef } from 'react';
import anime from 'animejs';
function AnimatedSection({ children }) {
const sectionRef = useRef(null);
const animated = useRef(false);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !animated.current) {
animated.current = true;
anime({
targets: sectionRef.current,
translateY: [50, 0],
opacity: [0, 1],
easing: 'easeOutElastic',
duration: 1200,
elasticity: 400
});
}
});
}, { threshold: 0.3 });
if (sectionRef.current) {
observer.observe(sectionRef.current);
}
return () => observer.disconnect();
}, []);
return <div ref={sectionRef} className="opacity-0">{children}</div>;
}
ΠΠΎΠ»Π½Π°Ρ ΡΡΡΡΠΊΡΡΡΠ° ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ° Hero Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠ΅ΠΉ
javascript
import React, { useEffect, useRef } from 'react';
import anime from 'animejs';
import { Scene3D } from './Scene3D';
import characterPNG from './assets/character.png';
function Hero() {
const titleRef = useRef(null);
const subtitleRef = useRef(null);
const ctaRef = useRef(null);
const characterRef = useRef(null);
useEffect(() => {
// ΠΠ½ΠΈΠΌΠ°ΡΠΈΡ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°
anime({
targets: titleRef.current,
translateY: [100, 0],
opacity: [0, 1],
easing: 'easeOutQuad',
duration: 1000,
delay: 200
});
// ΠΠ½ΠΈΠΌΠ°ΡΠΈΡ ΠΏΠΎΠ΄Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ°
anime({
targets: subtitleRef.current,
translateY: [80, 0],
opacity: [0, 1],
easing: 'easeOutQuad',
duration: 1000,
delay: 400
});
// ΠΠ½ΠΈΠΌΠ°ΡΠΈΡ ΠΊΠ½ΠΎΠΏΠΊΠΈ
anime({
targets: ctaRef.current,
scale: [0.8, 1],
opacity: [0, 1],
easing: 'easeOutElastic',
duration: 1200,
delay: 600,
elasticity: 500
});
// ΠΠ½ΠΈΠΌΠ°ΡΠΈΡ "Π΄ΡΡ
Π°Π½ΠΈΡ" ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ°
anime({
targets: characterRef.current,
translateY: ['-8px', '8px'],
scale: ['1', '1.03'],
direction: 'alternate',
loop: true,
easing: 'easeInOutSine',
duration: 2000
});
// ΠΠ°ΡΠ°Π»Π»Π°ΠΊΡ ΠΏΡΠΈ ΡΠΊΡΠΎΠ»Π»Π΅
const handleScroll = () => {
const scrollY = window.scrollY;
if (characterRef.current) {
characterRef.current.style.transform = `translateY(${scrollY * 0.1}px)`;
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<section className="relative h-screen flex items-center overflow-hidden">
{/* 3D Π€ΠΎΠ½ */}
<div className="absolute inset-0 z-0">
<Scene3D />
</div>
{/* ΠΠΎΠ½ΡΠ΅Π½Ρ */}
<div className="relative z-10 container mx-auto px-6">
<h1 ref={titleRef} className="text-6xl md:text-8xl font-bold text-white opacity-0">
NEXUS VISION
</h1>
<p ref={subtitleRef} className="text-xl md:text-2xl text-gray-200 mt-4 opacity-0">
ΠΡΠ΄ΡΡΠ΅Π΅ ΡΠΆΠ΅ Π·Π΄Π΅ΡΡ
</p>
<button ref={ctaRef} className="mt-8 px-8 py-4 bg-indigo-600 text-white rounded-full hover:bg-indigo-700 transition transform hover:scale-105 opacity-0">
ΠΠ°ΡΠ°ΡΡ
</button>
</div>
{/* PNG ΠΠ΅ΡΡΠΎΠ½Π°ΠΆ */}
<img
ref={characterRef}
src={characterPNG}
alt="ΠΠ΅ΡΡΠΎΠ½Π°ΠΆ"
className="absolute bottom-10 right-10 z-10 w-48 md:w-64 h-auto pointer-events-none select-none"
style={{ filter: 'drop-shadow(0 10px 30px rgba(0,0,0,0.5))' }}
/>
</section>
);
}
export default Hero;
Π‘ΡΡΡΠΊΡΡΡΠ° ΠΏΡΠΎΠ΅ΠΊΡΠ°
text
src/
βββ components/
β βββ Navigation.jsx
β βββ Hero.jsx
β βββ About.jsx
β βββ Portfolio.jsx
β βββ Team.jsx
β βββ Contact.jsx
β βββ Footer.jsx
βββ components/3D/
β βββ Scene3D.jsx
β βββ Particles.jsx
β βββ AnimatedObject.jsx
βββ hooks/
β βββ useScrollAnimation.js
β βββ useParallax.js
β βββ useCharacterAnimation.js
βββ assets/
β βββ character.png
βββ styles/
β βββ index.css
βββ App.jsx
βββ index.js
9. ΠΡΠΎΠ³ΠΎΠ²ΡΠΉ Π·Π°ΠΏΡΠΎΡ ΠΊ ΠΌΠΎΠ΄Π΅Π»ΠΈ
Π‘Π³Π΅Π½Π΅ΡΠΈΡΡΠΉ ΠΊΠΎΠ΄ Π΄Π»Ρ ΡΡΠΎΠ³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ°, Π½Π°ΡΠΈΠ½Π°Ρ Ρ ΡΠ°ΠΉΠ»Π° App.js, Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΡΠΎΡΡΠΈΠ½Π³Π°, ΠΈ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ° Hero Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠ΅ΠΉ 3D-ΡΠΎΠ½Π° ΠΈ PNG-ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ°, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ Π²ΡΡΠ΅ΠΎΠΏΠΈΡΠ°Π½Π½ΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»Π° Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ. Π£Π±Π΅Π΄ΠΈΡΡ, ΡΡΠΎ:
ΠΡΠ΅ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΠΈΠΌΠΏΠΎΡΡΠΈΡΠΎΠ²Π°Π½Ρ
ΠΠ½ΠΈΠΌΠ°ΡΠΈΠΈ ΡΠ°Π±ΠΎΡΠ°ΡΡ ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΠΎ ΠΈ Π½Π΅ ΠΊΠΎΠ½ΡΠ»ΠΈΠΊΡΡΡΡ
3D-ΡΡΠ΅Π½Π° ΠΎΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π° Π΄Π»Ρ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
ΠΠ΅ΡΡΠΎΠ½Π°ΠΆ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΡΠ΅Π°Π³ΠΈΡΡΠ΅Ρ Π½Π° ΡΠΊΡΠΎΠ»Π» ΠΈ ΠΈΠΌΠ΅Π΅Ρ "Π΄ΡΡ
Π°Π½ΠΈΠ΅"
ΠΠ°Π²ΠΈΠ³Π°ΡΠΈΡ ΠΏΠ»Π°Π²Π½ΠΎ ΡΠΊΡΠΎΠ»Π»ΠΈΡ ΠΊ ΡΠ΅ΠΊΡΠΈΡΠΌ
ΠΠΎΠ΄ ΡΠΈΡΡΡΠΉ, Ρ
ΠΎΡΠΎΡΠΎ ΡΡΡΡΠΊΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΠΈ Π·Π°Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ
10. ΠΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°ΡΠΈΠΈ
ΠΠ»Ρ ΡΠ»ΡΡΡΠ΅Π½ΠΈΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ:
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉ useMemo Π΄Π»Ρ ΡΠ»ΠΎΠΆΠ½ΡΡ
Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΠΉ Π² 3D-ΡΡΠ΅Π½Π΅
ΠΠΊΠ»ΡΡΠΈ shallow ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³ Π΄Π»Ρ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ ΡΠ°ΡΡΠΎ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡΡΡΡ
ΠΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΡΠΉ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ next/image ΠΈΠ»ΠΈ vite-plugin-imagemin
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉ React.lazy Π΄Π»Ρ ΠΊΠΎΠ΄-ΡΠΏΠ»ΠΈΡΡΠΈΠ½Π³Π° ΡΡΡΠ°Π½ΠΈΡ
ΠΠ»Ρ ΡΠ»ΡΡΡΠ΅Π½ΠΈΡ UX:
ΠΠΎΠ±Π°Π²Ρ ΠΈΠ½Π΄ΠΈΠΊΠ°ΡΠΎΡ Π·Π°Π³ΡΡΠ·ΠΊΠΈ Π΄Π»Ρ 3D-ΡΡΠ΅Π½Ρ
Π Π΅Π°Π»ΠΈΠ·ΡΠΉ ΠΏΠ»Π°Π²Π½ΡΠ΅ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄Ρ ΠΌΠ΅ΠΆΠ΄Ρ ΡΡΡΠ°Π½ΠΈΡΠ°ΠΌΠΈ
ΠΠΎΠ±Π°Π²Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΡ prefers-reduced-motion Π΄Π»Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅ΠΉ Ρ ΡΡΠ²ΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡΡ ΠΊ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ
ΠΠΊΠ»ΡΡΠΈ ΠΊΠ΅ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΡ
ΡΠ΅ΡΡΡΡΠΎΠ²
ΠΠ»Ρ SEO:
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉ react-helmet Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΌΠ΅ΡΠ°-ΡΠ΅Π³Π°ΠΌΠΈ
ΠΠΎΠ±Π°Π²Ρ ΡΡΡΡΠΊΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ (JSON-LD)
Π£Π±Π΅Π΄ΠΈΡΡ, ΡΡΠΎ ΠΊΠΎΠ½ΡΠ΅Π½Ρ Π΄ΠΎΡΡΡΠΏΠ΅Π½ Π±Π΅Π· JavaScript