news 2026/8/30 12:53:50

快手前端面试全流程复盘:基础、框架与场景题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
快手前端面试全流程复盘:基础、框架与场景题

快手这家公司的前端面试,整体风格给我的感觉是:基础考得细,框架问得深,场景题特别务实。不像有些公司上来就甩一堆偏题怪题,快手更关注你“有没有真的做过东西”,以及“做的时候有没有想过为什么”。这篇文章我就把当时的面经完整复盘一遍,把每道题背后的考察点、我当时怎么答的、后来复盘觉得应该怎么答更好,全部拆开讲清楚。如果你是准备跳槽的前端,或者正在面快手,这篇内容应该能帮你少走不少弯路。

1. 面试全流程回顾:从简历筛选到HR面

1.1 快手的整体面试流程与节奏

快手的面试流程一般是三轮技术面加一轮HR面,部分地区或者部门可能会有加面,但整体节奏比较紧凑。我当时走完整个流程用了大概两周时间,每轮面试间隔三到五天,不会让你等得特别焦虑。

第一轮通常是基础面,主要考察JavaScript、CSS、浏览器原理这些基本功,偶尔会穿插一两道简单的算法题。第二轮开始偏向框架和工程化,React或者Vue的原理、组件设计、打包工具、性能优化都会涉及,这一轮也最容易刷人。第三轮一般是leader面或者交叉面,这时候不再单纯问知识点,而是给你一个实际场景,看你怎么分析和解决问题,更像是在考察你的综合能力。

这里要提醒一下,快手的面试官普遍喜欢顺着你的回答往下追问。比如你说自己用过某个API,他会接着问这个API的源码实现,再问如果让你自己实现一个类似的你会怎么做。所以面试前一定要把自己简历里写的东西吃透,没有真正实践过的内容宁可不写,否则追问两三轮就会露馅。

1.2 面试前的准备策略与心态调整

面快手之前,我其实刚经历了另一家公司的面试打击,那家公司在框架原理上追得特别深,我有一半问题都答得磕磕绊绊。所以这次我调整了策略,没有一上来就刷题,而是先把前端知识体系重新梳理了一遍

具体做法是画了一张脑图,按照JavaScript、CSS、浏览器、网络、框架、工程化、性能优化这几个大方向,把每个方向下的核心知识点列出来,然后逐一自问自答。比如JavaScript下面我会列出来原型链、闭包、作用域、事件循环、异步方案这些,然后问自己“如果让我给一个刚入门的人讲清楚闭包是什么,我能不能做到”。如果发现某个问题讲不清楚,就说明这块还有盲区。

心态上也要调整好。面快手这种体量的公司,面试官见过太多候选人,你的水平几斤几两,基本上十分钟之内他就摸清楚了。所以不要试图伪装或者背答案,大大方方承认自己哪些地方没深入过,反而比硬撑着胡编要好得多。当然,承认不会之后最好补一句“但是我的理解是大概怎样,我打算面试结束后去深入看一下”,这样至少能体现你的学习意识。

2. 第一轮技术面:JavaScript基础与浏览器原理

2.1 高频考点:事件循环、闭包与异步机制

第一轮面试刚开始,面试官先是让我做了个自我介绍,然后就直接切入了JavaScript基础。第一个问题就是经典的事件循环,不过他没有直接问“什么是事件循环”,而是给了一段代码让我说出输出顺序。

console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('script end');

这道题考察的是宏任务与微任务的执行顺序,核心结论是:同步代码先执行,然后执行当前宏任务队列中的所有微任务,微任务执行过程中如果产生了新的微任务也会一起执行完,最后才会轮到下一个宏任务。

所以输出顺序是:script start、script end、promise1、promise2、setTimeout。我当时答对了,但面试官紧接着追问了一句“如果setTimeout和Promise嵌套在一起会怎样”,这才是真正的考点。比如:

setTimeout(function() { console.log('timeout1'); Promise.resolve().then(function() { console.log('promise in timeout'); }); }, 0); setTimeout(function() { console.log('timeout2'); }, 0);

这里要注意的是,两个setTimeout都属于宏任务,先执行第一个setTimeout的回调,打印timeout1,然后它内部产生的微任务promise in timeout会被加入微任务队列,但需要等当前宏任务执行完毕后才会处理。所以输出是timeout1、promise in timeout、timeout2。这个细节很容易答错,大家一定要记住:每个宏任务执行完之后,都会清空当前的微任务队列,再进入下一个宏任务

闭包也是必考的点。面试官让我“说一段闭包的代码,并解释为什么能访问外部变量”,然后继续追问了闭包的内存泄漏问题。生产环境常见的坑是循环引用,比如把DOM元素保存在数组里,又在DOM上挂了一个引用指向这个数组,就会形成循环引用导致内存无法释放。现在大部分浏览器引擎的垃圾回收器已经能处理循环引用了,但闭包本身持有外部作用域的引用,如果这个外部作用域包含了很大的对象,即使闭包本身很小,也会把大对象一直留在内存里。所以闭包不是不能用,而是要注意控制作用域范围。

2.2 经典问题:原型链与继承方案对比

面试官问完事件循环之后,直接跳到原型链。他给我画了一个简单的对象关系图,让我说出objObj.prototypeObj.__proto__Function.prototype之间的关系。这个问题其实是在考察你是否真正理解JavaScript里“函数也是对象”这个概念。

我当时的回答思路是这样的:

  • obj是一个普通对象,它的__proto__指向Obj.prototype
  • Obj是个构造函数,它的__proto__指向Function.prototype
  • Obj.prototype是一个对象,它的__proto__指向Object.prototype
  • Function.prototype__proto__指向Object.prototype

画出来就是一条完整的链路:obj -> Obj.prototype -> Object.prototype -> null,而构造函数那一侧是Obj -> Function.prototype -> Object.prototype -> null

接着他问继承的实现方式,我列了原型链继承、构造函数继承、组合继承、寄生组合继承这几种,然后重点说了ES6的class继承其实也是基于寄生组合继承实现的。Class里面的extends关键字做的事情,本质上是把子类的原型对象指向父类原型对象的一个新对象,同时修改子类的__proto__指向父类构造函数。这个细节可能很多写业务代码的人没注意过,但在面试里答出来会很加分。

关于原型链这块,我后来复盘觉得有一个点值得补充:不要死记硬背继承的代码实现,而要理解prototype和__proto__的指向关系。面试官考察继承的目的,不是想让你背代码,而是想看你是否理解“JavaScript通过原型链实现属性查找”这个底层机制。你只要把“对象查找属性 -> 顺着原型链向上找 -> 找到就返回,找不到就返回undefined”这条链路想清楚,很多问题都能迎刃而解。

3. 第二轮技术面:框架原理与工程化实践

3.1 框架考察方向:Diff算法、Hooks闭包陷阱与响应式原理

进入第二轮,面试官明显更关注框架深度。我面的是React方向,所以上来先问了React的Diff算法

这个问题属于“人人都能说几句,但很少人能说透”的高频题。我当时的回答分了三层:

第一层是“同层比较”:React的Diff不会跨层级比较节点,而是逐层比较,一旦发现某个节点类型不同就直接替换整个子树,不做进一步比较。

第二层是“key的作用”:在同一个列表里,React通过key来判断节点是复用还是重新创建。高效的Diff需要尽可能减少节点移动,所以在用key的时候要选稳定且唯一的标识,不能选数组下标。因为如果列表头部插入了新元素,用index作为key会导致后面的所有元素都被认为改变了。

第三层是“Fiber的引入”:在React 16之后,Diff过程变得可中断了。Fiber把整个渲染过程拆成一个个小单元,每完成一个单元就让出主线程,这样就不会因为一次大渲染把页面卡死。这个设计其实是把同步的、不可中断的递归调用,改成了异步的、可中断的链表遍历。

面试官听完之后追问了一个场景:如果列表顺序发生了调换,用index作为key会有什么问题?这个我确实踩过坑,当时答得还算顺利。用index作为key,当列表顺序调换时,React并不知道这些节点只是换了位置,它会认为每个位置上的节点类型和内容都变了,于是销毁重建全部DOM。而在某些情况下,如果这个列表项是有内部状态的组件,比如输入框,位置的调换会导致输入框内容错乱,因为React复用了组件实例,但props和state没有正确对应起来。

Hooks闭包陷阱也是现在React面试的必问内容。面试官给了一个场景:

function Counter() { const [count, setCount] = useState(0); useEffect(() => { setInterval(() => { console.log(count); }, 1000); }, []); return <div>{count}</div>; }

这段代码的问题在于:useEffect传入的是空数组,所以effect只会在组件挂载时执行一次,闭包里捕获的count始终是初始值0。即使后面count变了,setInterval里的回调拿到的还是旧的闭包变量。

解决方式有几种:一是把count加入依赖数组,让effect重新执行,但这样会导致定时器被反复清除和重建;二是用ref来保存最新的count值;三是用函数式更新,比如setCount(prev => prev + 1)这种。

面试官继续追问:为什么依赖数组里的值变化了,闭包就能拿到新值?因为useEffect在依赖变化时会清理上一次的effect,然后重新创建新的effect。新的effect在创建时会捕获当前这次渲染的count值,所以闭包里的值就是新的。这个解释把“闭包陷阱”和“effect的清理机制”串起来了,我在回答的时候也明显感觉到面试官比较满意。

3.2 工程化考点:Webpack构建优化与Vite对比

快手这种体量的公司,前端代码库非常庞大,所以工程化能力一定是考察重点。面试官问的是:如果线上项目首屏加载很慢,你会怎么定位和优化。

这个问题很开放,我按“从外到内”的思路来答:

先看网络层面:资源体积是否过大,有没有开启gzip或者br压缩,CDN节点是否覆盖到位。接着看代码层面:首屏有没有懒加载,路由是不是按需拆分的,有没有加载了当前页面用不到的第三方库。再看缓存策略:静态资源的contenthash有没有配好,能不能做到长期缓存。最后看渲染层面:有没有SSR或者预渲染,白屏时间到底花了多久。

面试官听完之后,直接让我结合Webpack说具体配置方案。我提到了这几个点:

  • splitChunks把第三方库拆成单独chunk,这样业务代码更新时不会导致第三方库缓存失效
  • thread-loader或者terser-webpack-plugin开启多进程压缩,加快构建速度
  • cache-loader或者Webpack 5内置的持久化缓存,二次构建速度能快很多
  • import()语法做路由懒加载,配合React.lazy使用

然后他问了我一个对比问题:既然Webpack已经能完成这些事,为什么还要用Vite。我的理解是:开发阶段Vite利用浏览器原生ESM能力,启动速度比Webpack快非常多,因为Webpack启动时要先打包整个项目,而Vite只有在你请求某个模块时才实时编译那个模块。但在生产构建阶段,Vite还是通过Rollup打包,所以如果你遇到的是构建速度问题,Vite的优化效果主要体现在开发环境,而不是生产环境。

4. 场景题与综合能力考察:实际问题分析与方案设计

4.1 场景题:设计一个支持并发控制的请求队列

第三轮面试是leader面,开场没有直接问技术题,而是先聊了一下我在上一家公司的项目经历,然后抛出来一个场景题:如果前端需要向后端发送大量请求,比如1000个,但后端接口同时只能承受10个并发,你会怎么处理。

这种题在快手这种业务流量大的公司特别常见,因为他们的页面里确实会碰到大量数据请求的场景,比如推荐流的批量数据上报、批量查询用户状态等等。我当时的思路是设计一个带并发控制的请求队列,核心逻辑是:

  • 初始化一个任务队列,把所有请求都放进队列里
  • 设置一个最大并发数,比如10
  • 一开始先启动10个任务并行发送
  • 每完成一个任务,从队列里再取出一个任务补上来
  • 直到队列清空,所有请求完成

我用JavaScript伪代码写了一个版本,核心内容是这样的:

function limitRequest(urls, limit = 10) { const results = []; let current = 0; let finished = 0; const total = urls.length; return new Promise((resolve) => { function run() { while (current < total && results.length - finished < limit) { const index = current++; fetch(urls[index]) .then(res => res.json()) .then(data => { results[index] = data; }) .catch(err => { results[index] = err; }) .finally(() => { finished++; if (finished === total) { resolve(results); } else { run(); } }); } } run(); }); }

说实话这段代码我写的时候稍微有点紧张,results.length - finished < limit这个条件其实写得不严谨,因为results数组在并发过程中会有空洞,直接取length有误差。如果换一种更严谨的方式,应该维护一个当前正在执行的任务数计数器,每次发起请求时加一,请求完成时减一,然后用循环判断是否还能继续发起请求。不过面试官看过之后没有太纠结代码细节,而是继续问我:如果某个请求一直挂起不返回,队列会不会被卡死。

这个问题就是考察你有没有考虑到超时和失败重试。我补充了AbortController来做请求超时控制,比如超过10秒就主动中断,再从队列里拿出下一个请求执行。同时也想了一下失败重试的策略:不能无脑重试,因为后端如果已经处于过载状态,重试只会加重压力,所以一般要用退避策略,比如第一次失败后等1秒重试,第二次等2秒,最多重试3次。

4.2 手写题:实现一个带过期时间的localStorage封装

场景题结束后,面试官说“来一道手写题吧”,给了我一个需求:封装一个localStorage的读写方法,支持设置过期时间,过期后读取返回null。

这个题目本身不算难,但他的要求是“写一个生产可用的版本”,这就意味着需要考虑异常情况,比如存储空间满了、JSON解析失败、单条数据格式损坏等。

我当时写了一个大概的版本:

const storage = { set(key, value, expireSeconds) { const data = { value, expire: expireSeconds ? Date.now() + expireSeconds * 1000 : null }; try { localStorage.setItem(key, JSON.stringify(data)); } catch (e) { // 处理存储已满的情况 console.error('存储失败', e); } }, get(key) { const raw = localStorage.getItem(key); if (!raw) return null; try { const data = JSON.parse(raw); if (data.expire && Date.now() > data.expire) { localStorage.removeItem(key); return null; } return data.value; } catch (e) { // 数据损坏直接清除 localStorage.removeItem(key); return null; } }, remove(key) { localStorage.removeItem(key); } };

写完之后面试官问我,如果同一个key被设置了很多次,之前的过期时间怎么管理。其实这里有个细节:localStorage本身没有索引机制,同一个key被覆盖写,旧值就丢了,所以只需要存储最新的过期时间即可,不需要管理历史版本。

后面复盘时我想到,还可以考虑一个更完善的方向:做一个lru淘汰策略。当存储空间满了,优先清除那些即将过期的数据,或者是最久没被访问的数据。不过这个方向在面试时间限制内一般不会展开要求,只要能说出思路和实现方案就够了。

4.3 性能优化面:从首屏加载到交互流畅度

快手作为短视频和直播平台,对页面性能的要求比一般公司高很多。leader面里有一道题让我印象很深,他问我:一个资讯类的Web页面,首屏要在1秒内达到可交互状态,你会怎么做。

我按流程拆解了一下:

  • 资源加载阶段,做DNS预解析、建立HTTP/2多路复用、关键资源加preload、非关键资源加defer
  • 数据获取阶段,能用SSR的首屏数据就用SSR,SSR的数据注入到客户端可以减少一次请求往返;如果是纯CSR,考虑在HTML里内联首屏数据
  • 渲染阶段,避免首屏出现长任务,把耗时的计算拆成多个小任务,用requestIdleCallback处理不紧急的工作
  • 图片优化,首屏的图片用CDN的二倍图或者WebP,小图标用SVG内联或者iconfont

面试官继续问:如果你的页面在低端机上滚动时有明显的卡顿,你会怎么排查。这个问题我实战经验不算多,所以回答中规中矩,说了检查是否有频繁的layouts、长列表渲染、大量使用box-shadow这类会造成重绘的样式。后来面试官给了个比较实用的排查思路:先在Performance面板录制一段滚动操作,看看每个帧的耗时分布,是脚本执行占用太高还是渲染本身占用太高,然后再针对性地优化。低端机性能弱,很多在高端机上跑得很顺畅的动画,在低端机上会卡到没法用,所以做性能优化时最好在低端机上测试一遍。

5. 算法与手写题专项:题目难度与解题策略

5.1 面试中的算法题:从简单到中等的梯度设计

快手技术面的算法题难度整体处于LeetCode简单到中等之间,不会出现特别极端的hard题,但会结合实际业务场景来做变形。我遇到的两道题都跟业务场景有绑定关系。

第一道题出现在一面,是字符串反转的变体:给定一个字符串,按单词反转,比如"hello world foo"变成"foo world hello",要求不使用split和reverse等内置方法。这道题其实考察的是双指针思路。我当时用双指针从尾部往头部扫描,遇到空格就切一个单词,拼接到结果里。需要注意的是题目的边界条件:开头和结尾可能有多个空格,单词之间也可能有多个空格,如果事先搞不清楚这些要求在面试中会反复调整代码。

第二道题出现在二面,是个数组去重加排序的变形:给定一个整数数组,返回出现频率最高的前K个元素。这道题最常见的解法是用哈希表统计频率,然后用桶排序或者小顶堆取出频率前K的元素。我当时用了小顶堆的方式,时间复杂度是O(n log k),空间复杂度是O(n)。面试官接着问如果n很大,比如上亿,内存装不下怎么办。这时候就需要考虑外部排序、分治或者近似算法,比如用Count-Min Sketch这种概率性数据结构来统计频率。

5.2 手写题:实现Promise.all与防抖函数

手写Promise.all是现在前端面试的标配,几乎每个公司都会问。快手这轮也没有例外,但面试官加了一个小限制条件:不能使用async/await,只能基于原生的Promise实现。

写Promise.all时最关键的点是要处理两种情况:一是全部成功时按传入顺序返回结果数组,二是有任意一个失败时直接reject这个错误。我当时用了一个计数器来判断是否全部完成,结果数组用索引赋值的方式保证顺序一致。还处理了传入空数组的情况,直接resolve一个空数组。

防抖函数也是高频手写题。这个函数的核心是:在事件被连续触发时,只有最后一次触发后等待指定时间才会执行。我在实现时用闭包保存定时器ID,每次触发时先清除定时器再重新设置。面试官还问到了immediate参数,也就是第一次触发时立刻执行,后面的连续触发不执行,直到停止后才重新reset。这里要特别小心this的指向问题,如果用普通函数写法,内部要用context来保存this,用箭头函数则会丢失this指向。

6. 面试总结:常见陷阱与个人经验体悟

6.1 面试过程中暴露的薄弱点与改进方向

整个面试下来,我最大的感受是:快手面试官问问题的逻辑性很强,不是零散地考知识点,而是会围绕一个核心主题层层深入。比如他在问React Diff的时候,是从“diff是什么”问到“为什么需要key”,再到“Fiber为什么能中断”,最后到这个设计对开发者有什么影响。这个追问链条,其实是在模拟一个真实的工作场景:当你在开发中遇到性能问题时,需要一层层往下定位,而不是只会说“组件加载慢加个memo”。

我自己的薄弱点在CSS布局和浏览器渲染细节上。一面的时候面试官问了一个flex布局中flex: 1的含义,我没有完整答出它其实是flex-grow: 1; flex-shrink: 1; flex-basis: 0%的缩写形式,只说了会占满剩余空间。这个点虽然在业务上够用,但在面试里会显得理解不够深入。后来我复盘时专门把CSS的常用布局、BFC、层叠上下文这些内容重新过了一遍。

6.2 给准备面试的朋友的几点实操建议

结合这次面快手的经验,我想给正在准备前端面试的同学几个建议,尤其是对标这个方向的朋友:

第一是不要只背面试题,要理解题目背后的原理。前端面试题在网上随处可见,但如果你只是把答案背下来,面试官换个角度追问就露馅了。最好的状态是能用自己的话把一个知识点顺下来,并且能举例说明。

第二是刷题要有针对性。算法题不用刷太多,但要让自己的思路保持熟练。重点复习哈希表、双指针、二叉树遍历、动态规划、数组和字符串操作这几种类型,概率最大。

第三是面试前把简历里的项目彻底复盘一遍。想想你做的每个功能,技术方案是怎么选的,有没有其他方案,为什么没用,上线后效果如何。这些内容是面试官追问的主要来源,也是你展现自己技术深度的最好机会。

第四是提前准备反问环节的问题。面试官最后一般都会问“你有什么想问我的”,这时候别问那种明显能从官网找到答案的问题,也别直接说“没有”。可以问团队的主要技术栈是什么,当前团队在做的核心项目是什么,或者对新人来说最重要的能力是什么。这既能显示出你对这个机会是认真的,也能帮你了解自己进去之后要面对的是什么。

6.3 我自己踩过的坑与一些小经验

最后再分享几个我亲身踩过的坑,这些细节如果没人提醒,真的很容易吃亏。

第一个坑是面试的时候不要为了展示自己懂得多而主动把话题带偏。比如面试官在问闭包,你觉得这个话题很简单,就开始扯垃圾回收机制、V8引擎的优化策略,结果面试官顺势追问,发现你的垃圾回收理解也就停留在表面,反而暴露了短板。面试是展示自己最强的地方,而不是把所有会的都倒出来,更不是在自己最弱的地方硬展开。

第二个坑是手写题的时候一定要先想清楚再动笔,不要边写边改。我二面的时候写Promise.all,一开始没想清楚空数组的处理逻辑,写了十几行发现有问题又回头改,整个过程看起来就很不专业。后来面试官跟我说,其实他不在乎你的代码写得多么完美,更在意的是你遇到问题时的处理方式。是慌慌张张乱改,还是先想清楚再动手,这两者的差距是很明显的。

第三个坑跟情绪有关,不要被上一轮的失利影响下一轮的心态。快手的面试节奏比较快,可能你刚觉得一面某个问题没答好,第二天就开始二面了。但面试官之间是独立评价的,一面没答好的问题,二面可能会从另一个角度重新验证你的能力。所以每一轮都把它当成全新的开始,不要背负上一轮的心理包袱。

我在实际面试过程中发现一个规律:真正让你通过面试的,往往不是你答对了多少道题,而是你面对不熟悉的问题时,能不能沉住气,按照“分析问题 -> 拆解问题 -> 提出思路”的方式来处理。如果你能做到这一点,哪怕最后方案不完美,面试官也会觉得你是个有潜力的人。这一点在快手的面试中表现得特别明显,也是我认为整个面试过程中最值得借鉴的地方。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 12:52:40

8款口碑AI论文写作工具横向实测,本硕博避坑选型手册

前言&#xff1a;AI 写论文乱象频发&#xff0c;实测 8 款工具理清适配边界 每到毕业季&#xff0c;本科生、硕博生都会集中寻找 AI 论文辅助工具&#xff0c;市面各类写作软件层出不穷&#xff0c;但普遍存在几类硬伤&#xff1a;虚假参考文献、无法匹配本校格式、不支持公式代…

作者头像 李华
网站建设 2026/8/30 12:50:42

点云处理与4D几何分析实战:库选型与工程落地指南

做点云处理和 4D 几何分析这几年&#xff0c;许多读者的第一反应是“装个 PCL 不就行了”。可实际动手时会发现&#xff0c;事情没有那么简单&#xff1a;PCL 编译耗时、依赖链复杂&#xff0c;Open3D 虽然开箱即用但深度处理能力弱&#xff0c;处理时序点云时又需要自己维护帧…

作者头像 李华
网站建设 2026/8/30 12:50:26

C++日志库spdlog实战:集成、异步日志与滚动文件配置详解

C 项目里打日志&#xff0c;是一件看起来简单、铺开就乱的事情。早期方案无非是 printf 加一个文件重定向&#xff0c;或者自己封装一个 fprintf 轮子&#xff0c;再往后可能换到 log4cxx、glog 这些老牌库。但要么跨平台麻烦&#xff0c;要么编译依赖重&#xff0c;要么接…

作者头像 李华
网站建设 2026/8/30 12:47:42

5年Java后端社招拿贝壳Offer:知识体系与面试复盘全记录

开头 去年这个时候&#xff0c;我还在上一家公司写业务代码&#xff0c;每天在工位上被需求追着跑&#xff0c;基本没怎么想过跳槽的事。直到连续做了三个大版本迭代&#xff0c;发现自己虽然把 Spring Boot 和 MyBatis 用得滚瓜烂熟&#xff0c;但真要让我讲讲“订单状态机怎么…

作者头像 李华
网站建设 2026/8/30 12:47:36

RAK3172 Class-C激活成功但IRQ全零?FUOTA多播收不到分片的排查指南

遇到这个问题的兄弟&#xff0c;我太懂你现在的心情了。Class-C session在ChirpStack那边明确显示激活成功&#xff0c;设备也入了多播组&#xff0c;但就是收不到固件分片&#xff0c;radio一点反应都没有&#xff0c;连个RxTimeout都不给。这问题我前前后后折腾了两周&#x…

作者头像 李华