前端 RUM:真实用户性能监测
「网站加载快吗?」这个问题,实验室(Lighthouse、压测)回答不了真实答案——用户可能在弱网、地铁、安卓千元机在后台还挂着几十个 App。性能是分用户、分设备、分网络、分地区的,只有从真实用户浏览器里采集到的数据,才反映真实体验。
这就是 RUM(Real User Monitoring,真实用户监控)——从真实用户的页面里采集性能与错误数据,用「真实发生」代替「假设场景」。
「网站加载快吗?」这个问题,实验室(Lighthouse、压测)回答不了真实答案——用户可能在弱网、地铁、安卓千元机在后台还挂着几十个 App。性能是分用户、分设备、分网络、分地区的,只有从真实用户浏览器里采集到的数据,才反映真实体验。
这就是 RUM(Real User Monitoring,真实用户监控)——从真实用户的页面里采集性能与错误数据,用「真实发生」代替「假设场景」。
前端面试考算法,问的不是"你数学好不好",而是两件事:你有没有数据结构意识(看到问题能想到该用栈、哈希表还是树),你有没有复杂度意识(知道这段代码是 O(n) 还是 O(n²))。因为前端日常处理的 DOM、事件、状态、缓存,底层全是数据结构——只是平时被框架藏起来了。
这篇文章按"数据结构 → 算法 → 手写题"三层把最常考的内容串一遍:每个结构讲清特性、给一两个高频题、再落到前端场景。不贪多,但每一块都能直接拿去应对面试。
"首屏优化"是前端工程化里最常被问、也最难答好的话题——因为它不是一个技巧,而是一整套从指标定义 → 网络传输 → 资源加载 → 构建产物 → 渲染执行全链路的方法论。本文把这条链路完整走一遍:先讲清楚"快"到底指什么、慢在哪,再按层给出可落地的方案,最后用淘宝、美团、字节等大厂的真实实践收尾。
首屏慢的代价是实打实的商业数字。美团技术团队在《前端黑科技:美团网页首帧优化实践》里引用过一组业界数据:
所以"秒开"不是一个技术执念,而是直接关系收入指标(转化率、跳出率、留存)。这也是为什么大厂会把首屏性能做成性能预算(Performance Budget)——把它当"依赖"一样锁进 CI,超了就不让发版。
一万条订单直接 map 渲染,浏览器会创建一万多个 DOM 节点——滚动掉帧、内存暴涨,甚至直接卡死。虚拟列表(Virtual List / Windowed Rendering)就是为了解决这个问题:不管数据有多少,始终只渲染视口附近的那几十个节点。但"上虚拟列表"只是第一步,真正难的是选型——不同场景(表格、feed、聊天、分组)该用哪个方案,不同数据量级该不该用、用什么级别的虚拟化,以及各种边界情况下的 fallback。
本文把主流方案和大厂实践完整调研一遍:原理 → 方案全景 → 场景选型 → 量级判断 → fallback。
https://dart.dev/interop/js-interop/past-js-interop
@js.JSExport()
class _MyAppState extends State<MyApp> {
final _streamController = StreamController<void>.broadcast();
DemoScreen _currentDemoScreen = DemoScreen.counter;
int _counterScreenCount = 0;
@override
void initState() {
super.initState();
final export = js.createJSInteropWrapper(this);
js.globalContext['_appState'] = export;
js.globalContext.callMethod('_stateSet'.toJS); // 调用js方法,将Dart实例化
}
在搜索框里敲「react」:r、re、rea、reac、react——如果每次敲键都发一次请求,一次输入就白白浪费 4 次网络请求,而且前 4 次的结果你根本来不及看,它们已经在回来的路上了。再往下想:滚动页面时 onScroll 一秒钟触发几十次,resize 拖拽时更是每帧一次。
这类问题有一个共同的名字:事件风暴——短时间内触发太多次,而真正需要执行的只有少数几次。
柯里化(Currying)是函数式编程里最常被提起、也最常被误解的概念之一。面试喜欢问,但很多人只背得住一句「把多参函数变成单参函数的链式调用」,说不清它从哪来、为什么存在、以及它到底划算不划算。
这篇文章把三件事讲透:它是谁发明的、它解决什么问题、它的代价是什么。
手写系列的意义不是「记住答案」,而是把 API 背后那句被藏起来的话重新说出来:「改变 this」本质是什么?「异步」到底是怎么串起来的? 顺着 apply → call → bind → Promise 这条线写下来,你会发现它们是一脉相承的——前面三个解决的是一件事(函数调用时的 this 和参数),最后一个把「回调」正式升级成了「一等公民」。