Luyện Phỏng Vấn IT — 2000+ Câu Hỏi Phỏng Vấn IT Có Đáp Án 2026
Performance
Ba Core Web Vitals: LCP (<2.5s, loading), INP (<200ms, interactivity — thay FID từ 3/2024), CLS (<0.1, visual stability) — ảnh hưởng SEO ranking từ 2021.
- Ba Core Web Vitals là bộ metrics của Google đo trải nghiệm người dùng thực tế, ảnh hưởng đến SEO ranking từ 2021.
- Ba metrics: LCP (Largest Contentful Paint — loading performance, target <2.5s: good, >4s: poor; đo thời gian render phần tử lớn nhất trong viewport); INP (Interaction to Next Paint — thay thế FID từ tháng 3/2024, target <200ms: good, >500ms: poor; đo total delay từ user interaction đến next paint cho tất cả interactions trong session, không chỉ first); CLS (Cumulative Layout Shift — visual stability, target <0.1: good, >0.25: poor; đo mức độ layout shift unexpected).
- Đo lường: PageSpeed Insights (field data từ Chrome UX Report + lab data từ Lighthouse), Search Console Core Web Vitals report, web-vitals JavaScript library (import { getLCP, getINP, getCLS } from 'web-vitals').
- Quan trọng: field data (real users) khác lab data (controlled) — optimize cho field data.
- TTFB (Time to First Byte) không phải Core Web Vital nhưng ảnh hưởng LCP mạnh; FCP (First Contentful Paint) là diagnostic metric.
Cải thiện LCP: preload hero image, không lazy-load LCP element, serve WebP/AVIF, dùng fetchpriority="high", cải thiện TTFB với CDN — đây thường là nguyên nhân chính cho LCP >2.5s.
- LCP đo thời gian render phần tử lớn nhất trong viewport — thường là hero image, large text block, video poster.
- Candidates: img, image trong CSS background, video (poster), block-level text.
- Cải thiện TTFB trước (ảnh hưởng LCP nhất): server response time, CDN, edge caching.
- Image optimization: serve WebP/AVIF (25-50% nhỏ hơn JPEG), đúng kích thước (không serve 2000px cho 400px container), dùng responsive images với srcset.
- Preload LCP image: <link rel='preload' as='image' href='hero.webp'> trong <head> — critical vì browser discover image late trong parsing.
- Không lazy load LCP image: loading='lazy' trên hero image làm tăng LCP đáng kể — chỉ lazy load below-fold.
- Eliminate render-blocking resources: defer/async non-critical JS, inline critical CSS, preconnect đến third-party domains. fetchpriority='high' attribute trên LCP image hint browser prioritize fetch.
- Next.js next/image tự động: format conversion, responsive sizes, lazy load by default (priority prop để disable cho LCP image).
- Thường gặp: LCP image discovered late vì được set bằng CSS background-image — browser không discover cho đến khi parse CSS; dùng <img> thay vì CSS background cho LCP element.
Fix CLS: luôn set width/height trên images/videos, reserve space cho dynamic content, dùng font-display: swap với size-adjust, dùng transform thay vì top/margin cho animations.
- CLS đo mức độ layout shift unexpected trong quá trình loading — score là sum của (impact fraction * distance fraction) cho mỗi shift.
- Target <0.1; >0.25 là poor.
- Nguyên nhân phổ biến và fix: Images/videos không có dimensions — luôn set width/height attributes (aspect-ratio CSS tự tính) hoặc dùng aspect-ratio: 16/9 container; browser reserve space trước khi image load.
- Fonts gây FOUT (Flash of Unstyled Text) khi web font load — dùng font-display: swap với size-adjust để match fallback font metrics; font-display: optional skip web font nếu không cached (zero CLS nhưng có thể không dùng web font); preload critical fonts.
- Dynamically injected content (banners, cookie notices, ads) shift content — reserve space trước bằng min-height container; không inject content above existing content sau page load.
- Dynamic ads: Google Ads có thể gây CLS — fix bằng reserve fixed space cho ad slot.
- Infinite scroll: thêm items dưới thay vì trên viewport. transform: translateY() thay vì top/margin cho animations — transform không trigger layout.
- CLS sau interaction (300ms window) không tính vào score — UX vẫn tệ nhưng không penalty.
- Debugging: Chrome DevTools Performance panel, Layout Shift Regions (Rendering tab).
Lazy loading với loading="lazy" cho images, React.lazy() + Suspense cho components, next/dynamic cho Next.js — không lazy load LCP image và đừng lazy load quá nhiều (waterfall).
- Lazy loading trì hoãn loading resources cho đến khi thực sự cần — giảm initial load time và Time to Interactive.
- Images: <img loading='lazy'> attribute là native browser lazy loading, browser load image khi cần thiết khi scroll đến; không dùng cho LCP image (dùng loading='eager' hoặc priority).
- Components trong React: const HeavyChart = React.lazy(() => import('./HeavyChart')) kết hợp <Suspense fallback={<Spinner/>}> — chunk được split tự động và load on demand; dùng cho large components, chart libraries, editors. next/dynamic trong Next.js: const DynamicMap = dynamic(() => import('./Map'), { loading: () => <p>Loading...</p>, ssr: false }) — thêm ssr: false để skip server-side rendering cho browser-only components.
- Route-based lazy loading: React Router <Suspense> + React.lazy() mỗi route là chunk riêng — standard approach.
- Preloading: sau lazy load, có thể preload chunk trước khi user navigate: import('./HeavyPage') trong onMouseEnter — reduce perceived latency.
- Intersection Observer API: detect khi element vào viewport để trigger lazy load — basis của nhiều lazy loading libraries.
- Trade-off: lazy loading adds waterfall (request phải chờ parent component render) — cẩn thận với critical path; chỉ lazy load truly non-critical components.
Route-based code splitting là chiến lược đầu tiên — mỗi route là chunk riêng; dùng bundle analyzer để tìm large deps có alternatives nhỏ hơn; prefetch chunks cho next likely navigation.
- Code splitting chia bundle thành chunks nhỏ hơn — load initial chunk nhỏ hơn, load thêm chunks on demand.
- Dynamic import() là foundation: import('./module') trả về Promise, bundler tự tạo separate chunk.
- Chiến lược: Route-based splitting — mỗi route là chunk riêng, user chỉ download code cho route đang visit; pattern chuẩn với React Router và React.lazy().
- Component-based splitting — lazy load heavy components (rich text editor, PDF viewer, 3D rendering) chỉ khi cần.
- Vendor splitting: tách node_modules thành vendor chunk — ít thay đổi hơn app code, cache lâu hơn; Vite tự động split vendor; Webpack SplitChunksPlugin cấu hình.
- Shared chunks: code dùng ở nhiều routes tách thành shared chunk — tránh duplicate code trong nhiều chunks.
- Prefetching và preloading: <link rel='prefetch'> cho next likely navigation (browser idle), <link rel='preload'> cho current route critical resources; Webpack magic comments: import(/ webpackPrefetch: true / './NextPage').
- Analysis: Webpack Bundle Analyzer, vite-bundle-visualizer, source-map-explorer để xem chunk composition và identify optimization opportunities.
Pitfall: quá nhiều chunks nhỏ tệ hơn một chunk lớn do HTTP request overhead — HTTP/2 multiplexing giảm vấn đề này nhưng có limits.
HTTP caching giảm bandwidth và latency bằng cách lưu responses tại browser hoặc CDN.
- Cache-Control directives: max-age=3600 (cache 1 giờ trước khi stale); no-cache (phải revalidate với server trước khi dùng — không phải 'không cache'); no-store (không cache bao giờ, sensitive data); public (CDN có thể cache); private (chỉ browser cache, không CDN); immutable (content không bao giờ thay đổi, skip revalidation kể cả khi max-age expired — dùng với content-hashed assets).
- Static assets strategy: Cache-Control: public, max-age=31536000, immutable cho files có content hash trong tên (bundle.abc123.js) — cache 1 năm, không bao giờ revalidate.
- HTML: Cache-Control: no-cache — revalidate mỗi lần nhưng serve từ cache nếu ETag match.
- Conditional requests: ETag (hash của content) và Last-Modified (timestamp); client gửi If-None-Match/If-Modified-Since trong request; nếu không thay đổi, server trả 304 Not Modified (no body) — tiết kiệm bandwidth.
- Stale-While-Revalidate: Cache-Control: max-age=60, stale-while-revalidate=300 — serve stale content trong 5 phút trong khi revalidate background; zero latency impact.
- CDN caching: Cloudflare, Vercel Edge Network cache responses — đặt s-maxage khác max-age để CDN cache lâu hơn browser.
- Cache busting: thay đổi URL khi content thay đổi (content hash, query string ?v=2).
Image optimization là một trong các cách hiệu quả nhất để giảm page weight và cải thiện LCP.
- Modern formats: WebP nhỏ hơn JPEG 25-34%, AVIF nhỏ hơn 50% nhưng encode chậm hơn; dùng <picture> element với fallback cho browser không support.
- Responsive images: srcset='image-400.webp 400w, image-800.webp 800w' với sizes='(max-width: 600px) 400px, 800px' — browser chọn đúng size cho device; next/image tự động generate.
- Correct sizing: không serve 2000px image cho 400px container — lãng phí bandwidth 25x; dùng Image CDN (Cloudinary, Imgix) để resize on demand theo URL params.
- Compression: lossy (JPEG quality 80 thường indistinguishable từ 100), lossless (PNG, WebP lossless); tools: Squoosh, Sharp (Node.js), ImageMagick.
- Lazy loading: loading='lazy' cho below-fold images; không lazy load LCP image.
- Preload LCP: <link rel='preload' as='image' href='hero.webp' imagesrcset='...' imagesizes='...'> cho critical images.
- LQIP (Low Quality Image Placeholder): show tiny blurred placeholder trong khi load — next/image placeholder='blur' prop.
- CDN và edge: serve images từ locations gần users; modern Image CDNs auto-convert format, resize, compress based on device.
- Sprites: combine nhiều icons thành một file — giảm HTTP requests; ngày nay SVG sprites hoặc icon fonts phổ biến hơn.
Lighthouse là công cụ của Google đánh giá website theo 4 tiêu chí:
- Performance: lazy load images (
loading='lazy'), code splitting với React.lazy, compress assets (gzip/brotli), preload fonts quan trọng. - Accessibility: mọi image có alt text, color contrast đủ ratio, dùng ARIA cho interactive elements.
- Best Practices: dùng HTTPS, không console errors, không deprecated APIs.
- SEO: meta tags đầy đủ (title, description), semantic HTML (h1, nav, main), sitemap.xml.
Mục tiêu: Performance > 90, Accessibility > 90. Đo bằng Chrome DevTools tab Lighthouse hoặc PageSpeed Insights.
Web Vitals là ba chỉ số chính mà Google dùng để đánh giá trải nghiệm người dùng thực tế trên website. LCP (Largest Contentful Paint) đo thời gian hiển thị phần tử lớn nhất, cần dưới 2.5 giây — tối ưu bằng cách preload hero image, inline critical CSS, và giảm server response time.
CLS (Cumulative Layout Shift) đo mức độ nhảy layout, cần dưới 0.1 — khắc phục bằng cách luôn set width/height cho images, reserve space cho quảng cáo và dynamic content, dùng font-display: swap để tránh font gây layout shift.
INP (Interaction to Next Paint) đo độ phản hồi khi user tương tác, cần dưới 200ms — cải thiện bằng cách break long tasks thành chunks nhỏ hơn, defer non-critical JavaScript, và dùng Web Workers cho tính toán nặng. Đo các chỉ số này bằng Chrome DevTools tab Performance hoặc thư viện web-vitals trong code.
LCP (Largest Contentful Paint) đo thời gian phần tử lớn nhất hiển thị trên màn hình, phản ánh tốc độ tải trực quan. INP (Interaction to Next Paint) đo thời gian phản hồi của tất cả tương tác trong suốt vòng đời trang, không chỉ tương tác đầu tiên — thay thế FID từ tháng 3/2024 trong Core Web Vitals. CLS (Cumulative Layout Shift) đo mức độ dịch chuyển layout bất ngờ, ảnh hưởng đến trải nghiệm ổn định thị giác. TTFB (Time to First Byte) đo thời gian từ lúc gửi request đến khi nhận byte đầu tiên từ server.
Để tối ưu: lazy load hình ảnh, code splitting, minify tài nguyên, cache hiệu quả, dùng CDN, và tối ưu server response time.
Browser rendering pipeline gồm các bước:
- parse HTML thành DOM tree,
- parse CSS thành CSSOM tree,
- kết hợp DOM và CSSOM thành render tree,
- tính toán layout (vị trí, kích thước),
- paint (vẽ pixel),
- composite (ghép các layer)
Critical rendering path là chuỗi bước tối thiểu trình duyệt phải hoàn thành trước khi hiển thị trang — CSS và JavaScript mặc định sẽ block quá trình render.
Tối ưu bằng cách: inline critical CSS, defer hoặc async cho JavaScript, giảm kích thước CSS/JS, và dùng DevTools Coverage để tìm code không sử dụng.
Bundle analysis là bước đầu tiên khi optimize bundle size — không đoán mò, phải đo.
- Tools: webpack-bundle-analyzer (treemap visualization), vite-bundle-visualizer (rollup-plugin-visualizer), source-map-explorer (analyze actual bundle không phải estimates), bundlephobia.com (check package size trước khi install).
- Những gì cần tìm: Large dependencies có alternatives nhỏ hơn — moment.js (330KB) → date-fns/dayjs (10KB); lodash (71KB full) → lodash-es + tree shaking hoặc chỉ import specific: import debounce from 'lodash/debounce'; chart.js (200KB) → recharts (160KB) → victory (100KB).
- Duplicate modules: cùng dependency nhiều versions (webpack deduplication), cùng code ở nhiều chunks.
- Unexpected large modules: ví dụ icon library import toàn bộ thay vì specific icons — @mui/icons-material 30MB vs import MenuIcon from '@mui/icons-material/Menu' (một file).
- Giải pháp: Tree shaking (sideEffects: false trong package.json), code splitting + dynamic imports, externals (load React từ CDN thay vì bundle), Module Federation (share dependencies giữa micro-frontends).
- Compression context: gzip/Brotli reduce bundle size 60-80% — báo cáo compressed size thực tế, không raw.
- Benchmark: track bundle size trong CI với size-limit package — fail build nếu bundle vượt threshold.
Service Worker là JavaScript running trong background thread, intercept network requests giữa app và network — foundation của PWA.
- Lifecycle: Install (cache precache assets) → Activate (cleanup old caches) → Fetch (intercept requests).
- Caching strategies: Cache First (check cache trước, fallback network — offline-first, dùng cho static assets, icon fonts); Network First (fetch từ network, fallback cache nếu offline — dùng cho API calls cần fresh data); Stale-While-Revalidate (serve từ cache immediately, fetch từ network để update cache background — balance speed và freshness, tốt cho non-critical data); Cache Only (chỉ cache, không network — precached resources); Network Only (không cache — real-time data).
- Background Sync: queue failed requests khi offline, retry khi network available — ví dụ submit form offline.
- Push Notifications: receive push messages từ server kể cả khi app không open — cần permission từ user.
- Workbox từ Google: abstraction layer cho Service Worker strategies — workbox-strategies, workbox-routing, workbox-precaching; tránh viết Service Worker boilerplate thủ công. vite-plugin-pwa tích hợp Workbox với Vite, tự động generate service worker.
Pitfall: Service Worker cache bugs khó debug — phải explicitly update cache version khi deploy; chrome://serviceworker-internals để debug.
- HTTPS required (localhost exception).
Virtual scrolling chỉ render items visible trong viewport (~20-50 nodes) bất kể list có hàng nghìn items — dùng @tanstack/react-virtual; chỉ cần khi list >100-200 items phức tạp, profile trước.
- Virtual scrolling (windowing) chỉ render DOM nodes cho items visible trong viewport — list 10,000 items chỉ render 20-50 items trong DOM tại bất kỳ thời điểm nào, maintain scroll position qua spacer elements.
- Mechanism: total height của list được set bằng container (tạo scrollbar đúng size), khi scroll, calculate which items should be visible, render chỉ those items với absolute positioning.
- Libraries: @tanstack/react-virtual (headless, flexible, không opinionated về UI — phổ biến nhất hiện tại); react-window (Brian Vaughn, smaller, simpler API cho fixed-size items); react-virtuoso (dynamic item heights, auto-measure).
- Khi cần dùng: list >100-200 items với complex rendering (images, many DOM nodes per item); performance profiling (React DevTools Profiler hoặc Chrome Performance tab) cho thấy list rendering là bottleneck — đừng premature optimize.
Pitfall: virtual scrolling phức tạp hơn regular list — khó implement features như 'scroll to item', dynamic item heights, sticky headers; @tanstack/react-virtual có examples cho tất cả cases này.
- Variable height items: cần measure items sau render để tính scroll position — @tanstack/react-virtual và react-virtuoso handle dynamic heights tốt hơn react-window.
- Alternative đơn giản hơn khi list không quá dài: pagination hoặc 'load more' button — ít phức tạp, không cần virtual scrolling.