前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 每日一题
    • 第401题 前端如何实现大模型的流式输出?
    • 第400题 RAG 是什么,一次检索增强生成经历了哪些步骤?
    • 第399题 Function Calling 与 MCP 有什么区别?
    • 第398题 什么是 Agent Loop,如何防止智能体失控?
    • 第397题 大文件如何做分片上传、断点续传和秒传?
    • 第396题 什么是单点登录,同域与跨域 SSO 如何实现?
    • 第395题 Promise.all、allSettled、race 和 any 应该怎么选?
    • 第394题 什么是执行上下文和调用栈?
    • 第393题 如何实现函数结果缓存,哪些场景不适合缓存?
    • 第392题 TypeScript 中 interface 和 type 有什么区别?
    • 第391题 WebSocket 和 SSE 有什么区别,实时通信该怎么选?
    • 第390题 多个请求同时过期时,如何避免重复刷新 token?
    • 第389题 Vue 3 中 ref 和 reactive 有什么区别?
    • 第388题 Vue 中 computed 和 watch 应该怎么选?
    • 第387题 十万条数据如何渲染,定高与不定高虚拟列表有什么区别?
    • 第386题 面向对象的三大特性
    • 第385题 创建对象有几种方法
    • 第384题 原型、构造函数、实例,以及原型链
    • 第383题 new 运算符
    • 第382题 类的定义、实例化
    • 第381题 继承的几种方式
    • 第380题 DOM 事件级别有哪些,DOM0 和 DOM2 有什么区别
    • 第379题 DOM 事件模型和事件流是怎么回事,捕获顺序是什么
    • 第378题 Event 对象有哪些常用 API
    • 第377题 自定义事件怎么创建和派发
    • 第376题 为什么 GUI 渲染线程与 JS 引擎线程互斥
    • 第375题 从 Event Loop 看 JS 的运行机制
    • 第374题 宏任务和微任务有什么区别,执行顺序是什么
    • 第373题 谈一谈你对 CSS 盒模型的认识
    • 第372题 JS 如何设置、获取盒模型对应的宽和高
    • 第371题 什么是 margin 塌陷(外边距重叠),怎么解决
    • 第370题 BFC 是什么,怎么触发,能解决什么问题
    • 第369题 三栏布局有哪几种写法(左右定宽、中间自适应)
    • 第368题 CSRF
    • 第367题 XSS
    • 第366题 CSRF 和 XSS 的区别
    • 第365题 同源策略的概念和具体限制
    • 第364题 前后端如何通信
    • 第363题 如何创建Ajax
    • 第362题 跨域通信的几种方式
    • 第361题 前端错误的分类
    • 第360题 每种错误的捕获方式
    • 第359题 错误上报的两种方式
    • 第358题 HTTP协议的主要特点
    • 第357题 HTTP报文的组成部分
    • 第356题 HTTP方法
    • 第355题 get 和 post的区别
    • 第354题 http状态码
    • 第353题 持久链接/http长连接
    • 第352题 长连接中的管线化
    • 第351题 CSS 选择器有哪些,优先级怎么计算
    • 第350题 什么是 BFC,有什么作用,如何形成
    • 第349题 position 有哪些值,各自相对谁定位
    • 第348题 Flex 布局有什么好处
    • 第347题 介绍一下 CSS 盒模型
    • 第346题 有哪些方式可以使 div 居中
    • 第345题 CSS 相关的性能优化有哪些
    • 第344题 双飞翼布局和圣杯布局有什么区别
    • 第343题 浮动元素会造成什么影响,如何清除浮动
    • 第342题 CSS 样式隔离有哪些手段
    • 第341题 行内元素和块级元素有哪些,行为有什么不同
    • 第340题 CSS3 有哪些重要新特性
    • 第339题 层叠上下文是什么
    • 第338题 重排和重绘是什么,有什么区别
    • 第337题 动画性能如何优化
    • 第336题 setState 是同步还是异步
    • 第335题 为什么两次 setState 有时只更新一次
    • 第334题 setState 和 useState 有什么区别
    • 第333题 如何解决 <a> 元素点击后 :hover 样式看似失效的问题?
    • 第332题 点击一个 input 元素时,事件通常按什么顺序触发
    • 第331题 响应式设计有哪些好处与工程取舍
    • 第330题 null 和 undefined 有什么区别?
    • 第329题 冒泡排序与数组去重的实现和取舍
    • 第328题 请描述一下 Promise
    • 第327题 Promise.all 中有一个任务抛出异常时会如何处理
    • 第326题 Promise 为什么能够链式调用
    • 第325题 window、document、html 与 body 之间是什么关系
    • 第324题 addEventListener 的第三个参数有什么作用?
    • 第323题 typeof 和 instanceof 有什么区别
    • 第322题 为什么 typeof null 的结果是 "object"?
    • 第321题 详细说说 instanceof 的原理、边界与适用场景
    • 第320题 如何准确描述 this,函数中的 this 在什么时候确定?
    • 第319题 apply、call 和 bind 有什么区别?
    • 第318题 Webpack 中的 Loader 和 Plugin 有什么区别
    • 第317题 HTTP 和 TCP 有什么不同
    • 第316题 TCP 和 UDP 的区别
    • 第315题 介绍一下虚拟 DOM
    • 第314题 从输入 URL 到页面呈现,浏览器经历了哪些过程
    • 第313题 JSONP 的原理及手写实现
    • 第312题 浏览器为什么限制跨域访问?小程序等客户端为什么看起来没有跨域限制?
    • 第311题 CORS 跨域的工作原理是什么
    • 第310题 CORS 预检请求 OPTIONS 就一定安全吗?
    • 第309题 在深圳的浏览器中访问百度,请求是如何到达北京服务器的
    • 第308题 Vue 的响应式原理
    • 第307题 在 Vue 3 的响应式系统中,数据变化是如何通知页面更新并建立数据与视图关联的?
    • 第306题 CommonJS 与 ES Modules 有什么区别
    • 第305题 前端模块如何实现异步加载
    • 第304题 如何让一组异步请求按顺序执行?有哪些实现方式?
    • 第303题 Promise.all() 是并发执行还是串行执行?
    • 第302题 Webpack 5 中几种 hash 的实现原理与使用区别
    • 第301题 Webpack 5 中如何处理图片资源?
    • 第300题 Flexbox 的兼容性如何?
    • 第299题 移动端开发中,CSS 单位应该如何选择
    • 第298题 rem 和 em 的区别
    • 第297题 移动端如何初始化根元素的字体大小
    • 第296题 移动端不同手机上 HTML 的默认字体大小都一样吗
    • 第295题 如果让你实现一个持续旋转的动画,你会怎么做
    • 第294题 animation 中的 steps() 有什么作用?
    • 第293题 移动端页面常用哪些调试工具,分别适合什么场景?
    • 第292题 V8 的垃圾回收通常在什么时候发生?
    • 第291题 请具体说明 JavaScript 的垃圾回收机制
    • 第290题 在项目中如何将 HTTP 请求迁移到 HTTPS
    • 第289题 meta 标签能把页面中的 HTTP 请求自动升级为 HTTPS 吗?
    • 第288题 如何拦截 HTTP 请求
    • 第287题 如何正确使用响应拦截器
    • 第286题 HTTPS 采用什么加密方式,TLS 握手如何建立安全连接?
    • 第285题 混合加密有什么好处
    • 第284题 浏览器在 HTTPS 连接中如何验证服务器身份
    • 第283题 说明 ETag 首部字段及其条件请求机制
    • 第282题 前端的 token 一般存放在哪里,应该如何选择
    • 第281题 Token 会不会被伪造?
    • 第280题 HTTPS 与 TLS 握手的工作流程
    • 第279题 前后端如何判断用户已退出登录或处于离线状态
    • 第278题 你了解 CSP 白名单机制吗?
    • 第277题 Vue 3 的 diff 算法是如何工作的
    • 第276题 浏览器兼容性问题应如何分析和处理?
    • 第275题 如何实现 Array.prototype.findIndex
    • 第274题 移动端布局有哪些常见方案,应该如何选择?
    • 第273题 移动端项目需要在 PC 端展示时,如何保证页面结构稳定?
    • 第272题 具体说说 Webpack 5 的 SplitChunksPlugin
    • 第271题 如何编写一个现代 Webpack 插件
    • 第270题 说一下 Vue Router 的实现原理
    • 第269题 Vue Router 的初始化发生在什么时候?
    • 第268题 Webpack 5 的构建流程是什么
    • 第267题 Webpack 插件的工作原理是什么,如何实现一个插件
    • 第266题 Webpack 配置中的插件数组有执行顺序吗
    • 第265题 让你从零开始构建一个 Webpack 5 项目,你会怎么做
    • 第264题 为什么 TCP 建立连接需要三次握手,而不是两次
    • 第263题 哪些情况会阻塞页面的加载与渲染
    • 第262题 script 放在 body 开头就一定会阻塞页面吗
    • 第261题 增删 DOM 节点时浏览器会发生什么?
    • 第260题 使用 left、right 与 transform 改变元素位置有什么区别
    • 第259题 什么是 GPU 加速?
    • 第258题 进程和线程有什么区别
    • 第257题 HTTP/2 相比 HTTP/1.1 有哪些改进与局限?
    • 第256题 为什么说 HTTPS 比 HTTP 更安全?
    • 第255题 说一下对称加密和非对称加密的区别
    • 第254题 HTTP 请求中何时使用对称加密,何时使用非对称加密
    • 第253题 对称加密的原理与工程实践
    • 第252题 Vue 中 key 的作用是什么
    • 第251题 除了 XSS,还知道哪些常见的 Web 攻击方式
    • 第250题 可以把列表项的 key 设置为 Math.random() 吗
    • 第249题 如果让你设计一个双向绑定系统,你会如何实现?
    • 第248题 如何让 if (a === 1 && a === 2 && a === 3) 条件成立?
    • 第247题 认证令牌存放在 Cookie、localStorage 和 sessionStorage 中有什么区别
    • 第246题 Cookie 存在哪些安全风险?应如何防护?
    • 第245题 SameSite=Lax 如何控制 Cookie 的发送
    • 第244题 请求跨越不同的可注册域名时,Cookie 会发送吗
    • 第243题 使用 JSONP 会带来哪些安全问题?
    • 第242题 SSR 适用于哪些场景
    • 第241题 requestAnimationFrame 属于宏任务还是微任务
    • 第240题 script、CSS 与页面解析和渲染的先后关系
    • 第239题 script 标签的 async 脚本何时加载和执行
    • 第238题 说说 JavaScript 中 == 的类型转换规则
    • 第237题 虚拟 DOM 的 diff 算法有哪些局限性
    • 第236题 Object.defineProperty() 实现响应式有哪些局限?Vue 3 为什么改用 Proxy?
    • 第235题 Vue 3 中 nextTick 的实现原理与使用边界
    • 第234题 Vue 的 nextTick 中 waiting 何时变为 true?
    • 第233题 Vue 3 新增了哪些 API,又做了哪些重要优化?
    • 第232题 请说明 HTTP 缓存相关的首部字段及其作用
    • 第231题 TCP 在一次传输中如何保证数据按顺序交付?
    • 第230题 为什么说 GET 请求会留下浏览器历史记录?
    • 第229题 GET 请求可以上传图片吗?
    • 第228题 GET 请求一定是幂等的吗?
    • 第227题 为什么说 POST 相对更安全吗
    • 第226题 点击按钮发起 GET 请求会留下浏览器历史记录吗?
    • 第225题 普通文档流中的元素如何排列和形成层级关系
    • 第224题 inline-block 有哪些使用场景和注意事项
    • 第223题 说说前端常见的缓存方案及其工程实践
    • 第222题 前端项目中的环境变量应如何配置和控制?
    • 第221题 一个广告页面,如何统计用户在不同平台上的停留时间
    • 第220题 Redux Saga 是什么,和 Redux Thunk 有什么区别
    • 第219题 Redux 的 compose 函数和中间件分别做什么
    • 第218题 JSX 转换后是什么样的
    • 第217题 PM2 的原理是什么,fork 和 cluster 模式有什么区别
    • 第216题 Vue 和 React 应该如何做技术选型
    • 第215题 JWT 的原理是什么,token 一般存在哪里
    • 第214题 手写实现一个简易的 useState
    • 第213题 GraphQL 相比 REST 有什么区别和优劣
    • 第212题 什么是 BFF,如何做全链路日志打通
    • 第211题 什么是 Trusted Types,防什么攻击
    • 第210题 HTTP/3 和 QUIC 解决了什么问题
    • 第209题 什么是边缘渲染(Edge Rendering)
    • 第208题 什么是 hydration,它有哪些问题
    • 第207题 INP 指标差如何优化交互性能
    • 第206题 Core Web Vitals 有哪些核心指标
    • 第205题 用 aspect-ratio 防止累积布局偏移(CLS)
    • 第204题 CSS 的 :has() 选择器能做什么
    • 第203题 CSS 容器查询(container query)有什么用
    • 第202题 Node Stream 的背压(backpressure)是什么
    • 第201题 Web Worker 的使用场景和限制
    • 第200题 IntersectionObserver 和 MutationObserver 的使用场景
    • 第199题 如何实现一个可取消的请求(AbortController)
    • 第198题 什么是 Module Federation 模块联邦
    • 第197题 esbuild 和 SWC 为什么比 Babel 快
    • 第196题 Vite 为什么比 webpack 快
    • 第195题 什么是 Signals 细粒度响应式
    • 第194题 手写实现 TS 的 ReturnType 和 Parameters
    • 第193题 TypeScript 的 satisfies 操作符解决什么问题
    • 第192题 TypeScript 的 infer 关键字有什么用
    • 第191题 Pinia 相比 Vuex 有哪些改进
    • 第190题 Vue3 的 defineModel 和 defineOptions 是什么
    • 第189题 Vue3 的 <script setup> 做了哪些编译期处理
    • 第188题 React Compiler 自动记忆化的原理是什么
    • 第187题 React Server Components 和 SSR 有什么区别
    • 第186题 React 19 的 use 和 Actions 有什么用
    • 第185题 什么是服务器端渲染(SSR)?SSR 的原理是什么
    • 第184题 假如有几十个请求,如何去控制并发
    • 第183题 Hooks闭包陷阱问题
    • 第182题 Suspense 有哪些使用场景,使用 Suspense 的好处有哪些?
    • 第181题 怎么理解 React 并发更新特性
    • 第180题 webpack性能优化-构建速度
    • 第179题 webpack性能优化-产出代码(线上运行)
    • 第178题 获取当前页面URL参数
    • 第177题 手写深度比较lodash.isEqual
    • 第176题 常见的web前端攻击方式有哪些
    • 第175题 前端性能优化
    • 第174题 HTTP面试题总结
    • 第173题 DOM和事件操作总结
    • 第172题 Event Loop执行机制过程
    • 第171题 async/await异步总结
    • 第170题 Promise异步总结
    • 第169题 手写Promise加载一张图片
    • 第168题 创建10个a标签,点击弹出对应的序号
    • 第167题 闭包读代码题输出
    • 第166题 实现简易版jQuery
    • 第165题 原型与原型链
    • 第164题 两个数组求交集和并集
    • 第163题 JS反转字符串
    • 第162题 从零搭建开发环境需要考虑什么
    • 第161题 手写Vue3基本响应式原理
    • 第160题 实现机器人走方格
    • 第159题 this读代码题
    • 第158题 使用XML描述自定义DSL流程图
    • 第157题 JS设计并实现撤销重做功能
    • 第156题 根据jsx写出vnode和render函数
    • 第155题 手写合并两个递增数组
    • 第154题 React useEffect闭包陷阱问题
    • 第153题 Vue React diff 算法有什么区别
    • 第152题 如何做code-review
    • 第151题 手写JS深拷贝-考虑各种数据类型和循环引用
    • 第150题 用JS实现一个LRU缓存
    • 第149题 手写EventBus自定义事件
    • 第148题 手写curry函数,实现函数柯里化
    • 第147题 手写一个LazyMan,实现sleep机制
    • 第146题 深度优先和广度优先遍历一个DOM树
    • 第145题 手写一个getType函数,获取详细的数据类型
    • 第144题 手写一个JS函数,实现数组扁平化Array Flatten
    • 第143题 设计实现一个H5图片懒加载
    • 第142题 如果你是项目前端技术负责人,将如何做技术选型
    • 第141题 开发一个H5抽奖页,需要后端提供哪些接口
    • 第140题 简单描述hybrid模板的更新流程
    • 第139题 设计一个“用户-角色-权限”的模型和功能
    • 第138题 设计一个H5编辑器的数据模型和核心功能
    • 第137题 SPA和MPA应该如何选择
    • 第136题 如何设计一个前端统计SDK
    • 第135题 React setState经典面试题
    • 第134题 一道让人失眠的promise then执行顺序问题
    • 第133题 把一个数组转换为树
    • 第132题 [“1”, “2”, “3”].map(parseInt) 答案是多少
    • 第131题 工作中遇到过哪些项目难点,是如何解决的
    • 第130题 如果一个H5很慢,如何排查性能问题
    • 第129题 如何统一监听React组件报错
    • 第128题 如何统一监听Vue组件报错
    • 第127题 在实际工作中,你对React做过哪些优化
    • 第126题 在实际工作中,你对Vue做过哪些优化
    • 第125题 前端常用的设计模式和使用场景
    • 第124题 后端一次性返回十万条数据,你该如何渲染
    • 第123题 H5页面如何进行首屏优化
    • 第122题 如何实现网页多标签tab通讯
    • 第121题 从输入URL 到网页显示的完整过程
    • 第120题 WebSocket和HTTP协议有什么区别
    • 第119题 前端攻击手段有哪些,该如何预防
    • 第118题 script标签的defer和async有什么区别
    • 第117题 什么是HTTPS中间人攻击,如何预防(HTTPS加密过程、原理)
    • 第116题 HTTP协议1.0和1.1和2.0有什么区别
    • 第115题 HTTP请求中token、cookie、session有什么区别
    • 第114题 移动端H5点击有300ms延迟,该如何解决
    • 第113题 requestIdleCallback和requestAnimationFrame有什么区别
    • 第112题 请描述js-bridge的实现原理
    • 第111题 nodejs如何开启多进程,进程如何通讯
    • 第110题 遍历一个数组用for和forEach哪个更快
    • 第109题 虚拟DOM(vdom)真的很快吗
    • 第108题 浏览器和nodejs事件循环(Event Loop)有什么区别
    • 第107题 JS内存泄露如何检测?场景有哪些?
    • 第106题 HTTP跨域请求时为什么要发送options请求
    • 第105题 HTMLCollection和NodeList的区别
    • 第104题 for in和for of有什么区别
    • 第103题 请描述TCP三次握手和四次挥手
    • 第102题 什么时候不能使用箭头函数
    • 第101题 切换字母大小写
    • 第100题 实现数字千分位格式化
    • 第99题 高效的字符串前缀匹配如何做
    • 第98题 获取1-10000之前所有的对称数(回文数)
    • 第97题 实现快速排序并说明时间复杂度
    • 第96题 获取字符串中连续最多的字符以及次数
    • 第95题 将数组中的0移动到末尾
    • 第94题 求斐波那契数列的第n值
    • 第93题 求一个二叉搜索树的第k小值
    • 第92题 时间复杂度与空间复杂度基本概念
    • 第91题 给一个数组,找出其中和为n的两个元素(两数之和)
    • 第90题 实现二分查找并分析时间复杂度
    • 第89题 反转一个单项链表
    • 第88题 实现队列功能
    • 第87题 手写判断一个字符串"{a(b[c]d)e}f"是否括号匹配
    • 第86题 下面这道题输出什么
    • 第85题 下面这道题输出什么
    • 第84题 下面这道题输出什么
    • 第83题 动态生成4x4表格,每个表格中有坐标(0,0)-(3,3)点击格增加次数,次数互不干扰,次数通过弹窗显示
    • 第82题 实现add(1)(2)(3)等于6
    • 第81题 下面这道题输出什么
    • 第80题 require 具体实现原理是什么
    • 第79题 Es6 的 let 实现原理
    • 第78题 原型链判断
    • 第77题 手写 Vue.extend 实现
    • 第76题 怎么在制定数据源里面生成一个长度为 n 的不重复随机数组 能有几种方法 时间复杂度多少(字节)
    • 第75题 如何找到数组中第一个没出现的最小正整数 怎么优化(字节)
    • 第74题 字符串最长的不重复子串
    • 第73题 查找数组公共前缀(美团)
    • 第72题 判断括号字符串是否有效(小米)
    • 第71题 实现一个对象的 flatten 方法(阿里)
    • 第70题 将虚拟DOM转化为真实DOM
    • 第69题 手写setTimeout 模拟实现 setInterval(阿里)
    • 第68题 Vue nextTick 原理
    • 第67题 实现一个比setTimeout快 80 倍的定时器
    • 第66题 数组转为tree
    • 第65题 原型调用面试题 说出结果并说出 why
    • 第64题 函数执行 说出结果并说出why
    • 第63题 如何拦截全局Promise reject
    • 第62题 JS执行机制 说出结果并说出why
    • 第61题 实现继承
    • 第60题 实现深拷贝
    • 第59题 不使用循环API 来删除数组中指定位置的元素(如:删除第三位) 写越多越好
    • 第58题 一个正则题
    • 第57题 实现Promisify
    • 第56题 完整实现Promises/A+规范
    • 第55题 JS中 ?? 与 || 的区别
    • 第54题 HTTP 中的 301、302、303、307、308 响应状态码
    • 第53题 简单请求和复杂请求的区别
    • 第52题 实现一个对象被for of遍历
    • 第51题 判断JS对象是否存在循环引用
    • 第50题 对象的深度比较
    • 第49题 实现一个find函数,并且find函数能够满足下列条件
    • 第48题 实现数组扁平化的 6 种方式
    • 第47题 JS易错题
    • 第46题 关于0.1+0.2!=0.3浮点数计算分析与解决方法
    • 第45题 介绍一下Tree Shaking及其工作原理
    • 第44题 执行new Vue干了什么
    • 第43题 说一下vue2.x中如何监测数组变化
    • 第42题 介绍 HTTPS 握手过程
    • 第41题 解释以下代码输出什么
    • 第40题 实现 (5).add(3).minus(2) 功能
    • 第39题 React setState 笔试题,下面的代码输出什么?
    • 第38题 (头条)异步笔试题
    • 第37题 实现ES6的class
    • 第36题 实现一个柯里化函数
    • 第35题 实现一个简易的MVVM
    • 第34题 实现一下hash路由
    • 第33题 实现一个发布订阅者模式
    • 第32题 关于async/await代码执行顺序
    • 第31题 实现一个padStart()或padEnd()的polyfill
    • 第30题 设计一个方法提取对象中所有value大于2的键值对并返回最新的对象
    • 第29题 用一个正则提取字符串中所有""里内容
    • 第28题 去除字符串首位空格
    • 第27题 用正则写一个根据name获取cookie中的值的方法
    • 第26题 实现 arr[-1] = arr[arr.length - 1]
    • 第25题 JSONP的原理并用代码实现
    • 第24题 实现一个拖拽
    • 第23题 项目中你做过哪些优化
    • 第22题 手写Promise最简20行版本,实现异步链式调用
    • 第21题 实现一个迷你版的vue
    • 第20题 基于Promise.all实现Ajax的串行和并行
    • 第19题 JQ Ajax、Axios、Fetch的核心区别
    • 第18题 基于HTTP网络层的前端性能优化
    • 第17题 实现vue reactive原理
    • 第16题 异步串行 | 异步并行
    • 第15题 以下代码输出什么
    • 第14题 如何让if(a == 1 && a == 2)条件成立
    • 第13题 异步执行顺序问题
    • 第12题 微任务执行问题 async await
    • 第11题 this指向问题
    • 第10题 promise执行问题
    • 第9题 promise解决并发请求
    • 第8题 跨域问题
    • 第7题 下面输出什么
    • 第6题 验证回文串
    • 第5题 写一个函数来判断它是否是 3 的幂次方
    • 第4题 旋转数组
    • 第3题 修改嵌套层级很深对象的 key
    • 第2题 只出现一次的数字
    • 第1题 两数之和

# 第401题 前端如何实现大模型的流式输出?

30 秒速记

  • 流式输出不让总时间变短,解决的是「首字等太久」,让用户一直看到进展
  • fetch + response.body.getReader() 读流,同一个 TextDecoder 带 { stream: true } 解码
  • 一次 read() 不等于一条消息:要自己留 buffer,按 \n 或 SSE 的空行拆帧,处理半包和粘包
  • 协议里要能区分增量文本、错误、结束;用户停止用 AbortController,后端也要跟着停
  • 渲染别一个 token 刷一次,合并到每帧一次,Markdown 解析和代码高亮要节流

前端做流式输出,就是边读响应流边拼字,但要自己处理「一块数据不等于一条消息」这件事。 打个比方,后端是一条一条发快递,网络中途可能把两个包捆一起送,也可能把一个包拆成两半送。所以我会用 getReader() 一直读,同一个 TextDecoder 解码,防止中文字被切成两半变乱码,再在 buffer 里按换行拆出完整的 JSON。页面上把增量攒一下,每帧刷一次,不然回答一长就卡。另外取消、断网、服务端报错要分开提示,别只留半截文字。

const reader = res.body.getReader()
const decoder = new TextDecoder() // 只建一个,跨块保留半个字符
let buffer = ''
while (true) {
  const { value, done } = await reader.read()
  buffer += decoder.decode(value, { stream: !done })
  const lines = buffer.split('\n')
  buffer = lines.pop() // 最后一段可能不完整,留到下一轮
  for (const line of lines) if (line.trim()) onEvent(JSON.parse(line))
  if (done) break
}
@前端进阶之旅: 代码已经复制到剪贴板

假设服务端返回按行分隔的 NDJSON,每行都是一个独立的 JSON 事件。一次网络读取可能只拿到半行,也可能一次拿到三行,所以必须保留未完整的尾部:

async function* readNdjson(response, signal) {
  if (!response.ok || !response.body) {
    throw new Error(`HTTP ${response.status}`)
  }

  const reader = response.body.getReader()
  const decoder = new TextDecoder()
  let buffer = ''

  try {
    while (true) {
      if (signal?.aborted) throw signal.reason
      const { value, done } = await reader.read()
      buffer += decoder.decode(value, { stream: !done })

      const lines = buffer.split('\n')
      buffer = lines.pop() ?? ''
      for (const line of lines) {
        if (line.trim()) yield JSON.parse(line)
      }

      if (done) break
    }
    if (buffer.trim()) yield JSON.parse(buffer)
  } finally {
    reader.releaseLock()
  }
}

const controller = new AbortController()
const response = await fetch('/api/chat', {
  method: 'POST',
  signal: controller.signal,
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ prompt: '解释事件循环' })
})

let answer = ''
for await (const event of readNdjson(response, controller.signal)) {
  if (event.type === 'delta') answer += event.text
  if (event.type === 'error') throw new Error(event.message)
  if (event.type === 'done') break
}
@前端进阶之旅: 代码已经复制到剪贴板

TextDecoder.decode(value, { stream: true }) 会保留未组成完整字符的字节,避免中文在数据块边界处变成乱码。buffer 解决的则是应用层半包问题。生产中还应给每次对话分配请求标识,只允许当前请求更新页面;用户重新提问时,先调用 AbortController.abort() 终止旧流,防止旧回答串到新消息里。

渲染层的常见瓶颈是更新太细。模型一秒可能送出几十个增量,如果每次都重新解析全部 Markdown,回答越长开销越大。可以先把文本收进变量,通过 requestAnimationFrame 或 30–50ms 的刷新窗口更新视图。流结束后再做一次完整校验和最终渲染,并保留错误态、重试入口与已接收的部分内容。

面试官追问

追问 1线上偶尔有中文显示成 �,后端日志里是正常的,先查哪?
参考回答

查前端是不是每块都 new TextDecoder(),或者没传 { stream: true }。一个中文在 UTF-8 里占 3 个字节,刚好被切在两块之间就会乱码,同一个解码器才能把半个字符留到下一块。

追问 2后端说每次 write 都是完整 JSON,前端直接对每次 read() 的结果 JSON.parse 行不行?
参考回答

不行。中间有代理、压缩、TCP 分段,一次 write 可能被拆开或和下一次合并。必须按协议分隔符拆帧,buffer 里不完整的尾巴留到下一轮。

追问 3用户点了停止,页面不动了,但服务端还在调模型计费,算取消成功吗?
参考回答

不算。前端 controller.abort() 只是断开连接,服务端要监听连接关闭,再把取消传给下游模型请求。不然只是界面不收了,钱照样在花。

追问 4回答越长页面越卡,Performance 里全是 Markdown 解析,怎么改?
参考回答

先把增量攒起来,用 requestAnimationFrame 每帧最多渲染一次。再看能不能只重新解析最后一个没闭合的段落,已经完成的部分缓存起来,代码高亮放到流结束后再做。

追问 5用户连着发了两个问题,第一个回答串到了第二条消息里,为什么?
参考回答

旧流没停,还在往同一个状态里写。发新问题前先 abort 旧请求,每条消息带个请求 id,只有当前 id 的数据才允许更新页面。

# 第400题 RAG 是什么,一次检索增强生成经历了哪些步骤?

30 秒速记

  • RAG = 先检索、再生成:从知识库找证据塞进提示词,模型参数一点没变
  • 离线:清洗 → 切块 → 算 embedding → 建索引;在线:改写问题 → 召回 → 权限过滤 → 重排 → 拼上下文 → 生成
  • 只靠向量会丢精确词(型号、错误码),实际多用关键词 + 向量混合召回,再用重排模型精排
  • 切块太小丢上下文,太大噪声多还费 token;块里带上标题、版本、来源方便引用
  • 权限过滤必须在内容进模型之前做;效果评估要把「召回对不对」和「答得好不好」分开测

RAG 就是让模型开卷考试:先去知识库翻出相关的几段,再让它照着这几段回答。 它适合公司文档、产品手册这种经常变的知识,改文档只要重建索引,比重新微调模型快得多也便宜得多。但链路远不止「向量搜一下」:切块切得好不好、关键词和向量要不要混合、谁有权看哪篇、召回的几十条怎么重排挑前几条,每一环都会影响结果。召回错了,模型说得再顺也是在胡说。我排查答错的问题,第一步永远是看正确的那段有没有进候选。

const candidates = await hybridSearch(question, { limit: 20 }) // 关键词 + 向量
const visible = candidates.filter(c => c.allowedUserIds.includes(userId)) // 先过滤权限
const top = (await rerank(question, visible)).slice(0, 5)                // 再精排取前 5
const answer = await generate({ question, evidence: top })               // 最后带证据生成
@前端进阶之旅: 代码已经复制到剪贴板

下面的示例用一个可替换的检索器表达在线链路。真实项目可以把 search 换成向量数据库与全文搜索的混合召回,但权限过滤必须发生在内容进入模型之前:

type Chunk = {
  id: string
  text: string
  score: number
  allowedUserIds: string[]
}

async function answerWithRag(question: string, userId: string) {
  
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 每日一题
    • 第401题 前端如何实现大模型的流式输出?
    • 第400题 RAG 是什么,一次检索增强生成经历了哪些步骤?
    • 第399题 Function Calling 与 MCP 有什么区别?
    • 第398题 什么是 Agent Loop,如何防止智能体失控?
    • 第397题 大文件如何做分片上传、断点续传和秒传?
    • 第396题 什么是单点登录,同域与跨域 SSO 如何实现?
    • 第395题 Promise.all、allSettled、race 和 any 应该怎么选?
    • 第394题 什么是执行上下文和调用栈?
    • 第393题 如何实现函数结果缓存,哪些场景不适合缓存?
    • 第392题 TypeScript 中 interface 和 type 有什么区别?
    • 第391题 WebSocket 和 SSE 有什么区别,实时通信该怎么选?
    • 第390题 多个请求同时过期时,如何避免重复刷新 token?
    • 第389题 Vue 3 中 ref 和 reactive 有什么区别?
    • 第388题 Vue 中 computed 和 watch 应该怎么选?
    • 第387题 十万条数据如何渲染,定高与不定高虚拟列表有什么区别?
    • 第386题 面向对象的三大特性
    • 第385题 创建对象有几种方法
    • 第384题 原型、构造函数、实例,以及原型链
    • 第383题 new 运算符
    • 第382题 类的定义、实例化
    • 第381题 继承的几种方式
    • 第380题 DOM 事件级别有哪些,DOM0 和 DOM2 有什么区别
    • 第379题 DOM 事件模型和事件流是怎么回事,捕获顺序是什么
    • 第378题 Event 对象有哪些常用 API
    • 第377题 自定义事件怎么创建和派发
    • 第376题 为什么 GUI 渲染线程与 JS 引擎线程互斥
    • 第375题 从 Event Loop 看 JS 的运行机制
    • 第374题 宏任务和微任务有什么区别,执行顺序是什么
    • 第373题 谈一谈你对 CSS 盒模型的认识
    • 第372题 JS 如何设置、获取盒模型对应的宽和高
    • 第371题 什么是 margin 塌陷(外边距重叠),怎么解决
    • 第370题 BFC 是什么,怎么触发,能解决什么问题
    • 第369题 三栏布局有哪几种写法(左右定宽、中间自适应)
    • 第368题 CSRF
    • 第367题 XSS
    • 第366题 CSRF 和 XSS 的区别
    • 第365题 同源策略的概念和具体限制
    • 第364题 前后端如何通信
    • 第363题 如何创建Ajax
    • 第362题 跨域通信的几种方式
    • 第361题 前端错误的分类
    • 第360题 每种错误的捕获方式
    • 第359题 错误上报的两种方式
    • 第358题 HTTP协议的主要特点
    • 第357题 HTTP报文的组成部分
    • 第356题 HTTP方法
    • 第355题 get 和 post的区别
    • 第354题 http状态码
    • 第353题 持久链接/http长连接
    • 第352题 长连接中的管线化
    • 第351题 CSS 选择器有哪些,优先级怎么计算
    • 第350题 什么是 BFC,有什么作用,如何形成
    • 第349题 position 有哪些值,各自相对谁定位
    • 第348题 Flex 布局有什么好处
    • 第347题 介绍一下 CSS 盒模型
    • 第346题 有哪些方式可以使 div 居中
    • 第345题 CSS 相关的性能优化有哪些
    • 第344题 双飞翼布局和圣杯布局有什么区别
    • 第343题 浮动元素会造成什么影响,如何清除浮动
    • 第342题 CSS 样式隔离有哪些手段
    • 第341题 行内元素和块级元素有哪些,行为有什么不同
    • 第340题 CSS3 有哪些重要新特性
    • 第339题 层叠上下文是什么
    • 第338题 重排和重绘是什么,有什么区别
    • 第337题 动画性能如何优化
    • 第336题 setState 是同步还是异步
    • 第335题 为什么两次 setState 有时只更新一次
    • 第334题 setState 和 useState 有什么区别
    • 第333题 如何解决 <a> 元素点击后 :hover 样式看似失效的问题?
    • 第332题 点击一个 input 元素时,事件通常按什么顺序触发
    • 第331题 响应式设计有哪些好处与工程取舍
    • 第330题 null 和 undefined 有什么区别?
    • 第329题 冒泡排序与数组去重的实现和取舍
    • 第328题 请描述一下 Promise
    • 第327题 Promise.all 中有一个任务抛出异常时会如何处理
    • 第326题 Promise 为什么能够链式调用
    • 第325题 window、document、html 与 body 之间是什么关系
    • 第324题 addEventListener 的第三个参数有什么作用?
    • 第323题 typeof 和 instanceof 有什么区别
    • 第322题 为什么 typeof null 的结果是 "object"?
    • 第321题 详细说说 instanceof 的原理、边界与适用场景
    • 第320题 如何准确描述 this,函数中的 this 在什么时候确定?
    • 第319题 apply、call 和 bind 有什么区别?
    • 第318题 Webpack 中的 Loader 和 Plugin 有什么区别
    • 第317题 HTTP 和 TCP 有什么不同
    • 第316题 TCP 和 UDP 的区别
    • 第315题 介绍一下虚拟 DOM
    • 第314题 从输入 URL 到页面呈现,浏览器经历了哪些过程
    • 第313题 JSONP 的原理及手写实现
    • 第312题 浏览器为什么限制跨域访问?小程序等客户端为什么看起来没有跨域限制?
    • 第311题 CORS 跨域的工作原理是什么
    • 第310题 CORS 预检请求 OPTIONS 就一定安全吗?
    • 第309题 在深圳的浏览器中访问百度,请求是如何到达北京服务器的
    • 第308题 Vue 的响应式原理
    • 第307题 在 Vue 3 的响应式系统中,数据变化是如何通知页面更新并建立数据与视图关联的?
    • 第306题 CommonJS 与 ES Modules 有什么区别
    • 第305题 前端模块如何实现异步加载
    • 第304题 如何让一组异步请求按顺序执行?有哪些实现方式?
    • 第303题 Promise.all() 是并发执行还是串行执行?
    • 第302题 Webpack 5 中几种 hash 的实现原理与使用区别
    • 第301题 Webpack 5 中如何处理图片资源?
    • 第300题 Flexbox 的兼容性如何?
    • 第299题 移动端开发中,CSS 单位应该如何选择
    • 第298题 rem 和 em 的区别
    • 第297题 移动端如何初始化根元素的字体大小
    • 第296题 移动端不同手机上 HTML 的默认字体大小都一样吗
    • 第295题 如果让你实现一个持续旋转的动画,你会怎么做
    • 第294题 animation 中的 steps() 有什么作用?
    • 第293题 移动端页面常用哪些调试工具,分别适合什么场景?
    • 第292题 V8 的垃圾回收通常在什么时候发生?
    • 第291题 请具体说明 JavaScript 的垃圾回收机制
    • 第290题 在项目中如何将 HTTP 请求迁移到 HTTPS
    • 第289题 meta 标签能把页面中的 HTTP 请求自动升级为 HTTPS 吗?
    • 第288题 如何拦截 HTTP 请求
    • 第287题 如何正确使用响应拦截器
    • 第286题 HTTPS 采用什么加密方式,TLS 握手如何建立安全连接?
    • 第285题 混合加密有什么好处
    • 第284题 浏览器在 HTTPS 连接中如何验证服务器身份
    • 第283题 说明 ETag 首部字段及其条件请求机制
    • 第282题 前端的 token 一般存放在哪里,应该如何选择
    • 第281题 Token 会不会被伪造?
    • 第280题 HTTPS 与 TLS 握手的工作流程
    • 第279题 前后端如何判断用户已退出登录或处于离线状态
    • 第278题 你了解 CSP 白名单机制吗?
    • 第277题 Vue 3 的 diff 算法是如何工作的
    • 第276题 浏览器兼容性问题应如何分析和处理?
    • 第275题 如何实现 Array.prototype.findIndex
    • 第274题 移动端布局有哪些常见方案,应该如何选择?
    • 第273题 移动端项目需要在 PC 端展示时,如何保证页面结构稳定?
    • 第272题 具体说说 Webpack 5 的 SplitChunksPlugin
    • 第271题 如何编写一个现代 Webpack 插件
    • 第270题 说一下 Vue Router 的实现原理
    • 第269题 Vue Router 的初始化发生在什么时候?
    • 第268题 Webpack 5 的构建流程是什么
    • 第267题 Webpack 插件的工作原理是什么,如何实现一个插件
    • 第266题 Webpack 配置中的插件数组有执行顺序吗
    • 第265题 让你从零开始构建一个 Webpack 5 项目,你会怎么做
    • 第264题 为什么 TCP 建立连接需要三次握手,而不是两次
    • 第263题 哪些情况会阻塞页面的加载与渲染
    • 第262题 script 放在 body 开头就一定会阻塞页面吗
    • 第261题 增删 DOM 节点时浏览器会发生什么?
    • 第260题 使用 left、right 与 transform 改变元素位置有什么区别
    • 第259题 什么是 GPU 加速?
    • 第258题 进程和线程有什么区别
    • 第257题 HTTP/2 相比 HTTP/1.1 有哪些改进与局限?
    • 第256题 为什么说 HTTPS 比 HTTP 更安全?
    • 第255题 说一下对称加密和非对称加密的区别
    • 第254题 HTTP 请求中何时使用对称加密,何时使用非对称加密
    • 第253题 对称加密的原理与工程实践
    • 第252题 Vue 中 key 的作用是什么
    • 第251题 除了 XSS,还知道哪些常见的 Web 攻击方式
    • 第250题 可以把列表项的 key 设置为 Math.random() 吗
    • 第249题 如果让你设计一个双向绑定系统,你会如何实现?
    • 第248题 如何让 if (a === 1 && a === 2 && a === 3) 条件成立?
    • 第247题 认证令牌存放在 Cookie、localStorage 和 sessionStorage 中有什么区别
    • 第246题 Cookie 存在哪些安全风险?应如何防护?
    • 第245题 SameSite=Lax 如何控制 Cookie 的发送
    • 第244题 请求跨越不同的可注册域名时,Cookie 会发送吗
    • 第243题 使用 JSONP 会带来哪些安全问题?
    • 第242题 SSR 适用于哪些场景
    • 第241题 requestAnimationFrame 属于宏任务还是微任务
    • 第240题 script、CSS 与页面解析和渲染的先后关系
    • 第239题 script 标签的 async 脚本何时加载和执行
    • 第238题 说说 JavaScript 中 == 的类型转换规则
    • 第237题 虚拟 DOM 的 diff 算法有哪些局限性
    • 第236题 Object.defineProperty() 实现响应式有哪些局限?Vue 3 为什么改用 Proxy?
    • 第235题 Vue 3 中 nextTick 的实现原理与使用边界
    • 第234题 Vue 的 nextTick 中 waiting 何时变为 true?
    • 第233题 Vue 3 新增了哪些 API,又做了哪些重要优化?
    • 第232题 请说明 HTTP 缓存相关的首部字段及其作用
    • 第231题 TCP 在一次传输中如何保证数据按顺序交付?
    • 第230题 为什么说 GET 请求会留下浏览器历史记录?
    • 第229题 GET 请求可以上传图片吗?
    • 第228题 GET 请求一定是幂等的吗?
    • 第227题 为什么说 POST 相对更安全吗
    • 第226题 点击按钮发起 GET 请求会留下浏览器历史记录吗?
    • 第225题 普通文档流中的元素如何排列和形成层级关系
    • 第224题 inline-block 有哪些使用场景和注意事项
    • 第223题 说说前端常见的缓存方案及其工程实践
    • 第222题 前端项目中的环境变量应如何配置和控制?
    • 第221题 一个广告页面,如何统计用户在不同平台上的停留时间
    • 第220题 Redux Saga 是什么,和 Redux Thunk 有什么区别
    • 第219题 Redux 的 compose 函数和中间件分别做什么
    • 第218题 JSX 转换后是什么样的
    • 第217题 PM2 的原理是什么,fork 和 cluster 模式有什么区别
    • 第216题 Vue 和 React 应该如何做技术选型
    • 第215题 JWT 的原理是什么,token 一般存在哪里
    • 第214题 手写实现一个简易的 useState
    • 第213题 GraphQL 相比 REST 有什么区别和优劣
    • 第212题 什么是 BFF,如何做全链路日志打通
    • 第211题 什么是 Trusted Types,防什么攻击
    • 第210题 HTTP/3 和 QUIC 解决了什么问题
    • 第209题 什么是边缘渲染(Edge Rendering)
    • 第208题 什么是 hydration,它有哪些问题
    • 第207题 INP 指标差如何优化交互性能
    • 第206题 Core Web Vitals 有哪些核心指标
    • 第205题 用 aspect-ratio 防止累积布局偏移(CLS)
    • 第204题 CSS 的 :has() 选择器能做什么
    • 第203题 CSS 容器查询(container query)有什么用
    • 第202题 Node Stream 的背压(backpressure)是什么
    • 第201题 Web Worker 的使用场景和限制
    • 第200题 IntersectionObserver 和 MutationObserver 的使用场景
    • 第199题 如何实现一个可取消的请求(AbortController)
    • 第198题 什么是 Module Federation 模块联邦
    • 第197题 esbuild 和 SWC 为什么比 Babel 快
    • 第196题 Vite 为什么比 webpack 快
    • 第195题 什么是 Signals 细粒度响应式
    • 第194题 手写实现 TS 的 ReturnType 和 Parameters
    • 第193题 TypeScript 的 satisfies 操作符解决什么问题
    • 第192题 TypeScript 的 infer 关键字有什么用
    • 第191题 Pinia 相比 Vuex 有哪些改进
    • 第190题 Vue3 的 defineModel 和 defineOptions 是什么
    • 第189题 Vue3 的 <script setup> 做了哪些编译期处理
    • 第188题 React Compiler 自动记忆化的原理是什么
    • 第187题 React Server Components 和 SSR 有什么区别
    • 第186题 React 19 的 use 和 Actions 有什么用
    • 第185题 什么是服务器端渲染(SSR)?SSR 的原理是什么
    • 第184题 假如有几十个请求,如何去控制并发
    • 第183题 Hooks闭包陷阱问题
    • 第182题 Suspense 有哪些使用场景,使用 Suspense 的好处有哪些?
    • 第181题 怎么理解 React 并发更新特性
    • 第180题 webpack性能优化-构建速度
    • 第179题 webpack性能优化-产出代码(线上运行)
    • 第178题 获取当前页面URL参数
    • 第177题 手写深度比较lodash.isEqual
    • 第176题 常见的web前端攻击方式有哪些
    • 第175题 前端性能优化
    • 第174题 HTTP面试题总结
    • 第173题 DOM和事件操作总结
    • 第172题 Event Loop执行机制过程
    • 第171题 async/await异步总结
    • 第170题 Promise异步总结
    • 第169题 手写Promise加载一张图片
    • 第168题 创建10个a标签,点击弹出对应的序号
    • 第167题 闭包读代码题输出
    • 第166题 实现简易版jQuery
    • 第165题 原型与原型链
    • 第164题 两个数组求交集和并集
    • 第163题 JS反转字符串
    • 第162题 从零搭建开发环境需要考虑什么
    • 第161题 手写Vue3基本响应式原理
    • 第160题 实现机器人走方格
    • 第159题 this读代码题
    • 第158题 使用XML描述自定义DSL流程图
    • 第157题 JS设计并实现撤销重做功能
    • 第156题 根据jsx写出vnode和render函数
    • 第155题 手写合并两个递增数组
    • 第154题 React useEffect闭包陷阱问题
    • 第153题 Vue React diff 算法有什么区别
    • 第152题 如何做code-review
    • 第151题 手写JS深拷贝-考虑各种数据类型和循环引用
    • 第150题 用JS实现一个LRU缓存
    • 第149题 手写EventBus自定义事件
    • 第148题 手写curry函数,实现函数柯里化
    • 第147题 手写一个LazyMan,实现sleep机制
    • 第146题 深度优先和广度优先遍历一个DOM树
    • 第145题 手写一个getType函数,获取详细的数据类型
    • 第144题 手写一个JS函数,实现数组扁平化Array Flatten
    • 第143题 设计实现一个H5图片懒加载
    • 第142题 如果你是项目前端技术负责人,将如何做技术选型
    • 第141题 开发一个H5抽奖页,需要后端提供哪些接口
    • 第140题 简单描述hybrid模板的更新流程
    • 第139题 设计一个“用户-角色-权限”的模型和功能
    • 第138题 设计一个H5编辑器的数据模型和核心功能
    • 第137题 SPA和MPA应该如何选择
    • 第136题 如何设计一个前端统计SDK
    • 第135题 React setState经典面试题
    • 第134题 一道让人失眠的promise then执行顺序问题
    • 第133题 把一个数组转换为树
    • 第132题 [“1”, “2”, “3”].map(parseInt) 答案是多少
    • 第131题 工作中遇到过哪些项目难点,是如何解决的
    • 第130题 如果一个H5很慢,如何排查性能问题
    • 第129题 如何统一监听React组件报错
    • 第128题 如何统一监听Vue组件报错
    • 第127题 在实际工作中,你对React做过哪些优化
    • 第126题 在实际工作中,你对Vue做过哪些优化
    • 第125题 前端常用的设计模式和使用场景
    • 第124题 后端一次性返回十万条数据,你该如何渲染
    • 第123题 H5页面如何进行首屏优化
    • 第122题 如何实现网页多标签tab通讯
    • 第121题 从输入URL 到网页显示的完整过程
    • 第120题 WebSocket和HTTP协议有什么区别
    • 第119题 前端攻击手段有哪些,该如何预防
    • 第118题 script标签的defer和async有什么区别
    • 第117题 什么是HTTPS中间人攻击,如何预防(HTTPS加密过程、原理)
    • 第116题 HTTP协议1.0和1.1和2.0有什么区别
    • 第115题 HTTP请求中token、cookie、session有什么区别
    • 第114题 移动端H5点击有300ms延迟,该如何解决
    • 第113题 requestIdleCallback和requestAnimationFrame有什么区别
    • 第112题 请描述js-bridge的实现原理
    • 第111题 nodejs如何开启多进程,进程如何通讯
    • 第110题 遍历一个数组用for和forEach哪个更快
    • 第109题 虚拟DOM(vdom)真的很快吗
    • 第108题 浏览器和nodejs事件循环(Event Loop)有什么区别
    • 第107题 JS内存泄露如何检测?场景有哪些?
    • 第106题 HTTP跨域请求时为什么要发送options请求
    • 第105题 HTMLCollection和NodeList的区别
    • 第104题 for in和for of有什么区别
    • 第103题 请描述TCP三次握手和四次挥手
    • 第102题 什么时候不能使用箭头函数
    • 第101题 切换字母大小写
    • 第100题 实现数字千分位格式化
    • 第99题 高效的字符串前缀匹配如何做
    • 第98题 获取1-10000之前所有的对称数(回文数)
    • 第97题 实现快速排序并说明时间复杂度
    • 第96题 获取字符串中连续最多的字符以及次数
    • 第95题 将数组中的0移动到末尾
    • 第94题 求斐波那契数列的第n值
    • 第93题 求一个二叉搜索树的第k小值
    • 第92题 时间复杂度与空间复杂度基本概念
    • 第91题 给一个数组,找出其中和为n的两个元素(两数之和)
    • 第90题 实现二分查找并分析时间复杂度
    • 第89题 反转一个单项链表
    • 第88题 实现队列功能
    • 第87题 手写判断一个字符串"{a(b[c]d)e}f"是否括号匹配
    • 第86题 下面这道题输出什么
    • 第85题 下面这道题输出什么
    • 第84题 下面这道题输出什么
    • 第83题 动态生成4x4表格,每个表格中有坐标(0,0)-(3,3)点击格增加次数,次数互不干扰,次数通过弹窗显示
    • 第82题 实现add(1)(2)(3)等于6
    • 第81题 下面这道题输出什么
    • 第80题 require 具体实现原理是什么
    • 第79题 Es6 的 let 实现原理
    • 第78题 原型链判断
    • 第77题 手写 Vue.extend 实现
    • 第76题 怎么在制定数据源里面生成一个长度为 n 的不重复随机数组 能有几种方法 时间复杂度多少(字节)
    • 第75题 如何找到数组中第一个没出现的最小正整数 怎么优化(字节)
    • 第74题 字符串最长的不重复子串
    • 第73题 查找数组公共前缀(美团)
    • 第72题 判断括号字符串是否有效(小米)
    • 第71题 实现一个对象的 flatten 方法(阿里)
    • 第70题 将虚拟DOM转化为真实DOM
    • 第69题 手写setTimeout 模拟实现 setInterval(阿里)
    • 第68题 Vue nextTick 原理
    • 第67题 实现一个比setTimeout快 80 倍的定时器
    • 第66题 数组转为tree
    • 第65题 原型调用面试题 说出结果并说出 why
    • 第64题 函数执行 说出结果并说出why
    • 第63题 如何拦截全局Promise reject
    • 第62题 JS执行机制 说出结果并说出why
    • 第61题 实现继承
    • 第60题 实现深拷贝
    • 第59题 不使用循环API 来删除数组中指定位置的元素(如:删除第三位) 写越多越好
    • 第58题 一个正则题
    • 第57题 实现Promisify
    • 第56题 完整实现Promises/A+规范
    • 第55题 JS中 ?? 与 || 的区别
    • 第54题 HTTP 中的 301、302、303、307、308 响应状态码
    • 第53题 简单请求和复杂请求的区别
    • 第52题 实现一个对象被for of遍历
    • 第51题 判断JS对象是否存在循环引用
    • 第50题 对象的深度比较
    • 第49题 实现一个find函数,并且find函数能够满足下列条件
    • 第48题 实现数组扁平化的 6 种方式
    • 第47题 JS易错题
    • 第46题 关于0.1+0.2!=0.3浮点数计算分析与解决方法
    • 第45题 介绍一下Tree Shaking及其工作原理
    • 第44题 执行new Vue干了什么
    • 第43题 说一下vue2.x中如何监测数组变化
    • 第42题 介绍 HTTPS 握手过程
    • 第41题 解释以下代码输出什么
    • 第40题 实现 (5).add(3).minus(2) 功能
    • 第39题 React setState 笔试题,下面的代码输出什么?
    • 第38题 (头条)异步笔试题
    • 第37题 实现ES6的class
    • 第36题 实现一个柯里化函数
    • 第35题 实现一个简易的MVVM
    • 第34题 实现一下hash路由
    • 第33题 实现一个发布订阅者模式
    • 第32题 关于async/await代码执行顺序
    • 第31题 实现一个padStart()或padEnd()的polyfill
    • 第30题 设计一个方法提取对象中所有value大于2的键值对并返回最新的对象
    • 第29题 用一个正则提取字符串中所有""里内容
    • 第28题 去除字符串首位空格
    • 第27题 用正则写一个根据name获取cookie中的值的方法
    • 第26题 实现 arr[-1] = arr[arr.length - 1]
    • 第25题 JSONP的原理并用代码实现
    • 第24题 实现一个拖拽
    • 第23题 项目中你做过哪些优化
    • 第22题 手写Promise最简20行版本,实现异步链式调用
    • 第21题 实现一个迷你版的vue
    • 第20题 基于Promise.all实现Ajax的串行和并行
    • 第19题 JQ Ajax、Axios、Fetch的核心区别
    • 第18题 基于HTTP网络层的前端性能优化
    • 第17题 实现vue reactive原理
    • 第16题 异步串行 | 异步并行
    • 第15题 以下代码输出什么
    • 第14题 如何让if(a == 1 && a == 2)条件成立
    • 第13题 异步执行顺序问题
    • 第12题 微任务执行问题 async await
    • 第11题 this指向问题
    • 第10题 promise执行问题
    • 第9题 promise解决并发请求
    • 第8题 跨域问题
    • 第7题 下面输出什么
    • 第6题 验证回文串
    • 第5题 写一个函数来判断它是否是 3 的幂次方
    • 第4题 旋转数组
    • 第3题 修改嵌套层级很深对象的 key
    • 第2题 只出现一次的数字
    • 第1题 两数之和
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

前端每日一题:JavaScript、CSS、网络与框架高频面试题

# 第401题 前端如何实现大模型的流式输出?

30 秒速记

  • 流式输出不让总时间变短,解决的是「首字等太久」,让用户一直看到进展
  • fetch + response.body.getReader() 读流,同一个 TextDecoder 带 { stream: true } 解码
  • 一次 read() 不等于一条消息:要自己留 buffer,按 \n 或 SSE 的空行拆帧,处理半包和粘包
  • 协议里要能区分增量文本、错误、结束;用户停止用 AbortController,后端也要跟着停
  • 渲染别一个 token 刷一次,合并到每帧一次,Markdown 解析和代码高亮要节流

前端做流式输出,就是边读响应流边拼字,但要自己处理「一块数据不等于一条消息」这件事。 打个比方,后端是一条一条发快递,网络中途可能把两个包捆一起送,也可能把一个包拆成两半送。所以我会用 getReader() 一直读,同一个 TextDecoder 解码,防止中文字被切成两半变乱码,再在 buffer 里按换行拆出完整的 JSON。页面上把增量攒一下,每帧刷一次,不然回答一长就卡。另外取消、断网、服务端报错要分开提示,别只留半截文字。

const reader = res.body.getReader()
const decoder = new TextDecoder() // 只建一个,跨块保留半个字符
let buffer = ''
while (true) {
  const { value, done } = await reader.read()
  buffer += decoder.decode(value, { stream: !done })
  const lines = buffer.split('\n')
  buffer = lines.pop() // 最后一段可能不完整,留到下一轮
  for (const line of lines) if (line.trim()) onEvent(JSON.parse(line))
  if (done) break
}
@前端进阶之旅: 代码已经复制到剪贴板

假设服务端返回按行分隔的 NDJSON,每行都是一个独立的 JSON 事件。一次网络读取可能只拿到半行,也可能一次拿到三行,所以必须保留未完整的尾部:

async function* readNdjson(response, signal) {
  if (!response.ok || !response.body) {
    throw new Error(`HTTP ${response.status}`)
  }

  const reader = response.body.getReader()
  const decoder = new TextDecoder()
  let buffer = ''

  try {
    while (true) {
      if (signal?.aborted) throw signal.reason
      const { value, done } = await reader.read()
      buffer += decoder.decode(value, { stream: !done })

      const lines = buffer.split('\n')
      buffer = lines.pop() ?? ''
      for (const line of lines) {
        if (line.trim()) yield JSON.parse(line)
      }

      if (done) break
    }
    if (buffer.trim()) yield JSON.parse(buffer)
  } finally {
    reader.releaseLock()
  }
}

const controller = new AbortController()
const response = await fetch('/api/chat', {
  method: 'POST',
  signal: controller.signal,
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ prompt: '解释事件循环' })
})

let answer = ''
for await (const event of readNdjson(response, controller.signal)) {
  if (event.type === 'delta') answer += event.text
  if (event.type === 'error') throw new Error(event.message)
  if (event.type === 'done') break
}
@前端进阶之旅: 代码已经复制到剪贴板

TextDecoder.decode(value, { stream: true }) 会保留未组成完整字符的字节,避免中文在数据块边界处变成乱码。buffer 解决的则是应用层半包问题。生产中还应给每次对话分配请求标识,只允许当前请求更新页面;用户重新提问时,先调用 AbortController.abort() 终止旧流,防止旧回答串到新消息里。

渲染层的常见瓶颈是更新太细。模型一秒可能送出几十个增量,如果每次都重新解析全部 Markdown,回答越长开销越大。可以先把文本收进变量,通过 requestAnimationFrame 或 30–50ms 的刷新窗口更新视图。流结束后再做一次完整校验和最终渲染,并保留错误态、重试入口与已接收的部分内容。

面试官追问

追问 1线上偶尔有中文显示成 �,后端日志里是正常的,先查哪?
参考回答

查前端是不是每块都 new TextDecoder(),或者没传 { stream: true }。一个中文在 UTF-8 里占 3 个字节,刚好被切在两块之间就会乱码,同一个解码器才能把半个字符留到下一块。

追问 2后端说每次 write 都是完整 JSON,前端直接对每次 read() 的结果 JSON.parse 行不行?
参考回答

不行。中间有代理、压缩、TCP 分段,一次 write 可能被拆开或和下一次合并。必须按协议分隔符拆帧,buffer 里不完整的尾巴留到下一轮。

追问 3用户点了停止,页面不动了,但服务端还在调模型计费,算取消成功吗?
参考回答

不算。前端 controller.abort() 只是断开连接,服务端要监听连接关闭,再把取消传给下游模型请求。不然只是界面不收了,钱照样在花。

追问 4回答越长页面越卡,Performance 里全是 Markdown 解析,怎么改?
参考回答

先把增量攒起来,用 requestAnimationFrame 每帧最多渲染一次。再看能不能只重新解析最后一个没闭合的段落,已经完成的部分缓存起来,代码高亮放到流结束后再做。

追问 5用户连着发了两个问题,第一个回答串到了第二条消息里,为什么?
参考回答

旧流没停,还在往同一个状态里写。发新问题前先 abort 旧请求,每条消息带个请求 id,只有当前 id 的数据才允许更新页面。

# 第400题 RAG 是什么,一次检索增强生成经历了哪些步骤?

30 秒速记

  • RAG = 先检索、再生成:从知识库找证据塞进提示词,模型参数一点没变
  • 离线:清洗 → 切块 → 算 embedding → 建索引;在线:改写问题 → 召回 → 权限过滤 → 重排 → 拼上下文 → 生成
  • 只靠向量会丢精确词(型号、错误码),实际多用关键词 + 向量混合召回,再用重排模型精排
  • 切块太小丢上下文,太大噪声多还费 token;块里带上标题、版本、来源方便引用
  • 权限过滤必须在内容进模型之前做;效果评估要把「召回对不对」和「答得好不好」分开测

RAG 就是让模型开卷考试:先去知识库翻出相关的几段,再让它照着这几段回答。 它适合公司文档、产品手册这种经常变的知识,改文档只要重建索引,比重新微调模型快得多也便宜得多。但链路远不止「向量搜一下」:切块切得好不好、关键词和向量要不要混合、谁有权看哪篇、召回的几十条怎么重排挑前几条,每一环都会影响结果。召回错了,模型说得再顺也是在胡说。我排查答错的问题,第一步永远是看正确的那段有没有进候选。

const candidates = await hybridSearch(question, { limit: 20 }) // 关键词 + 向量
const visible = candidates.filter(c => c.allowedUserIds.includes(userId)) // 先过滤权限
const top = (await rerank(question, visible)).slice(0, 5)                // 再精排取前 5
const answer = await generate({ question, evidence: top })               // 最后带证据生成
@前端进阶之旅: 代码已经复制到剪贴板

下面的示例用一个可替换的检索器表达在线链路。真实项目可以把 search 换成向量数据库与全文搜索的混合召回,但权限过滤必须发生在内容进入模型之前:

type Chunk = {
  id: string
  text: string
  score: number
  allowedUserIds: string[]
}

async function answerWithRag(question: string, userId: string) {
  const candidates = await search(question, { limit: 20 })
  const visible = candidates.filter((chunk: Chunk) =>
    chunk.allowedUserIds.includes(userId)
  )
  const evidence = (await rerank(question, visible))
    .slice(0, 5)
    .map((chunk, index) => `[${index + 1}] ${chunk.text}`)
    .join('\n\n')

  return generate({
    system: '只根据证据作答;证据不足时直说不确定,并标注引用编号。',
    prompt: `问题:${question}\n\n证据:\n${evidence}`
  })
}
@前端进阶之旅: 代码已经复制到剪贴板

这段代码的顺序很重要:先召回,再用当前用户身份过滤,然后重排和裁剪。如果先把候选文本送给外部重排服务,再做权限过滤,未授权文档已经泄露给了下游。上下文也不应无限堆叠,应优先保留能直接支撑答案的片段,同时附上文档标识、版本和原文位置,便于用户核对。

评估时要把“检索对不对”和“模型答得好不好”分开。可以准备一组带标准证据的问题,检查正确片段是否进入前 K 名,再单独检查答案是否被证据支持、引用是否可回溯。否则一旦答错,团队只会笼统地调整提示词,却没发现真正问题是切块或召回。

fe