
前端移动开发【免费下载链接】Mars腾讯移动 Web 前端知识库项目地址https://gitcode.com/gh_mirrors/mar/Mars点击查看免费下载导读本文以腾讯移动 Web 前端知识库 Mars 仓库的 performance 性能专题 为主体系统讲解移动端高性能 Web 开发的三条主线——动画技术选型、GPU 加速与动画防闪、DOM layout 读写批处理与 CSS 动画属性性能并结合仓库内的源码级注释、问题清单与触摸事件 Demo 做交叉印证。读完本文你将掌握从「CSS3 动画为什么更快」到「哪些操作会触发 layout」的完整性能优化链路并能在自己的移动端项目里直接落地translate3d加速、backface-visibility防闪、layout-queue 批量读写等实战方案。一、性能专题的定位从 PC 到 Mobile 的三维考量Mars 的 性能目录 将「高性能 Mobile Web 开发」组织为四大方向构成了移动端性能优化的完整知识地图高性能 CSS3 动画对应 high-performance-css3-animation.mdCSS 动画属性性能对应 css-property-animation-performance.md高性能滚动核心思路是通过 passive event listener 降低滚动事件对渲染主线程的阻塞让滚动过程中的事件监听默认不再调用preventDefault()从而避免浏览器为等待可能的拦截而被迫同步执行减少滚动卡顿离线 Web App 以及消息同步与推送基于 Service Worker 实现页面资源离线缓存、后台消息同步与推送能力这是移动网络环境下降低首屏等待与流量损耗的关键基建。与 PC 时代「只关注流畅度」不同移动端需要额外关注两个维度流量用户基于运营商基站网络访问页面体积、资源加载策略直接关系到用户的流量账单功耗设备电池有限动画渲染策略不当会导致耗电激增例如无节制的 3D 变形会持续占用 GPU 资源流畅度这是 PC 与移动端共同的核心指标主要体现在前端动画中。二、动画技术选型为什么移动端优先 CSS3 动画在现有前端动画体系中通常有两种模式JS 动画通过 JS 动态改写样式实现动画。在 PC 端兼容低版本浏览器时是一种推荐方案但每一帧的样式改写都要经过 JS 引擎 → 样式计算 → 布局 → 绘制的完整链路性能受制于脚本执行效率CSS3 动画由浏览器原生实现渲染管线完全在浏览器内部调度无需 JS 逐帧介入。移动端终端性能与 PC 存在显著差距因此 Mars 给出的结论是在移动端选择性能更优的浏览器原生实现方案——CSS3 动画。不过 CSS3 动画在移动多终端场景下依然会面对比 PC 更多的性能问题主要体现在动画的卡顿与闪烁需要在下面几个层面逐一治理。三、充分利用硬件能力用 3D 变形开启 GPU 加速开启 GPU 加速的最常用手段是给动画元素施加一个恒定的 3D 变形。Mars 文档给出的标准写法见 high-performance-css3-animation.md-webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -ms-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);translate3d(0, 0, 0)本身没有位移效果但其作用在于将元素提升到独立的合成层composite layer让后续的transform动画由 GPU 合成而非 CPU 逐帧绘制动画流畅度显著提升。权衡提示文档原意3D 变形会消耗更多的内存与功耗。Mars 明确提醒「应确实有性能问题时才去使用它兼在权衡」——不要为了炫技而对所有元素无差别施加 3D 变形否则会在多终端上造成内存占用与电量损耗的副作用。四、消除动画闪烁backface-visibility 与 perspective 的 Hack动画过程中尤其是动画开始瞬间常出现闪白/闪烁Mars 给出了如下 Hack 组合见 high-performance-css3-animation.md-webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden; -webkit-perspective: 1000; -moz-perspective: 1000; -ms-perspective: 1000; perspective: 1000;原理上backface-visibility: hidden隐藏元素的背面perspective建立透视投影二者组合能够稳定元素的 3D 渲染上下文减少合成层在动画首帧被重新创建时产生的闪烁。这条 Hack 在仓库的 问题清单 issues/README.md 中也有完全一致的印证——「消除 transition 闪屏」条目给出了另一种等价组合-webkit-transform-style: preserve-3d; /* 设置内嵌的元素在 3D 空间如何呈现保留 3D */ -webkit-backface-visibility: hidden; /* 设置进行转换的元素的背面在面对用户时是否可见隐藏 */并且明确指出「动画过程中的动画闪白可以通过 backface-visibility 隐藏」。可见这一组属性是 Mars 团队在真实移动端项目中反复验证过的「防闪标配」。五、实战对比translate3d 为什么比 left 更流畅Mars 用一个双 ball 的对比示例直观说明差异见 high-performance-css3-animation.md/* 方案一使用 transform 完成右移 500px */ #ball-1 { transition: -webkit-transform .5s ease; -webkit-transform: translate3d(0, 0, 0); } #ball-1.slidein { -webkit-transform: translate3d(500px, 0, 0); } /* 方案二使用 left 完成右移 500px */ #ball-2 { transition: left .5s ease; left: 0; } #ball-2.slidein { left: 500px; }两者视觉结果相同但渲染开销天差地别修改left会改变元素的几何位置浏览器必须重新计算布局layout/reflow再重绘repaint最后合成composite——链路长、开销大修改transform不改变文档流中的几何信息元素已在合成层中浏览器只需做一次合成compositeGPU 直接完成位移动画。这一结论在仓库 issues/README.md 中同样被列为移动端问题清单的一项「动画效果中使用 translate 比使用定位性能高」。可以说这是整个性能专题反复强调的第一铁律。六、规避渲染代价高的属性box-shadow、gradients 与文档流6.1 远离 box-shadow 与 gradientsbox-shadow与gradients往往都是页面的性能杀手尤其是在一个元素同时使用两者时阴影与渐变的绘制成本会叠加直接影响首屏与动画帧率。Mars 的应对策略非常干脆「拥抱扁平化设计吧」见 high-performance-css3-animation.md——减少高成本绘制属性是移动端视觉设计的隐性能约束。6.2 让动画元素脱离文档流减少重排动画元素若处于文档流中其位置变化会连锁引发周围元素的重排。让动画元素脱离文档流可显著缩小 layout 的影响范围见 high-performance-css3-animation.mdposition: fixed; position: absolute;七、DOM layout 性能优化layout-queue 与读写批处理这是 Mars 性能文档中篇幅最长、也最具实战价值的一节。先看两段能力上完全等价、执行顺序不同的代码见 high-performance-css3-animation.md// 触发两次 layout var newWidth aDiv.offsetWidth 10; // Read aDiv.style.width newWidth px; // Write var newHeight aDiv.offsetHeight 10; // Read aDiv.style.height newHeight px; // Write // 只触发一次 layout var newWidth aDiv.offsetWidth 10; // Read var newHeight aDiv.offsetHeight 10; // Read aDiv.style.width newWidth px; // Write aDiv.style.height newHeight px; // Write规律总结把连续的「读取」聚合在一起再把连续的「写入」聚合在一起相比「读写交替」可少触发一次 layout。其底层原因是浏览器的优化策略所有可触发 layout 的操作都会被暂时放入layout-queue布局队列中等到「必须更新」的时机浏览器一次性计算整个队列中所有操作影响的结果从而只进行一次 layout。如果读写交替每次读操作都会因为「必须读取最新值」而强制 flush 队列layout 次数随之增加。这条规律直接对应到移动端动画场景在 touchmove 或 rAF 回调中操作 DOM 几何属性时务必「先集中读取、后集中写入」否则每帧都会付出多次 layout 的代价。八、源码级追问哪些操作会触发 layout「等到必须更新的时候」——这个必要条件是什么Mars 从开源浏览器内核实现入手给出了答案。以开源 Webkit/Blink 为例layout 的更新主要通过Document::updateLayout与Document::updateLayoutIgnorePendingStylesheets两个方法完成源码如下见 high-performance-css3-animation.mdvoid Document::updateLayout() { ASSERT(isMainThread()); FrameView* frameView view(); if (frameView frameView-isInLayout()) { ASSERT_NOT_REACHED(); return; } if (Element* oe ownerElement()) oe-document()-updateLayout(); updateStyleIfNeeded(); StackStats::LayoutCheckPoint layoutCheckPoint; if (frameView renderer() (frameView-layoutPending() || renderer()-needsLayout())) frameView-layout(); if (m_focusedNode !m_didPostCheckFocusedNodeTask) { postTask(CheckFocusedNodeTask::create()); m_didPostCheckFocusedNodeTask true; } } void Document::updateLayoutIgnorePendingStylesheets() { bool oldIgnore m_ignorePendingStylesheets; if (!haveStylesheetsLoaded()) { m_ignorePendingStylesheets true; HTMLElement* bodyElement body(); if (bodyElement !bodyElement-renderer() m_pendingSheetLayout NoLayoutWithPendingSheets) { m_pendingSheetLayout DidLayoutWithPendingSheets; styleResolverChanged(RecalcStyleImmediately); } else if (m_hasNodesWithPlaceholderStyle) recalcStyle(Force); } updateLayout(); m_ignorePendingStylesheets oldIgnore; }从实现可知updateLayoutIgnorePendingStylesheets是对updateLayout的扩展且在现有 layout 更新模式中大部分场景都是调用前者。基于内核源码Mars 归纳出了以下会触发 layout 的操作清单即被强制 flush layout-queue 的读取点ElementclientHeight、clientLeft、clientTop、clientWidth、focus()、getBoundingClientRect()、getClientRects()、innerText、offsetHeight、offsetLeft、offsetParent、offsetTop、offsetWidth、outerText、scrollByLines()、scrollByPages()、scrollHeight、scrollIntoView()、scrollIntoViewIfNeeded()、scrollLeft、scrollTop、scrollWidthFrame, HTMLImageElementheight、widthRangegetBoundingClientRect()、getClientRects()SVGLocatablecomputeCTM()、getBBox()SVGTextContentgetCharNumAtPosition()、getComputedTextLength()、getEndPositionOfChar()、getExtentOfChar()、getNumberOfChars()、getRotationOfChar()、getStartPositionOfChar()、getSubStringLength()、selectSubString()SVGUseinstanceRootwindowgetComputedStyle()、scrollBy()、scrollTo()、scrollX、scrollY、webkitConvertPointFromNodeToPage()、webkitConvertPointFromPageToNode()。实战含义非常直接在「写入样式」与「读取这些几何/计算属性」之间尽量保持「先读后写」的批处理顺序同时动画循环内避免调用getComputedStyle()、offsetWidth等会强制同步 layout 的接口。九、CSS 动画属性性能relayout / repaint / recomposite 三阶段如果说上一节回答的是「什么会触发 layout」那么 CSS动画属性性能 回答的则是「动画属性本身走哪条渲染管线」。核心结论有三条CSS 动画属性会触发整个页面的重排relayout、重绘repaint、重组recompositePaint绘制通常是其中最花费性能的环节应尽可能避免使用触发 paint 的 CSS 动画属性这正是推荐用-webkit-transform: translateX(3em)代替left: 3em的原因left会额外触发 layout 与 paint而transform只触发整个页面的 composite。文档给出了一个完整可运行的对比实验见 css-property-animation-performance.md。基础样式如下div { -webkit-animation-duration: 5s; -webkit-animation-name: move; -webkit-animation-iteration-count: infinite; -webkit-animation-direction: alternate; width: 200px; height: 200px; margin: 100px; background-color: #808080; position: absolute; }用left驱动的 keyframes原文档配图显示页面被持续触发重绘以红色边框标记-webkit-keyframes move{ from { left: 100px; } to { left: 200px; } }用-webkit-transform驱动的 keyframes原文档配图显示页面只发生重组以橙色边框标记-webkit-keyframes move{ from { -webkit-transform: translateX(100px); } to { -webkit-transform: translateX(200px); } }两种写法在 5 秒往返无限循环的动画中表现截然不同前者每秒多出大量 layout paint 工作后者仅需合成层位移。这一实验结论可以进一步推广为一份决策表动画涉及width/height/left/top/margin等布局属性时走最重的管线涉及transform/opacity时走最轻的管线。原文档中附有「CSS 属性在 CSS 动画中行为表」即在动画中逐属性标注其触发的渲染阶段选属性前先查表是移动端动画设计的基本功。十、仓库内的实践印证问题清单与触摸事件 Demo性能专题的结论并非孤例Mars 仓库的其它目录提供了大量一线印证。10.1 问题清单中的性能条目在 issues/README.mdiOS 与 Android 平台问题列表中与性能直接相关的条目包括translate 优于定位issues/README.md「动画效果中使用 translate 比使用定位性能高」与专题铁律完全一致滚动事件的高频回调处理issues/README.md绑定touchmove时若回调中处理内容较多FPS 会下降把代码包进setTimeout(..., 0)延迟到下一轮事件循环执行程序反而会变快// 处理量大的写法FPS 易下降 $(div).on(touchmove, function(){ //.….code }); // 推荐写法将重活推迟到 setTimeout 中执行 $(div).on(touchmove, function(){ setTimeout(function(){ //.….code },0); });滚动位置读取issues/README.mdwindow.scrollY/window.scrollX属于前文清单中的 layout 触发点读取时机应与写入批处理tap 事件的构成issues/README.md移动端 click 存在普遍约 300ms 延迟开发者大多使用由touchstarttouchmove判断 touchend封装而成的 tap 事件替代——这与高性能滚动专题中「减少事件处理对渲染的阻塞」是同一主题的两面。10.2 触摸事件 Demo验证合成层加速的载体仓库 demos/touchevents.html 提供了一个可直接在移动浏览器打开的触摸事件实验页其中#touch元素在样式中就应用了-webkit-transform: translate3d(0,0,0)来开启合成层同时通过ontouchstart、ontouchmove、ontouchend、ontouchcancel、onclick记录各事件的时间戳并输出 touchstart 到 click 的差值。这个 Demo 一方面验证了触摸事件序列另一方面也演示了「动画/交互元素常驻合成层」的实际写法可与本专题的 GPU 加速方案配合使用。十一、专题知识地图的另外两块拼图回到 performance/README.md 的知识地图除了两篇深度文档还有两个方向值得纳入移动端性能优化全景高性能滚动核心思路是采用 passive event listener——让滚动/触摸事件监听默认不调用preventDefault()避免浏览器为了等待拦截而必须同步执行监听器从而减少滚动过程中对渲染主线程的阻塞结合上文touchmove的setTimeout技巧可显著改善长列表滚动帧率离线 Web App 与消息同步、推送基于 Service Worker 实现资源离线缓存、后台消息同步与推送把移动网络下的首屏加载与流量损耗问题前置解决。这一方向与前文「流量、功耗、流畅度」的三维考量直接呼应。十二、落地检查清单综合全文给出可直接照做的移动端高性能开发清单动画优先使用transform含translate3d开启 GPU 加速避免用left/top/width/height/margin驱动动画动画元素统一position: absolute/fixed脱离文档流缩小重排影响面动画开始闪白时叠加backface-visibility: hidden、perspective或transform-style: preserve-3d慎用box-shadow与gradients避免同元素叠加使用倾向扁平化设计JS 操作 DOM 几何时「先集中读、后集中写」利用 layout-queue 把每帧 layout 次数压到最低动画循环中避免读取offsetWidth/offsetHeight/getComputedStyle/scrollTop等强制同步 layout 的属性touchmove/scroll高频回调中把重活推迟到setTimeout(..., 0)3D 变形有内存与功耗成本仅在确有性能问题时使用权衡取舍。相关阅读仓库内性能专题目录高性能 CSS3 动画GPU 加速、防闪与 layout 优化CSS 动画属性性能relayout/repaint/recomposite 决策iOS 与 Android 平台问题列表:active、transition 闪屏、300ms 延迟等触摸事件 Demo合成层加速的交互实验页Mars 项目主 README知识库全景入口赞分享前端移动开发【免费下载链接】Mars腾讯移动 Web 前端知识库项目地址https://gitcode.com/gh_mirrors/mar/Mars点击查看免费下载上一篇AMD Ryzen调试工具终极指南免费开源SMUDebugTool一步到位释放处理器全部潜力下一篇AMD Ryzen处理器SMU调试工具从零开始免费开源读懂芯片隐藏参数的完整实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考