前端js代码
打开任何一个网页,前端右键“检查”,代码切到Console面板,前端输入 很多人以为JS就是前端写写点击事件、做做轮播图。代码这看法停在2010年。前端现在一个React组件的 拿一个搜索框的防抖逻辑举例。用户每敲一个字符就发请求,服务器扛不住。常规写法: 这段代码能跑,但有问题。每次input都创建新闭包,timer变量挂在模块作用域里,多个搜索框会互相干扰。更麻烦的是,如果用户输入“abc”后立刻删成“ab”,300ms内只发一次请求——这符合预期。但如果用户在280ms时又敲了一个字符呢?计时器重置,前一次请求永远不会发出。逻辑上没错,体验上却可能让用户觉得“卡了一下”。 实际项目里我会写成: 区别在哪? JS是单线程的,但浏览器不是。渲染、网络、定时器各走各的线程。你写 我见过一个列表拖拽排序的组件,每移动一像素就读一次 ES Module的 我的习惯:工具函数单独放 前端JS代码写到后面,拼的不是语法糖用得多溜,是对执行时机和内存边界的判断。一行document.querySelectorAll('*').length,代码回车。前端一个普通电商首页,代码这个数字通常在1500到3000之间。前端这些节点不是代码静态写死的——它们几乎全部由JavaScript在运行时创建、修改、前端销毁。代码前端JS代码的前端本质,就是代码一套在浏览器里实时操控DOM树的操作指令集。useEffect里可能同时跑着WebSocket心跳、IntersectionObserver监听和requestAnimationFrame动画循环。代码量不大,但每一行都在和浏览器的渲染管线抢时间。一段真实的代码长什么样
let timer = null; input.addEventListener('input', (e) => { clearTimeout(timer); timer = setTimeout(() => { fetch(`/api/search?q=${ e.target.value}`); }, 300); });function debounce(fn, delay) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); timer = null; }, delay); }; } const search = debounce((q) => fetch(`/api/search?q=${ q}`), 300); input.addEventListener('input', (e) => search(e.target.value));timer = null这行。它让闭包里的计时器句柄在触发后释放,避免内存里堆积一堆已完成的setTimeout引用。小细节,但一个日活十万的页面,这种泄漏累积起来能吃掉几十MB内存。浏览器不等你写完代码
element.style.height = '100px',浏览器不会立刻重绘。它把这次修改记下来,等当前宏任务结束,再统一走样式计算、布局、绘制。如果你在同一个任务里先读offsetHeight再写style.height,浏览器被迫提前布局——这就是强制同步布局,性能杀手。getBoundingClientRect(),然后改transform。在低端安卓机上直接掉到12帧。改成用requestAnimationFrame批量读写,帧率回到55以上。代码改动不到十行。模块化不是目的,隔离才是
import看着干净,但循环依赖能让你调试到怀疑人生。A模块导入B的foo,B模块导入A的bar,执行顺序取决于入口先碰见谁。CommonJS时代用require还能靠缓存部分绕过,ESM的静态解析直接把问题暴露在加载阶段。utils/,不依赖任何业务模块;业务模块之间通信走事件总线或状态管理,不直接互相import。这不是教条,是被undefined is not a function报错逼出来的。setTimeout(fn, 0)放在循环里和放在循环外,结果可能差出一个数量级的性能。这些东西文档不写,得自己踩。
- 最近发表
- 随机阅读
- 搜索
-
- 友情链接
-