As the internet industry enters deep stock competition, the 2025 front-end hiring market is undergoing a profound paradigm shift: the era when mechanically reciting "eight-legged essays" could easily pass interviews is over. The new generation of interview standards has moved from single API memorization to deep probing of engineering thinking, architecture design ability, and underlying principles. Mastering the core high-frequency 2025 front-end interview questions is not just for passing assessments but for reshaping developers' competitiveness in the AI-assisted programming era. Senior front-end interviews now focus on how candidates use Vue3 or React core mechanisms to solve performance bottlenecks in complex scenarios and whether they can make technical decisions that balance maintainability and extensibility in system design problems. With front-end AI engineering interviews becoming standard at major companies, pure coding ability is no longer the sole metric; code review, logical abstraction, and tool-based productivity are critical. This article will, through a layered progressive strategy, deeply analyze the complete knowledge map from JavaScript runtime mechanisms to large-scale system architecture, offering practical exercises with solutions from major companies and helping developers build a systematic knowledge system to evolve from "code executors" to "senior engineers."
2025 Frontend Interview Trend Analysis: From “Reciting Answers” to “Problem Solving”
As the internet industry enters an era of stock competition, the recruitment logic for frontend positions has fundamentally changed. The days when “reciting standard answers” could get you through are over. Interviews in 2025 are more like an in-depth assessment of engineering capabilities and architectural thinking. Interviewers are no longer satisfied with testing API memorization, but instead focus on candidates’ ability to solve complex scenario problems.
As mentioned in Heading to 2025, Frontend Interview Summary, companies’ requirements for frontend developers are continuously rising, and simply piling up technical points can no longer demonstrate competitiveness. The core focus has shifted from “how to write code” to “how to design systems” and “how to leverage tools for efficiency.”
2020 vs. 2025: Evolution of Core Interview Focus
To visually demonstrate this difference, we’ve compiled the following comparison table. This is also the watershed between Senior engineers and junior developers:
Assessment Dimension | 2020 Interview Focus (Recitation & Basics) | 2025 Interview Focus (Engineering & Architecture) |
|---|---|---|
CSS/UI | Vertical centering, Flexbox property memorization, BFC principle | Tailwind/CSS-in-JS selection trade-offs, Design Token architecture, responsive performance optimization |
Framework Principles | Lifecycle hooks, two-way binding principles | Compiler optimization (Vue Vapor/React Compiler), React Server Components (RSC), fine-grained reactivity design |
JavaScript | Prototype chain inheritance, closure definition, array deduplication | Asynchronous concurrency control, memory leak troubleshooting, V8 garbage collection mechanism, advanced TypeScript type gymnastics |
Engineering | Webpack Loader/Plugin configuration dictation | Vite/Rspack build optimization, Monorepo strategies, tree shaking and bundle analysis |
Performance Optimization | Image lazy loading, debounce/throttle | Core Web Vitals (LCP/CLS/INP) practice, SSR/ISR architecture decisions, frontend monitoring system design |
AI & Tools | None (pure manual coding) | AI-assisted coding (Copilot/Cursor) implementation, using LLMs to optimize workflows, prompt engineering |
Trend 1: Scenario-Based and System Design Become “Killer Skills”
In advanced interviews, simple “fill-in-the-blank” questions are disappearing, replaced by open-ended scenario questions. Interviewers are more likely to ask “How would you design a frontend monitoring SDK?” or “How would you implement large file breakpoint resume?” rather than “What are the HTTP status codes?”
These questions have no standard answers and assess the candidate’s ability to break down requirements and weigh technical solutions (trade-offs). For example, in 200 Popular Frontend Interview Questions, system design questions such as “Design a flash sale frontend” or “Implement infinite scroll with automatic recycling” have been marked as “difficult” and high-frequency topics. This requires job seekers to not only understand code, but also business and architecture.
Trend 2: AI Tools Reshape Code Assessment Standards
The popularity of AI programming tools (such as GitHub Copilot, ChatGPT) has shifted the focus of code assessment. Interviewers no longer insist on whether you can handwrite a perfect QuickSort, since AI can do it in seconds. The focus now is on:
- Code Review Ability: Can you identify logical flaws or security risks in AI-generated code?
- Architectural Design Ability: AI can write functions, but it can’t decide for you whether to use Micro-frontend or Monolith architecture.
- Complex Logic Abstraction: For asynchronous scheduling or state management involving complex business logic, solid programming skills are still required.
This guide will focus on the two core areas of “Senior Depth” and “Engineering Capabilities,” discarding outdated junior-level topics and helping developers build the knowledge system needed to tackle high-standard interviews in 2025. We’ll start from underlying language mechanisms, delve into framework principles, and finally land on engineering and system design, helping you transform from a “question solver” to a “senior engineer.”
Advanced Core of JavaScript and TypeScript

In senior front-end interviews in 2025, interviewers no longer filter candidates through basic syntax questions (such as var vs let or basic closure definitions). The focus has fully shifted to a deep understanding of JavaScript runtime mechanisms and the practical application of the TypeScript type system in large-scale projects. This level of questioning aims to verify whether candidates possess the ability to troubleshoot complex memory leaks, optimize execution order, and build type-safe codebases.
Deep Dive into Runtime: Event Loop and Macro/Microtasks
Understanding JavaScript's single-threaded non-blocking mechanism is the cornerstone of advanced development. Interviews often use complex code execution order prediction questions to assess your grasp of macro tasks and micro tasks execution timing.
The core principle is: After synchronous code finishes executing, the browser clears all microtask queues, attempts DOM rendering, and finally executes the next macro task.
Here is a typical 2025 interview example:
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => console.log('3'));
}, 0);
Promise.resolve().then(() => {
console.log('4');
setTimeout(() => console.log('5'), 0);
});
console.log('6');Analysis Logic:
- Synchronous Code: First outputs
1and6. - Microtask Queue: After synchronous execution, immediately checks microtasks. The
Promise.resolve().thencallback executes, outputting4. At this point, the internalsetTimeout(..., '5')is registered as a macro task. - Macro Task Queue: After microtasks are cleared, executes the earliest registered macro task (outer
setTimeout). Outputs2. - Nested Microtasks: During macro task execution, a new microtask (
then(..., '3')) is generated, which will execute immediately before the current macro task ends (or before the next macro task), outputting3. - Remaining Macro Tasks: Finally, executes the macro task registered previously in the microtask, outputting
5.
Such questions require candidates not only to provide the answer but also to clearly articulate the interaction flow between the V8 engine's call stack and task queues.
Memory Management and V8 Garbage Collection (GC)
As single-page applications (SPA) become increasingly complex, memory leaks have become the main cause of system-level performance bottlenecks. Senior interviews focus on V8's garbage collection strategies and memory optimization in real-world scenarios.
- Generational Collection Mechanism: V8 divides memory into "new space" (young generation) and "old space" (old generation). Young generation objects have short lifespans and are quickly cleaned using the Scavenge algorithm; old generation objects reside in memory longer and are cleaned using mark-sweep and mark-compact algorithms.
- Engineering Value of WeakMap: When associating metadata with DOM nodes (such as Vue3's reactivity system or custom directives), directly using objects or Maps can cause DOM nodes to remain uncollectable after removal. Using
WeakMapestablishes a weak reference, ensuring that when DOM nodes are destroyed, the associated data is also automatically cleared by the garbage collection mechanism, effectively preventing memory leaks.
TypeScript Type Manipulation and Engineering Practice
In large projects, TypeScript is not just for type annotations, but also for type programming. Interviewers tend to assess the combined use of the infer keyword, conditional types, and utility types to verify whether you can write highly reusable, library-level code.
For example, requiring you to handwrite a Pick or use infer to extract the return type of a function:
// Scenario: Extract the return type inside a Promise
// T extends Promise<infer U> ? U : T
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type Result = UnpackPromise<Promise<string>>; // string
// Scenario: Reuse an existing interface but only select certain fields (foundation for utility types)
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};Mastering these advanced features is crucial for maintaining public component libraries or handling dynamic data structures returned from the backend. They can significantly enhance code robustness and maintainability, and are the dividing line between junior and senior engineers.
Note: While runtime mechanisms are fundamental, in actual business scenarios, the most complex runtime issues often arise in asynchronous process control. In the next section, we will delve into practical patterns for Promise and concurrency control.
Asynchronous Programming: Promise, Async/Await, and Concurrency Control

In interviews in 2025, interviewers are no longer satisfied with hearing the definitions of "macro tasks and micro tasks." The focus now is on asynchronous governance capabilities in engineering scenarios: How to handle high-concurrency requests? How to gracefully handle partial failures? How to avoid performance losses caused by Async/Await?
Here are three of the most distinguishing practical examination points.
1. Rejecting the "Serial Trap": The Correct Use of Async/Await
Although async/await makes asynchronous code look like synchronous code, this is often the root of performance issues. Many developers habitually use await in loops, causing IO operations that could be parallelized to become serial, greatly increasing interface response time.
Anti-pattern (Promise Hell):
// ❌ Incorrect example: Requests are forced to be serial, time taken is the sum of all requests
async function loadPageData() {
const user = await fetchUser();
const posts = await fetchPosts(); // Wait for user to return before sending posts request
const notifications = await fetchNotifications(); // Wait for posts to return before sending notifications
return { user, posts, notifications };
}Best Practice:
For asynchronous tasks without dependencies, Promise.all must be used for concurrent processing.
// ✅ Correct example: Requests are sent concurrently, time taken depends on the slowest request
async function loadPageData() {
const [user, posts, notifications] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchNotifications()
]);
return { user, posts, notifications };
}2. Resilient Design: Promise.all vs Promise.allSettled
When building complex dashboards or micro-frontend applications, an interface error in one module should not cause the entire page to crash.
- Promise.all: Follows the "Fail-fast" principle. Once any Promise in the array is rejected, the entire
Promise.allimmediately rejects. This is suitable for strong dependency scenarios (e.g., both Token and UserID must be obtained simultaneously to proceed). - Promise.allSettled: Suitable for partial rendering scenarios. Regardless of success or failure, it waits for all tasks to finish and returns the status of each task (
fulfilledorrejected).
Scenario Example: A page needs to display 3 independent charts. If Promise.all is used and one chart's interface times out, it will cause the entire page to throw an error. Using Promise.allSettled allows successful requests to be filtered for rendering, while failed requests can separately display a "Retry" button.
3. High-Frequency Handwriting: Concurrency Request Controller (Scheduler)
This is a frequently encountered coding question in interviews at major companies. Interviewers usually set a scenario, such as "Batch upload 100 images, but limit the number of concurrent upload requests to a maximum of 3," and require the implementation of a scheduler.
This question tests the comprehensive application of Promise chaining, queues, and recursion/iteration.
Reference Implementation:
class Scheduler {
constructor(limit) {
this.limit = limit; // Maximum concurrency
this.queue = []; // Task queue
this.runningCount = 0; // Current number of running tasks
}
/*
Add task
@param {number} time Simulated task duration
@param {string} order Task name
*/
add(time, order) {
const taskCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve();
}, time);
});
};
this.queue.push(taskCreator);
this.run();
}
// Scheduling logic
run() {
// Stop scheduling if the current queue is empty or the concurrency limit is reached
if (this.queue.length === 0 || this.runningCount >= this.limit) {
return;
}
// Take out the first task
const task = this.queue.shift();
this.runningCount++;
// Execute the task
task().then(() => {
this.runningCount--;
// Core: After the current task is completed, recursively trigger the next scheduling
this.run();
});
// Try to start the next task (ensure concurrency slots are filled)
this.run();
}
}
// Test case
const scheduler = new Scheduler(2); // Limit concurrency to 2
scheduler.add(1000, '1');
scheduler.add(500, '2');
scheduler.add(300, '3');
scheduler.add(400, '4');
// Expected output order: 2 -> 3 -> 1 -> 4
// (Explanation: 1,2 enter. After 500ms, 2 completes, 3 enters. After 300ms, 3 completes, 4 enters...)Key Points to Solve the Problem:
- Task Storage: Do not execute the Promise immediately, but store a "function to generate the Promise" (Factory Pattern), otherwise the Promise will start executing upon creation, making it uncontrollable.
- Recursive Calls: Call
run()ornext()in thethencallback to ensure that a task is immediately filled after it ends. - Boundary Defense: Always check
runningCount < limit.
Such questions have many variants discussed in communities like Juejin, and it is recommended to master both Class and Function based implementations to meet different interview requirements.
Underlying Principles of Mainstream Frameworks: Vue3 and React Ecosystem

In the advanced front-end interviews of 2025, interviewers are no longer satisfied with shallow questions like "What are the syntax differences between Vue and React?" The focus of assessment has shifted to a deep comparison of architecture design philosophy, compile-time optimization, and runtime performance bottlenecks. Candidates need to demonstrate a profound understanding of the internal mechanisms of the frameworks and be able to explain why certain performance issues in React require manual optimization (like useMemo), while in Vue they are handled automatically.
Core Architecture Comparison: Immutability vs. Fine-grained Reactivity
React and Vue adopt entirely different mental models when handling state updates. React tends towards immutable data and runtime scheduling, while Vue delves into mutable data and compile-time optimization.
Here are the core differences between the two from a 2025 technical perspective:
Feature Dimension | React (Fiber & Concurrent) | Vue 3 (Proxy & Compiler) |
|---|---|---|
State Update Mechanism | Pull-based: State changes trigger a re-render of the component tree, relying on the Fiber reconciler to calculate the Diff. | Push-based: Fine-grained dependency tracking based on Proxy, state changes directly notify the corresponding effect functions. |
Data Flow | Unidirectional data flow, emphasizing immutability. Each update generates a new state snapshot. | Unidirectional data flow, but supports direct modification of reactive data, with automatic interception of Set operations at the lower level. |
Performance Bottlenecks | CPU Intensive: Large component trees have significant Diff computation, requiring time slicing to avoid blocking the main thread. | Memory Intensive: Each reactive object needs to maintain dependency collection, leading to high memory overhead in scenarios with massive data. |
Optimization Methods | Relies on developers to manage manually ( | The framework handles it automatically, even completely discarding the virtual DOM through Vapor Mode. |
In-depth Analysis of Reactivity Principles
Vue 3's Proxy Mechanism
Vue 3 completely abandons Vue 2's Object.defineProperty, opting for Proxy to proxy the entire object. This not only resolves the issue of not being able to listen to array index modifications but also implements lazy proxying (only proxies when accessing deep properties), significantly improving initialization performance. In interviews, you should be able to draw a flowchart of "Dependency Collection (Track)" and "Dispatch Update (Trigger)": when the component render function (Effect) reads reactive data, Vue records that dependency; when data changes, Vue directly finds the corresponding Effect to execute, without needing to traverse the component tree like React.
React's Fiber Architecture
The core of React lies in the Fiber reconciler. Since React cannot precisely know which variable has changed like Vue, it assumes that the entire subtree may need updating. To prevent such large-scale computations from blocking browser rendering (causing frame drops), React introduces the Fiber data structure, breaking rendering tasks into small units (Unit of Work). This allows React to perform low-priority updates during idle browser time while prioritizing responses during user interactions (like input).
Lifecycle from the "Compiler" Perspective
Another important topic in 2025 is compile-time optimization. Interviewers may ask: "How does Vue's template transform into DOM, and why is it said to be easier to optimize than JSX?"
- Vue's Static Hoisting and Patch Flags:
Vue's compiler performs static analysis on templates during the build phase. It can identify which nodes will never change (static nodes) and hoist them outside the render function to avoid recreating them on every render. For dynamic nodes, the compiler marks them withPatch Flags(for example: this node only changes Text, or only changes Class). During the runtime Diff phase, Vue only needs to check nodes with Flags, achieving targeted updates. - React's Compilation and React Compiler:
Traditional JSX is very flexible, but it also means the compiler struggles to know which parts are static through static analysis. Therefore, React has long relied on runtime Diff. However, with the popularity of the React Compiler (formerly React Forget), React has begun to attempt to automatically add memoization code during the compile phase for components, trying to bridge the performance gap with Vue in fine-grained updates.
Understanding these underlying differences is a key step in advancing from "API caller" to "architect," and lays the theoretical foundation for further discussions on best practices for Hooks and the Composition API.
Best Practices and Pitfalls of Hooks and the Composition API
In the advanced frontend interviews of 2025, interviewers no longer settle for asking about the basic usage of useEffect or Vue3's lifecycle mapping. They focus more on how you manage the complexity of Hooks/Composition API in real business scenarios, particularly how to identify “anti-patterns” and how to design maintainable logic reuse layers. Here are three high-frequency traps and their corrective strategies.
1. The “Stale Closures” in React
This is the classic “make-or-break” question in React Hooks interviews. When the dependency array of useEffect or useCallback is misconfigured, or an old State is referenced in asynchronous operations, a stale-closure trap occurs.
Anti-pattern example:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
// Here count is always the initial 0 from the first render, causing the UI to stay at 1
console.log(count);
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, []); // Empty dependency array, effect runs only once, closure locks in initial count
}Corrective approach (production-grade):
Use functional updates or useRef to hold a reference to the latest value.
function Counter() {
const [count, setCount] = useState(0);
// Option A: useRef to keep the latest value (suitable for event listeners, etc.)
const countRef = useRef(count);
countRef.current = count;
useEffect(() => {
const timer = setInterval(() => {
// Option B: functional update, prev is always the latest state
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
}2. Overuse of watch in Vue3
In Vue's Composition API, a common architectural mistake is treating watch as React's useEffect, which leads to imperative and hard-to-maintain code. In Vue, derived state should prefer computed, and watch should be used only for side effects (like API requests, DOM operations).
Anti-pattern example:
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = ref('');
// Mistake: manually maintaining data synchronization, easy to miss dependencies or cause race conditions
watch([firstName, lastName], ([newFirst, newLast]) => {
fullName.value = ${newFirst} ${newLast};
});Correction:
Leverage Vue’s fine-grained reactivity system and use computed to automatically track dependencies and cache results. As mentioned in Brilworks' analysis of Vue vs React, Vue’s strength lies in precisely tracking changes to component state, and computed embodies this advantage.
const firstName = ref('John');
const lastName = ref('Doe');
// Correct: declarative dependencies, automatic caching, better performance
const fullName = computed(() => ${firstName.value} ${lastName.value});3. The “God Hook” in logic reuse
Whether it’s React’s Custom Hooks or Vue’s Composables, beginner developers often make the mistake of creating a giant hook (for example useUserLogic) that contains all business logic, violating the single responsibility principle and making reuse difficult.
Best practice: atomic and composable
Refer to design patterns from open-source libraries like ahooks or VueUse; custom hooks should be “atomic.” A complex business hook should be composed from multiple basic hooks.
Code structure comparison:
- Bad (highly coupled):
// useTableLogic internally mixes paging, filtering, API requests, and even UI state
const { data, loading, run } = useTableLogic('/api/users'); - Good (compositional design):
// Break functionalities into independent hooks for easier testing and reuse
function useUserTable() {
const { pagination, setPage } = usePagination(); // paging logic
const { filters, setFilter } = useFilters(); // filtering logic
// Compose basic capabilities to implement business logic
const { data, loading } = useRequest(() =>
fetchUserList({ ...pagination, ...filters }),
{ refreshDeps: [pagination, filters] }
);
return { data, loading, pagination, filters };
}When showcasing code in interviews, it’s not just about delivering functionality; you should also demonstrate this kind of engineering thinking: code should run, but also be maintainable and testable. This is the key watershed that separates junior engineers from senior engineers.
Frontend Engineering: Build, Performance, and Deployment

In senior frontend interviews in 2025, interviewers are no longer satisfied with asking “what's the difference between Webpack Loader and Plugin”. The focus has shifted to engineering architecture skills: how to choose build tools based on business scale, how to design fine-grained caching strategies, and how to systematically optimize for Core Web Vitals. Engineering is the watershed between “writing pages” and “architecting systems”.
Evolution of Build Tools: Bundle vs. Bundleless and Rustification
The choice of build tool directly affects the developer experience and production performance. Current interview hotspots compare Webpack with modern tools (like Vite, Rspack) at a fundamental level:
- Webpack (Bundle based): The classic bundling approach requires analyzing the entire dependency graph and bundling at startup, suitable for large complex projects that require extreme control over the output.
- Vite (Bundleless in Dev): Leverages the browser's native ESM to achieve second-level cold starts. But in production it still relies on Rollup to get better code minification and tree-shaking effects.
- Rspack (Rust based): A hot topic in 2025. It is compatible with the Webpack API but rewrites core processes in Rust, addressing Webpack’s slow builds in very large projects.
Interview strategy: Don’t just recite features — answer from a “trade-off” perspective. For example: “When migrating an old large Monorepo, Rspack offers a smoother path than Vite because it is compatible with most Webpack Loaders.”
Fine-Grained Build Strategies: Tree Shaking and Code Splitting
“How to reduce bundle size” is a must-have question, but an advanced answer cannot stop at “compress images” or “enable Gzip”. You need to demonstrate deep understanding of build configuration, especially Tree Shaking and Code Splitting.
Tree Shaking relies on ES Module static analysis to eliminate unused code (dead code). A common interview trap is: “Why does Tree Shaking sometimes fail?” The answer often involves misconfigured sideEffects or dynamic references in code that prevent static analysis.
For code splitting, reasonable strategies can significantly improve cache hit rates. Below is a common production Webpack splitting strategy configuration example showing how to separate third-party libraries from business code:
// webpack.config.js - optimization.splitChunks example
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // split both sync and async code
maxInitialRequests: 5, // limit parallel requests to avoid HTTP/1.1 blocking
cacheGroups: {
// extract base frameworks like React/Vue; these libraries change less frequently and are suitable for long-term caching
framework: {
test: /[\\/]nodemodules\\/[\\/]/,
name: 'framework',
priority: 20,
},
// extract other third-party libraries
vendors: {
test: /[\\/]nodemodules[\\/]/,
name: 'vendors',
priority: 10,
},
// extract shared business code to avoid duplicate bundling across pages
commons: {
name: 'commons',
minChunks: 2, // extract only if referenced at least twice
priority: 0,
},
},
},
},
};Core Web Vitals and Targeted Optimizations
Performance optimization has evolved from “fast page load” to “good user experience”. Google’s Core Web Vitals are the current standard for measuring experience; in interviews you should precisely define the metrics and provide targeted solutions:
- LCP (Largest Contentful Paint): Measures loading performance.
- Architectural-level optimizations: Not just compressing resources, but optimizing the critical rendering path.
- Specific techniques: Use fetchpriority="high" to raise priority for above-the-fold critical images; leverage HTTP/2 multiplexing or HTTP/3 to reduce connection latency; implement resource lazy loading for non-first-screen components resource lazy loading.
- CLS (Cumulative Layout Shift): Measures visual stability.
- Common issue: Images or ads expand containers after loading, causing page jank.
- Solutions: Explicitly set width and height attributes for all img and video tags to reserve layout space; use skeleton screens as placeholders when inserting dynamic content.
- INP (Interaction to Next Paint): Measures interaction responsiveness (replacing the old FID).
- Core challenge: The main thread is blocked by long tasks.
- Optimization strategies: Use requestIdleCallback or scheduler.postTask to chunk non-critical computations; move complex data processing logic to Web Workers to avoid blocking UI rendering.
Additionally, for long list rendering, you can use the new CSS property content-visibility: auto to skip rendering calculations for off-screen elements — this is a lighter-weight browser-native alternative to traditional virtual scrolling, demonstrating your attention to the latest browser rendering mechanisms.
2025 New Focus: AI Engineering and System Design

If the interviews in 2020 tested whether you "can write code", the advanced interviews in 2025 test whether you "can architect systems". With the prevalence of AI-assisted programming tools, interviewers are no longer fixated on having you hand-write a canonical Promise; they are more likely to throw a fuzzy business scenario at you and observe how you break down requirements, weigh technology choices, and mitigate potential risks.
In this section, we analyze the three most prominent "system design" topics in 2025: AI application deployment, frontend monitoring systems, and micro-frontend architectures. For these questions, there is no single correct answer—only the most reasonable trade-offs.
1. Scenario 1: How to integrate LLM capabilities into an existing frontend project?
This is the most representative new topic of 2025. Interviewers care less about whether you've called OpenAI's API and more about how you handle the engineering challenges brought by streaming data.
Core points of assessment and response strategies:
- Communication protocol choice: Don’t answer only HTTP. Compare the pros and cons of Server-Sent Events (SSE) and WebSocket. For most one-way conversational scenarios, SSE is a lighter-weight choice that aligns with HTTP semantics, whereas WebSocket is better suited for duplex real-time voice or complex interactions.
- Streaming rendering and user experience:
- Typewriter effect: How do you parse chunked-arrival data (Chunked Transfer Encoding) and update the UI in real time?
- Markdown rendering performance: When generated text contains many code blocks or mathematical formulas, frequent reflows and repaints can cause the page to stutter. A high-scoring answer should mention using
memoto cache already-rendered paragraphs, or using a Web Worker to perform Markdown parsing.
- Context management: How does the frontend maintain conversation history? Before sending it to the backend, how do you estimate token counts to avoid exceeding the context window limit?
- Request cancellation: When the user clicks "stop generation", how do you gracefully abort fetch requests with
AbortControllerto save tokens?
2. Scenario 2: Designing a general-purpose frontend monitoring SDK
Monitoring systems are the touchstone of a seasoned engineer's engineering abilities. According to the 2025 problem bank from 面试鸭, "how to design a frontend logging/tracking SDK" has become a high-frequency, medium-to-high difficulty question.
High-scoring design ideas:
- Data collection (non-intrusive):
- Error capture: Distinguish
window.onerror(runtime errors),unhandledrejection(Promise exceptions), and framework-level errors (such as Vue'serrorHandleror React'sErrorBoundary). - Session recording: Mention the principle of RRWeb, namely serializing DOM changes (MutationObserver) to achieve “replay” of user operations, which is crucial for reproducing hard-to-locate bugs.
- Error capture: Distinguish
- Data reporting strategy:
- Reliability: Prefer using
Navigator.sendBeaconto ensure data can be sent on page unload without blocking navigation. - Peak shaving and valley filling: Design a task queue that uses
requestIdleCallbackto batch-report during browser idle time, or set thresholds (e.g., every 10 events or every 5 seconds) to merge requests and reduce server pressure.
- Reliability: Prefer using
- Fault tolerance and sampling: How to dynamically adjust sampling rates via server-provided configuration? If the logging server goes down, the SDK should support graceful degradation or local caching with retry mechanisms (IndexedDB).
3. Scenario 3: Micro-frontend architecture selection and rollout
Micro-frontends are not a new concept, but the 2025 focus is on decision logic: why choose A over B? Analysis of frequent exam topics from CSDN also points out that micro-frontend rollout plans are a must-ask in architecture interviews.
Key comparison dimensions:
Solution | Core principle | Advantages | Disadvantages | Suitable scenarios |
|---|---|---|---|---|
iframe | Browser-native isolation | Perfect JS/CSS isolation, simple to implement | URL state synchronization is difficult, modals cannot cover the whole page, communication is cumbersome | Legacy system integration, backend panels with low UX requirements |
qiankun (base shell mode) | Based on single-spa, HTML Entry | Tech-stack agnostic, mature ecosystem, resource preloading | CSS sandboxing (Shadow DOM) may have compatibility pitfalls, risk of global variable pollution | Large central platforms with a unified tech stack |
Wujie (Wujie) | Web Component + iframe | Combines iframe isolation with SPA experience | Relatively new; community ecosystem not as mature as qiankun | New projects pursuing extreme isolation and performance |
Module Federation | Webpack 5 module federation | Shared dependencies, runtime loading | Requires unified build tools (Webpack/Rspack), weaker isolation | Multi-team collaboration, component-level sharing rather than app-level |
Answering tips: Don’t memorize framework APIs by rote. Interviewers prefer hearing: “Our team mainly uses React and has no SEO requirements. To integrate an old system (jQuery), we chose qiankun, but encountered XX issues with style isolation, which we resolved through XX conventions.”
Summary: The "universal formula" for system design questions
When facing such open-ended questions, follow the "4S analysis" to organize your answer and demonstrate your depth of logic:
- Scenario: Clarify scale first (what QPS? daily active users?), core functions (is real-time prioritized or accuracy?), and constraints.
- Service: Design module decomposition (e.g., the SDK contains collection, reporting, and cleaning modules).
- Storage: Where does the data live? (LocalStorage, IndexedDB, CDN).
- Scale: How to handle high concurrency? How to do version control? How to ensure security?
This structured answering approach shows interviewers that you not only have coding ability but also a Tech Lead–level global perspective.
High-Frequency Handwritten Code Checklist (with Analysis)
In 2025 front-end interviews, the handwritten code section no longer simply tests API memorization, but evaluates candidates' engineering thinking, boundary-handling ability, and understanding of language internals through code details. Interviewers prefer candidates to implement a "production-ready" utility function rather than a teaching example that only passes the happy path.
Below are 7 core handwritten problem types, with key points distinguishing "junior" and "senior" expectations for each.
2025 Must-Practice Top 7 Core Checklist
- Concurrency Control Scheduler (Async Scheduler)
- What it tests: Not just
Promise.all, but restricting the number of concurrently running tasks (e.g., "at most 3 simultaneous requests"). - Key details: Use a queue to manage tasks, employ recursion or
awaitblocking mechanisms to control execution flow, and ensure tasks automatically fill vacancies after completion.
- What it tests: Not just
- Deep Clone
- What it tests: Handling circular references and special object types.
- Key details: Must use
WeakMapto cache already cloned objects; handleDate,RegExp, andSymboltyped keys/values.
- Debounce & Throttle
- What it tests: Use of closures and higher-order functions.
- Key details: Can you implement an
immediateoption? Can you support acancelmethod? Arethisbinding and argument passthrough correct?
- Custom Promise Implementation (A+ spec subset)
- What it tests: Asynchronous state machine management.
- Key details: Chaining of
thenand microtask simulation (commonly usingqueueMicrotaskorsetTimeout);catcherror propagation handling.
- Array Flatten
- What it tests: Converting recursion to iteration.
- Key details: Support a
depthlevel parameter; consider using Generators orreduce; avoid directly calling theflat()API.
- Publish-Subscribe Pattern (Event Emitter)
- What it tests: Design patterns and memory management.
- Key details: How does
offcorrectly remove callbacks (especially anonymous functions)? The wrapping logic foronce(auto-unsubscribe after one execution).
- Function Currying
- What it tests: Foundations of functional programming.
- Key details: Recursively collect arguments until the number of arguments meets
fn.lengthbefore executing; support placeholders (advanced).
---
Deep Case Analysis: From "Passing" to "Outstanding" (Deep Clone Example)
Deep cloning is a high-failure-rate interview question. Many candidates stop at a simple recursion and ignore the stack overflow risk from circular references, which directly exposes a lack of engineering experience.
Version A: Basic Implementation (suitable only for junior roles)
The simplest recursive approach cannot handle circular references and will lose Symbol keys.
function basicClone(target) {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
// Potential risk: does not filter prototype chain properties, does not handle circular references
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = basicClone(target[prop]);
}
}
return cloneTarget;
}
return target;
}Version B: Production-Level Implementation (Senior Standard)
This version introduces WeakMap to solve circular references (common interview reference), and adds support for special types and Symbols.
function deepClone(target, map = new WeakMap()) {
// 1. Primitive types and null are returned directly
if (target === null || typeof target !== 'object') return target;
// 2. Handle special object types (Date, RegExp)
if (target instanceof Date) return new Date(target);
if (target instanceof RegExp) return new RegExp(target);
// 3. Use WeakMap to solve circular references: if cached, return directly
if (map.has(target)) return map.get(target);
// 4. Initialize clone container (preserve array/object structure)
const cloneTarget = Array.isArray(target) ? [] : {};
// 5. Record cache to prevent recursive infinite loops
map.set(target, cloneTarget);
// 6. Get all keys, including Symbol-typed keys
const keys = [...Object.keys(target), ...Object.getOwnPropertySymbols(target)];
for (const key of keys) {
// Recursively clone child properties and pass through map
cloneTarget[key] = deepClone(target[key], map);
}
return cloneTarget;
}Code analysis and extra credit points:
- Circular reference defense: Use
WeakMapto store mapping between original objects and cloned objects.WeakMapkeys are weak references which aid garbage collection and are preferred over a regularMap. - Type coverage: Explicitly handle
DateandRegExpto avoid them being incorrectly processed as empty objects{}. - Symbol support: Use
Object.getOwnPropertySymbolsorReflect.ownKeysto ensure Symbol properties are not lost, which is critical in modern framework/library development. - Recursion optimization: Pass
mapas a parameter to keep the function signature simple.







