【详细精选】前端面试题(2026精选附详细解答)包含10w数据展示优化、前端核心
【详细精选】前端面试题(2026精选附详细答案)包含10w数据展示优化、前端核心
- 前端面试题详细解答
【支持与反对请投票和评论区留言】尽管前端已经非常内卷,开猿节流,甚至怀疑会被AI取代,但小y认为:前端依然是一门学科、一个岗位。不管是全栈还是前端,都会有存在的必要和价值。
直接上干货!!
前端面试题详细解答
1. ES6新特性详解(重要10个)
核心特性
// 1. let和const - 块级作用域let x =10;// 可重新赋值const y =20;// 不可重新赋值,但对象属性可修改// 2. 箭头函数constadd=(a, b)=> a + b;// 特点:没有自己的this,继承父级this;不能作为构造函数// 3. 模板字符串const name ="John";const greeting =`Hello, ${name}!`;// 支持多行字符串// 4. 解构赋值const[a, b]=[1,2];// 数组解构const{name, age}={name:"John",age:25};// 对象解构// 5. 默认参数functiongreet(name ="Guest"){return`Hello, ${name}`;}// 6. 扩展运算符const arr1 =[1,2];const arr2 =[...arr1,3,4];// [1, 2, 3, 4]const obj1 ={a:1};const obj2 ={...obj1,b:2};// {a: 1, b: 2}// 7. 对象简写const x =10, y =20;const point ={x, y};// 等同于 {x: x, y: y}// 8. Promiseconst promise =newPromise((resolve, reject)=>{setTimeout(()=>resolve("Done"),1000);});// 9. async/await(ES8,但通常归为ES6+)asyncfunctionfetchData(){const data =await promise;return data;}// 10. 类(Class)classPerson{constructor(name){this.name = name;}greet(){return`Hello, I'm ${this.name}`;}staticcreate(name){returnnewPerson(name);}}其他重要特性
- 模块化:
import/export - Symbol:唯一标识符
- Set/Map:新的数据结构
- for…of循环:遍历可迭代对象
- 生成器函数:
function* - Proxy/Reflect:元编程
- 可选链操作符:
?.(ES2020) - 空值合并运算符:
??(ES2020)
2. 什么是跨域
同源策略
// 同源条件:协议、域名、端口必须完全相同// http://example.com:80 ≠ https://example.com:80 (协议不同)// http://example.com:80 ≠ http://api.example.com:80 (域名不同)// http://example.com:80 ≠ http://example.com:8080 (端口不同)跨域解决方案
1.CORS(跨域资源共享)
// 服务器设置响应头 res.setHeader('Access-Control-Allow-Origin','http://localhost:3000'); res.setHeader('Access-Control-Allow-Methods','GET, POST, PUT'); res.setHeader('Access-Control-Allow-Headers','Content-Type'); res.setHeader('Access-Control-Allow-Credentials','true');// 允许携带cookie// 简单请求 vs 预检请求// 简单请求:GET/HEAD/POST,Content-Type为特定值// 预检请求:非简单请求前会发送OPTIONS请求2.JSONP
// 客户端functionhandleResponse(data){ console.log(data);}// 动态创建script标签const script = document.createElement('script'); script.src ='http://api.example.com/data?callback=handleResponse'; document.body.appendChild(script);// 服务器响应handleResponse({data:"some data"});3. 代理服务器
// 开发环境代理(webpack配置) module.exports ={devServer:{proxy:{'/api':{target:'http://api.example.com',changeOrigin:true,pathRewrite:{'^/api':''}}}}};4. WebSocket
// WebSocket不受同源策略限制const socket =newWebSocket('ws://api.example.com');5. Nginx反向代理
# nginx配置 location /api/ { proxy_pass http://api.example.com/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } 3. 监听数组变化
Vue2的实现原理
// 1. 重写数组方法const arrayProto =Array.prototype;const arrayMethods = Object.create(arrayProto);const methodsToPatch =['push','pop','shift','unshift','splice','sort','reverse']; methodsToPatch.forEach(function(method){const original = arrayProto[method]; Object.defineProperty(arrayMethods, method,{value:function(...args){const result =original.apply(this, args);const ob =this.__ob__;// 获取新增的元素let inserted;switch(method){case'push':case'unshift': inserted = args;break;case'splice': inserted = args.slice(2);break;}// 对新增元素进行响应式处理if(inserted) ob.observeArray(inserted);// 通知依赖更新 ob.dep.notify();return result;},enumerable:false,writable:true,configurable:true});});// 2. 替换数组的原型const arr =[1,2,3]; Object.setPrototypeOf(arr, arrayMethods);Vue3的实现原理
// 使用Proxy代理整个数组functionreactiveArray(arr){returnnewProxy(arr,{get(target, key){// 处理数组的特殊情况if(key ==='length')return target.length;const value = target[key];// 如果访问的是方法,绑定正确的thisif(typeof value ==='function'){returnvalue.bind(target);}return value;},set(target, key, value, receiver){const oldValue = target[key];// 设置属性值const result = Reflect.set(target, key, value, receiver);// 判断是新增还是修改const type =Number(key)< target.length ?'SET':'ADD';// 触发更新if(type ==='ADD'|| oldValue !== value){trigger(target, key, type);}return result;}});}4. v-if vs v-show
原理对比
<template> <!-- v-if:条件渲染 --> <div v-if="show">使用v-if</div> <!-- v-show:条件显示 --> <div v-show="show">使用v-show</div> </template> <script> export default { data() { return { show: true }; } }; </script> 差异对比表
| 特性 | v-if | v-show |
|---|---|---|
| DOM操作 | 条件为false时,元素从DOM移除 | 元素始终在DOM中,只切换display属性 |
| 编译/卸载 | 切换时触发组件的生命周期钩子 | 不触发生命周期钩子 |
| 性能 | 初始渲染开销小,切换开销大 | 初始渲染开销大,切换开销小 |
| 使用场景 | 不频繁切换,条件很少改变 | 频繁切换,需要保持状态 |
| 配合使用 | 可与v-else、v-else-if配合 | 没有配套指令 |
源码分析
// v-if的编译结果functionrender(){return show ?createElement('div','v-if内容'):createEmptyVNode();}// v-show的编译结果functionrender(){returncreateElement('div',{directives:[{name:'show',value: show }],style:{display: show ?'':'none'}},'v-show内容');}5. 网页加载优化
性能指标(Core Web Vitals)
- LCP(最大内容绘制) < 2.5秒
- FID(首次输入延迟) < 100毫秒
- CLS(累计布局偏移) < 0.1
优化策略
1. 代码优化
// 代码分割constLazyComponent=()=>import('./LazyComponent.vue');// 路由懒加载const routes =[{path:'/home',component:()=>import('./views/Home.vue')}];// Webpack配置优化 module.exports ={optimization:{splitChunks:{chunks:'all',// 代码分割cacheGroups:{vendor:{test:/[\\/]node_modules[\\/]/,name:'vendors'}}}}};2. 资源优化
<!-- 图片优化 --><imgsrc="image.jpg"srcset="image-320w.jpg 320w, image-480w.jpg 480w"sizes="(max-width: 600px) 480px, 800px"loading="lazy"<!--懒加载--> alt="描述文本" > <!-- 预加载关键资源 --><linkrel="preload"href="font.woff2"as="font"type="font/woff2"crossorigin><linkrel="preload"href="critical.css"as="style"><linkrel="prefetch"href="next-page.html"><!-- 预取 -->3. 缓存策略
// Service Worker缓存 self.addEventListener('install',event=>{ event.waitUntil( caches.open('v1').then(cache=>{return cache.addAll(['/','/styles/main.css','/script/main.js']);}));});4. 使用Lighthouse分析
# 命令行使用 npx lighthouse https://example.com --view # 生成报告 npx lighthouse https://example.com --output=json --output-path=report.json 6. 浏览器渲染过程
详细流程
1. 构建DOM树
// HTML → DOM树// <html> → DOM节点// <body> → DOM节点// <div> → DOM节点2. 构建CSSOM树
/* CSS → CSSOM树 */body{font-size: 16px;}div{color: red;}3. 渲染树构建
- 结合DOM和CSSOM
- 排除不可见元素(display: none)
4. 布局(重排)
// 计算每个元素的位置和大小// 触发重排的操作: element.style.width ='100px'; element.style.height ='200px'; window.getComputedStyle(element);5. 绘制(重绘)
// 填充像素// 触发重绘的操作: element.style.color ='red'; element.style.backgroundColor ='#fff';6. 合成
// GPU加速的图层合成 element.style.transform ='translateX(100px)';// 触发合成层优化建议
// 避免强制同步布局functionbadExample(){// 读取 → 写入 → 读取 → 写入const width = element.offsetWidth;// 触发重排 element.style.width = width +10+'px';// 触发重排}functiongoodExample(){// 读取 → 读取 → 写入 → 写入const width = element.offsetWidth;const height = element.offsetHeight; element.style.width = width +10+'px'; element.style.height = height +10+'px';}7. 节流和防抖
防抖(Debounce)
// 原理:在事件被触发n秒后再执行,如果在这n秒内又被触发,则重新计时functiondebounce(fn, delay){let timer =null;returnfunction(...args){const context =this;// 清除之前的定时器clearTimeout(timer);// 设置新的定时器 timer =setTimeout(()=>{fn.apply(context, args);}, delay);};}// 使用场景:搜索框输入、窗口resizeconst searchInput = document.getElementById('search'); searchInput.addEventListener('input',debounce(function(){ console.log('搜索:',this.value);},500));节流(Throttle)
// 原理:规定在一个单位时间内,只能触发一次函数// 时间戳实现functionthrottle(fn, delay){let lastTime =0;returnfunction(...args){const now = Date.now();const context =this;if(now - lastTime >= delay){fn.apply(context, args); lastTime = now;}};}// 定时器实现functionthrottle(fn, delay){let timer =null;let lastArgs =null;returnfunction(...args){ lastArgs = args;if(!timer){ timer =setTimeout(()=>{fn.apply(this, lastArgs); timer =null;}, delay);}};}// 使用场景:滚动加载、按钮点击 window.addEventListener('scroll',throttle(function(){ console.log('滚动位置:', window.scrollY);},100));结合版(加强版节流)
functionenhancedThrottle(fn, delay, options ={}){const{ leading =true, trailing =true}= options;let timer =null;let lastTime =0;let lastArgs =null;returnfunction(...args){const context =this;const now = Date.now();// 计算剩余时间const remaining = delay -(now - lastTime);// 更新参数 lastArgs = args;if(remaining <=0){// 时间到了,执行函数if(timer){clearTimeout(timer); timer =null;}if(leading){fn.apply(context, args);} lastTime = now;}elseif(!timer && trailing){// 设置定时器,确保最后一次执行 timer =setTimeout(()=>{fn.apply(context, lastArgs); timer =null; lastTime = Date.now();}, remaining);}};}8. 闭包
基本概念
// 闭包:能够访问其他函数作用域中变量的函数functionouter(){const name ="John";// 局部变量functioninner(){ console.log(name);// 访问外部函数的变量}return inner;// 返回内部函数}const closureFn =outer();closureFn();// "John"实际应用
1. 数据私有化
functioncreateCounter(){let count =0;// 私有变量return{increment(){ count++;return count;},decrement(){ count--;return count;},getCount(){return count;}};}const counter =createCounter(); console.log(counter.increment());// 1 console.log(counter.increment());// 2 console.log(counter.getCount());// 22. 函数工厂
functioncreateMultiplier(multiplier){returnfunction(x){return x * multiplier;};}const double =createMultiplier(2);const triple =createMultiplier(3); console.log(double(5));// 10 console.log(triple(5));// 153. 模块模式
const myModule =(function(){let privateVar =0;functionprivateMethod(){return privateVar;}return{publicMethod(){ privateVar++;returnprivateMethod();}};})(); console.log(myModule.publicMethod());// 1内存管理
// 闭包可能导致内存泄漏functionleakMemory(){const largeArray =newArray(1000000).fill('data');returnfunction(){ console.log('仍然持有largeArray的引用');// largeArray不会被垃圾回收};}// 解决方案:及时释放引用functionnoLeak(){let largeArray =newArray(1000000).fill('data');const result = largeArray.length;// 使用完后设为null largeArray =null;returnfunction(){ console.log('不持有largeArray的引用');return result;};}9. 浏览器线程
主要线程
1. 主线程(UI线程)
// 负责:DOM渲染、JavaScript执行、用户交互// 注意:JavaScript是单线程的 console.log(1);setTimeout(()=> console.log(2),0); console.log(3);// 输出:1, 3, 2(因为有事件循环)2. 浏览器内核线程
- GUI渲染线程:解析HTML/CSS,构建DOM树
- JavaScript引擎线程:执行JavaScript代码
- 事件触发线程:管理事件队列
- 定时器触发线程:setTimeout/setInterval
- 异步HTTP请求线程:XMLHttpRequest
事件循环机制
// 微任务(Microtask) vs 宏任务(Macrotask) console.log('script start');// 同步任务setTimeout(()=>{ console.log('setTimeout');// 宏任务},0); Promise.resolve().then(()=>{ console.log('promise1');// 微任务}).then(()=>{ console.log('promise2');// 微任务}); console.log('script end');// 执行顺序:// 1. script start// 2. script end// 3. promise1// 4. promise2// 5. setTimeoutWeb Workers
// 主线程const worker =newWorker('worker.js'); worker.postMessage({data:'Hello'}); worker.onmessage=function(event){ console.log('收到worker消息:', event.data);};// worker.js(另一个线程) self.onmessage=function(event){const result =heavyComputation(event.data); self.postMessage(result);};functionheavyComputation(data){// 耗时操作return data.toUpperCase();}10. Vue3 vs Vue2区别
核心差异对比表
| 特性 | Vue2 | Vue3 |
|---|---|---|
| 响应式原理 | Object.defineProperty | Proxy |
| Composition API | Options API | Composition API + Options API |
| 性能 | 较慢 | 更快(打包体积小40%) |
| TypeScript支持 | 一般 | 完善 |
| Fragment | 不支持 | 支持多根节点 |
| Teleport | 不支持 | 支持 |
| Suspense | 不支持 | 支持 |
| Tree-shaking | 有限 | 更好的支持 |
响应式系统
// Vue2响应式const data ={count:0}; Object.defineProperty(data,'count',{get(){ console.log('获取count');return value;},set(newValue){ console.log('设置count'); value = newValue;}});// Vue3响应式const data ={count:0};const proxy =newProxy(data,{get(target, key){ console.log(`获取${key}`);return target[key];},set(target, key, value){ console.log(`设置${key}`); target[key]= value;returntrue;}});Composition API示例
<template> <div> <p>Count: {{ count }}</p> <p>Double: {{ doubleCount }}</p> <button @click="increment">Increment</button> </div> </template> <script> import { ref, computed, onMounted } from 'vue'; export default { setup() { // 响应式数据 const count = ref(0); // 计算属性 const doubleCount = computed(() => count.value * 2); // 方法 function increment() { count.value++; } // 生命周期 onMounted(() => { console.log('组件已挂载'); }); return { count, doubleCount, increment }; } }; </script> 性能优化
// Vue3的静态提升const _hoisted_1 ={id:"app"};// 静态节点提升const _hoisted_2 =/*#__PURE__*/createVNode("div",null,"静态内容");// 静态节点// Vue3的Patch Flagconst vnode ={type:'div',props:{id:'app',class:'container'},children:[{type:'span',children:'动态内容',patchFlag:1/* TEXT */}]};11. new关键字
实现原理
// new关键字的作用functionPerson(name){this.name = name;}const john =newPerson('John');// 模拟new的实现functionmyNew(constructor,...args){// 1. 创建一个空对象const obj ={};// 2. 将对象的原型指向构造函数的prototype Object.setPrototypeOf(obj, constructor.prototype);// 3. 将构造函数的this绑定到新对象const result =constructor.apply(obj, args);// 4. 如果构造函数返回对象,则返回该对象,否则返回新对象return result instanceofObject? result : obj;}const john2 =myNew(Person,'John');详细步骤分析
functionAnimal(name){this.name = name;this.speak=function(){return`${this.name} makes a noise`;};// 如果构造函数返回一个对象,new表达式会返回这个对象// return { custom: 'object' };}Animal.prototype.eat=function(){return`${this.name} eats food`;};// 使用newconst animal =newAnimal('Dog');// 等同于const animal ={}; animal.__proto__ =Animal.prototype;Animal.call(animal,'Dog');12. bind、call、apply的区别
基本用法对比
const person ={name:'John',greet:function(greeting, punctuation){return`${greeting}, ${this.name}${punctuation}`;}};const anotherPerson ={name:'Jane'};// call:立即调用,参数逐个传递 person.greet.call(anotherPerson,'Hello','!');// "Hello, Jane!"// apply:立即调用,参数数组传递 person.greet.apply(anotherPerson,['Hi','!!']);// "Hi, Jane!!"// bind:返回新函数,不立即调用const boundGreet = person.greet.bind(anotherPerson,'Hey');boundGreet('?');// "Hey, Jane?"手写实现
// 实现callFunction.prototype.myCall=function(context,...args){// 如果context是null或undefined,指向全局对象 context = context || window;// 创建唯一key,避免属性冲突const fnKey =Symbol('fn');// 将函数作为context的方法 context[fnKey]=this;// 执行函数const result = context[fnKey](...args);// 删除添加的方法delete context[fnKey];return result;};// 实现applyFunction.prototype.myApply=function(context, argsArray =[]){ context = context || window;const fnKey =Symbol('fn'); context[fnKey]=this;const result = context[fnKey](...argsArray);delete context[fnKey];return result;};// 实现bindFunction.prototype.myBind=function(context,...bindArgs){const originalFn =this;returnfunctionboundFn(...callArgs){// 判断是否作为构造函数调用(使用new)const isConstructorCall =new.target !==undefined;if(isConstructorCall){// 如果是new调用,忽略绑定的thisreturnneworiginalFn(...bindArgs,...callArgs);}else{// 普通调用,使用绑定的thisreturnoriginalFn.apply(context,[...bindArgs,...callArgs]);}};};实际应用场景
// 1. 借用数组方法处理类数组对象functionsum(){// arguments是类数组,没有数组方法const args =Array.prototype.slice.call(arguments);return args.reduce((acc, val)=> acc + val,0);}// 2. 构造函数继承functionParent(name){this.name = name;}functionChild(name, age){Parent.call(this, name);// 继承属性this.age = age;}// 3. 事件处理函数绑定thisclassButton{constructor(){this.text ='Click me';// 使用bind保持this指向this.handleClick =this.handleClick.bind(this);}handleClick(){ console.log(this.text);}}13. TypeScript装饰器
装饰器类型
// 类装饰器functionClassDecorator(constructor:Function){console.log('类装饰器执行'); constructor.prototype.newProperty ='added by decorator';}// 方法装饰器functionMethodDecorator( target:any, propertyKey:string, descriptor: PropertyDescriptor ){console.log(`方法装饰器:${propertyKey}`);const originalMethod = descriptor.value; descriptor.value=function(...args:any[]){console.log(`调用方法:${propertyKey}`);returnoriginalMethod.apply(this, args);};}// 属性装饰器functionPropertyDecorator(target:any, propertyKey:string){console.log(`属性装饰器:${propertyKey}`);}// 参数装饰器functionParameterDecorator( target:any, propertyKey:string, parameterIndex:number){console.log(`参数装饰器:${propertyKey}的第${parameterIndex}个参数`);}// 访问器装饰器functionAccessorDecorator( target:any, propertyKey:string, descriptor: PropertyDescriptor ){console.log(`访问器装饰器:${propertyKey}`);}// 装饰器工厂functionLogDecorator(message:string){returnfunction(target:any, propertyKey:string, descriptor?: PropertyDescriptor){console.log(`${message}: ${propertyKey}`);};}实际应用示例
// 1. 日志装饰器functionlog(target:any, propertyKey:string, descriptor: PropertyDescriptor){const originalMethod = descriptor.value; descriptor.value=function(...args:any[]){console.log(`调用 ${propertyKey},参数:`, args);const result =originalMethod.apply(this, args);console.log(`返回结果:`, result);return result;};}// 2. 自动绑定this装饰器functionautobind( _target:any, _propertyKey:string, descriptor: PropertyDescriptor ){const originalMethod = descriptor.value;const adjustedDescriptor: PropertyDescriptor ={ configurable:true, enumerable:false,get(){// 返回绑定this的方法returnoriginalMethod.bind(this);}};return adjustedDescriptor;}// 3. 验证装饰器functionvalidate( target:any, propertyKey:string, descriptor: PropertyDescriptor ){const originalMethod = descriptor.value; descriptor.value=function(...args:any[]){// 参数验证逻辑 args.forEach((arg, index)=>{if(arg ===undefined|| arg ===null){thrownewError(`参数 ${index} 不能为空`);}});returnoriginalMethod.apply(this, args);};}// 使用示例classUserService{private users:string[]=[];@log@validateaddUser(@ParameterDecorator name:string){this.users.push(name);returnthis.users;}@autobindgetUsers(){returnthis.users;}}14. 10w数据展示优化
虚拟列表实现
<template> <div ref="container" @scroll="handleScroll" > <!-- 撑开容器,显示滚动条 --> <div :style="{ height: totalHeight + 'px' }" ></div> <!-- 可视区域内容 --> <div :style="{ transform: `translateY(${offset}px)` }" > <div v-for="item in visibleData" :key="item.id" :style="{ height: itemHeight + 'px' }" > {{ item.content }} </div> </div> </div> </template> <script> export default { props: { data: { type: Array, required: true }, itemHeight: { type: Number, default: 50 }, buffer: { type: Number, default: 5 } }, data() { return { startIndex: 0, // 开始索引 endIndex: 0, // 结束索引 offset: 0 // 偏移量 }; }, computed: { // 总高度 totalHeight() { return this.data.length * this.itemHeight; }, // 容器高度 containerHeight() { return this.$refs.container?.clientHeight || 0; }, // 可见区域项数 visibleCount() { return Math.ceil(this.containerHeight / this.itemHeight); }, // 实际渲染的数据 visibleData() { const start = Math.max(0, this.startIndex - this.buffer); const end = Math.min(this.data.length, this.endIndex + this.buffer); return this.data.slice(start, end); } }, mounted() { this.updateVisibleData(); }, methods: { handleScroll() { const scrollTop = this.$refs.container.scrollTop; // 计算开始索引 this.startIndex = Math.floor(scrollTop / this.itemHeight); // 计算结束索引 this.endIndex = Math.min( this.data.length, this.startIndex + this.visibleCount ); // 计算偏移量 this.offset = this.startIndex * this.itemHeight; }, updateVisibleData() { this.startIndex = 0; this.endIndex = Math.min(this.data.length, this.visibleCount); } } }; </script> <style> .virtual-list-container { height: 500px; overflow-y: auto; position: relative; } .virtual-list-phantom { position: absolute; left: 0; top: 0; right: 0; z-index: -1; } .virtual-list-content { position: absolute; left: 0; top: 0; right: 0; } .virtual-list-item { display: flex; align-items: center; padding: 0 16px; box-sizing: border-box; border-bottom: 1px solid #eee; } </style> 时间分片渲染
// 使用requestAnimationFrame分批渲染functionrenderLargeData(data, container, chunkSize =100){let index =0;functionrenderChunk(){const fragment = document.createDocumentFragment();for(let i =0; i < chunkSize && index < data.length; i++, index++){const item = document.createElement('div'); item.textContent = data[index]; fragment.appendChild(item);} container.appendChild(fragment);// 如果还有数据,继续渲染下一批if(index < data.length){requestAnimationFrame(renderChunk);}}renderChunk();}// 使用Web Worker处理数据const worker =newWorker('data-processor.js'); worker.postMessage({action:'process',data: largeData }); worker.onmessage=function(event){const processedData = event.data;// 在主线程渲染处理后的数据};15. 时间切片
基本原理
// 使用requestIdleCallback实现时间切片functiontimeSlicingWork(unitsOfWork){let currentIndex =0;functiondoWork(deadline){// 在空闲时间内执行工作单元while(currentIndex < unitsOfWork.length && deadline.timeRemaining()>0){performUnitOfWork(unitsOfWork[currentIndex]); currentIndex++;}// 如果还有工作,继续调度if(currentIndex < unitsOfWork.length){requestIdleCallback(doWork);}else{ console.log('所有工作完成');}}requestIdleCallback(doWork);}// React中的时间切片实现(简化版)functionworkLoop(deadline){let shouldYield =false;while(nextUnitOfWork &&!shouldYield){// 执行工作单元 nextUnitOfWork =performUnitOfWork(nextUnitOfWork);// 检查是否需要让出控制权 shouldYield = deadline.timeRemaining()<1;}// 如果还有工作,继续调度if(nextUnitOfWork){requestIdleCallback(workLoop);}}requestIdleCallback(workLoop);实际应用示例
// 1. 大数据处理functionprocessBigData(data, processItem, chunkSize =100){returnnewPromise((resolve)=>{let index =0;const results =[];functionprocessChunk(deadline){while(index < data.length && deadline.timeRemaining()>0){const chunk = data.slice(index, index + chunkSize);// 处理数据块for(const item of chunk){ results.push(processItem(item));} index += chunkSize;// 更新进度updateProgress((index / data.length)*100);}if(index < data.length){// 还有数据,继续处理requestIdleCallback(processChunk);}else{// 处理完成resolve(results);}}requestIdleCallback(processChunk);});}// 2. 长列表渲染优化classVirtualScroller{constructor(container, items, renderItem){this.container = container;this.items = items;this.renderItem = renderItem;this.renderedItems =newSet();}renderVisibleItems(){const visibleRange =this.getVisibleRange();const itemsToRender =[];for(let i = visibleRange.start; i < visibleRange.end; i++){if(!this.renderedItems.has(i)){ itemsToRender.push(i);}}// 使用时间切片分批渲染this.renderWithTimeSlice(itemsToRender);}renderWithTimeSlice(itemIndices){let index =0;constrenderBatch=(deadline)=>{while(index < itemIndices.length && deadline.timeRemaining()>0){const itemIndex = itemIndices[index];const item =this.items[itemIndex];// 渲染单个项目const element =this.renderItem(item, itemIndex);this.container.appendChild(element);this.renderedItems.add(itemIndex); index++;}if(index < itemIndices.length){requestIdleCallback(renderBatch);}};requestIdleCallback(renderBatch);}}React Concurrent Mode中的时间切片
// React 18+中的并发特性import{ startTransition, useDeferredValue }from'react';functionSearchResults({ query }){const deferredQuery =useDeferredValue(query);return(<div><SlowList query={deferredQuery}/></div>);}// 在事件处理中使用startTransitionfunctionhandleChange(e){setInput(e.target.value);// 将setQuery标记为过渡更新startTransition(()=>{setQuery(e.target.value);});}// 这会告诉React这个更新不是紧急的,可以被中断最后,欢迎对前端岗位是否会被AI取代投票。如觉得面试题有用,评论区留言,会继续更新小中大厂常见精选面试题。