尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

小程序skyline迁移实战:从滚动卡顿到渲染引擎兼容避坑

小程序skyline迁移实战:从滚动卡顿到渲染引擎兼容避坑 最近在做一个小程序的项目重构核心诉求是首屏性能和列表滚动的体验优化一开始就盯上了微信官方的skyline渲染引擎。折腾了两周踩了一堆文档里没有明说的坑很多问题翻遍社区和官方issue才找到方向。今天把这些记录整理出来给准备迁skyline或者正在被skyline折磨的同学一些参考至少能少走几步弯路。先介绍下背景项目是一个偏工具型的小程序有长列表、有横向tab切换、有大量图片和视频卡片旧版用webview渲染滚动掉帧和页面切换卡顿比较明显。换到skyline后最大的体感是首屏渲染确实快列表滑动顺畅了一个档次但代价是很多webview时代理所当然的组件行为和样式生效方式在skyline里全都变了。整个过程我按问题类别梳理成四块分别对应滚动方案、组件兼容、渲染逻辑差异、以及开发者工具与真机的表现偏差。每个坑我都会讲清楚现象、原因和最终的处理方式方便你对照自己的项目排查。1. skyline模式整体设计与改造思路拆解1.1 为什么选择skyline而不是继续死磕webview先聊点背景。webview渲染下的滚动列表实际是页面级的scroll-view在撑手指滑动时渲染层和逻辑层之间隔着大量的数据通信。尤其当列表cell里面有图片、视频、动态样式setData频率一高主线程直接被拖垮表现出来就是快速滑动时白屏、掉帧、甚至卡死。skyline的思路等于换了一条路渲染层使用自绘引擎布局和渲染不再依赖webview同时滚动不再走系统原生的scroll事件而是通过worklet机制直接跑在UI线程。这样做的好处非常多——列表的滚动不再受逻辑层阻塞、节点渲染粒度更细、动画和手势响应更灵敏。对长列表和复杂交互场景来说这是webview很难追上的体验差距。所以如果你是做工具型小程序、内容型App、对滚动流畅度和首屏速度有硬指标skyline值得投入。但如果你只是做一个简单的静态展示页、交互极少那不建议迁收益低且改造成本不减。1.2 迁移前的兼容性盘点和改造路线skyline不是webview的一比一替代。表面上小程序的wxml、wxss、js代码文件能直接运行在skyline但运行效果和组件能力是不同的。我建议动手前做好三个层面的盘点和准备。第一层是组件盘点。检查项目里用了哪些内置组件、自定义组件、扩展库组件。skyline对内置组件的支持相对完善但第三方ui库特别是基于webview丰富dom结构实现的库大概率兼容性堪忧。我项目里用到的一个日期选择器组件就是典型问题在webview里一切正常切到skyline后弹层错位、点击穿透全来了。第二层是样式能力的盘点。skyline的css支持大部分常规属性但部分属性和选择器是受限的比如通配符选择器不可用、某些定位属性和transform组合表现异常。如果你的样式大量依赖这些特性改造量会很大。第三层是接口和行为差异的盘点。这部分最隐蔽很多方法是同名的但调用时机、返回值、副作用都不一样。比如createSelectorQuery在skyline里的执行时机就比webview严格必须在页面onReady甚至更晚调用否则拿到的节点信息全是null。我的建议方案是分区灰度而不是整包切换。先把首页、列表页这类滚动密集的页面切到skyline其余页面保持webview不变待兼容性稳定后再逐步收拢。微信支持页面维度配置skylineapp.json里配置renderer: skyline并在rendererOptions里指定skyline页面这也是官方推荐的渐进式迁移策略。2. 滚动方案与下拉刷新的各种坑2.1 onScrollToLower不触发的非常规场景先说最典型的一个问题。在skyline模式下scroll-view的onScrollToLower触底加载更多有时候完全不触发或者要滚动到非常靠近底部才触发跟webview下的表现很不一样。原因出在scroll-view默认的滚动容器高度计算上。webview下滚动区域的高度是内容撑开的页面的滚动是文档流滚动所以onScrollToLower可以在合适的距离触发。skyline里scroll-view是独立渲染层如果你没有给scroll-view设置一个明确的高度它的高度计算方式会非常诡异——有时候是零有时候是整个页面高度有时候又是内容高度。一旦高度不对触底判断就会出错。处理办法给scroll-view一个明确的高度约束保证它是一个定高滚动容器。我最终的做法是.scroll-container { height: 100vh; /* 或者 */ height: calc(100vh - 44px); }而且不要在wxml里写死style尽量用class控制方便在小程序后台动态调整。一定不能用height: auto或者不设高度指望它自适应skyline下大概率翻车。另外补充一个点onScrollToLower的触发阈值lower-threshold默认是50px这在webview下够用但在skyline下如果列表底部有图片懒加载、骨架屏占位这类动态高度内容最好把阈值调大一点比如100px否则底部内容加载完后高度变化触底判断会整个失效。2.2 scroll-view滚动事件的频率和触发时机skyline模式下scroll-view的bindscroll事件频率非常高远超webview。这本身不是问题但很多人拿webview的思路去处理流量比如在scroll事件里持续调用setData更新某个节点的样式那性能会直接崩掉。我实测在iPhone 13iOS 16.x上快速滚动时bindscroll每帧能触发多次。这时如果你在回调里做了哪怕很小的setData都会造成严重卡顿因为每次setData都意味着逻辑层和渲染层的通信而skyline的高帧率滚动会让通信量爆炸。处理办法不要在scroll回调里做任何同步的setData。如果需要更新滚动状态用data直接驱动样式或者转化思路把依赖滚动位置变化的效果改成scroll-view的enhanced属性能力CSS变量或者用worklet去实现。skyline的滚动联动尽量交给scroll-view自身提供的动画能力和属性而不是手动监听再触发更新。我当时做一个滚动一定距离后显示回到顶部按钮的效果webview下用bindscroll监听、判断scrollTop、setData控制显隐一切正常。skyline下这个方案直接卡成PPT后来改用scroll-view的scroll-top配合worklet方案流畅度立刻恢复。2.3 下拉刷新与自定义导航栏的黑色遮罩问题skyline模式下页面下拉刷新的实现也存在差异。我们用enablePullDownRefresh做下拉在webview下没问题但skyline下如果同时使用自定义导航栏navigationStyle为custom会出现下拉时顶部出现一片黑底或白底的遮罩非常丑。这个问题本质上是自定义导航栏与页面背景色设置不一致导致的。skyline下拉刷新时系统会在顶部预留一个回弹区域如果你的自定义导航栏背景色和页面背景色不一致回弹区域就会暴露底色看起来像遮罩。处理办法把自定义导航栏的背景色和页面背景色设置成完全相同。如果导航栏有渐变或者特殊样式那下拉刷新的表现就需要额外处理。直接设置navigationStyle的backgroundColor和页面的backgroundColor保持一致即可注意这个是全局配置所以最好在页面级的json里单独设置。{ navigationStyle: custom, backgroundColor: #ffffff, backgroundColorTop: #ffffff, backgroundColorBottom: #ffffff }如果这样设置后仍然存在建议放弃enablePullDownRefresh改用scroll-view的refresher-enabled属性做自定义下拉刷新组件可控性更强也不会被顶部遮罩问题困扰。3. 组件兼容与扩展库的各种坑3.1 自定义组件和第三方库的兼容性边界这个坑是重灾区。skyline模式下自定义组件的渲染机制与webview并不完全一致尤其是涉及弹层、浮层、绝对定位的组件。我遇到的最典型问题一个基于movable-area实现的气泡弹层在webview下完全正常skyline下点击后气泡定位错误且无法自动收起。原因是movable-area在skyline里的定位坐标系和webview不同尤其在嵌套scroll-view、自定义导航栏、以及其他transform组件的环境下坐标计算会错乱。另外几乎所有的toast、弹出面板、picker类第三方组件在skyline下都可能出现层级混乱。skyline的组件层级和webview的DOM树逻辑不同webview下可以通过z-index控制skyline里某些组件的层级是固定的z-index不生效。我最后的解决方案是所有需要绝对定位的弹层、下拉面板自己实现或者直接用官方popup/half-screen-dialog组件。官方组件在skyline下做了适配层级和坐标都对至少省心。3.2 slot插槽与组件数据同步的时序问题自定义组件里的slot使用在skyline下也存在坑。具体现象是父组件通过slot传入子组件的内容首次渲染时能正常显示但只要父组件的数据一变化slot内容就会闪一下或者直接消失过一会儿又出现像是渲染丢了。这个问题的根因是skyline模式对组件数据和槽内容的更新机制与webview不同。webview里slot内容跟着组件的data走组件数据一改变slot区域同步更新skyline里的slot更新存在时机差需要额外的机制来保证同步。处理办法不要过度依赖slot去实现动态内容的展示。如果slot内容只是静态展示问题不大如果slot内容里包含动态数据、条件渲染建议改成组件的properties传参方式把内容作为数据传入组件内部由组件自行渲染。我在项目里遇到的就是这种问题。一个封装的列表卡片组件内部用slot承接图片、标题和价格信息webview里表现完美skyline下数据更新后slot区域经常会闪白。最后把组件接口改成properties传入数据对象内部用模板渲染问题彻底解决。3.3 官方基础库扩展组件的使用限制skyline对官方组件库的支持也不是无条件的。比如button、input、textarea、map、video这些组件在skyline下各有各的限制。textarea在skyline下有个明显问题它是原生组件层级最高自定义弹层无法覆盖它。webview时代可以通过cover-view解决skyline下这种原生组件的层级逻辑并没有完全解决。如果你的页面有输入内容时弹出遮罩层这类交互textarea会直接穿透遮罩层展示。处理方式很朴素弹层显示时隐藏textarea或者用input替代如果只是单行输入。video在skyline下也有兼容问题。默认的video组件在列表滚动时如果不设置enable-progress-gesture和show-center-play-btn等属性表现会非常怪异。而且video的层级、封面图、播放按钮在快速滚动时都可能渲染异常。如果项目里视频卡片多建议统一封装一个视频组件针对skyline做属性调优别直接用裸video标签。4. 渲染表现与基础能力差异问题4.1 高度计算和百分比的怪异行为skyline模式下一些常规的CSS百分比高度表现得跟webview完全不一样。典型的坑是用height: 100%去继承父级的百分比高度在webview下能正常工作在skyline下经常计算不对尤其当父级高度本身也是动态的、或者父级是flex布局时。我做tab切换时两个tab页都需要占满全屏高度webview下设置height: 100%即可skyline下tab页的实际高度会撑到内容高度而不是父容器高度导致底部留白或内容溢出。处理办法取消百分比高度依赖改用flex: 1布局。skyline对flex布局的支持比百分比高度稳定得多。如果必须用百分比请把父级容器也设置成明确的height: 100vh或者固定像素值不要再套多层百分比。4.2 图片加载和懒加载的兼容图片是长列表的另一个坑。webview下image组件的lazy-load属性用起来很顺手但skyline下lazy-load的生效条件比较严格必须是scroll-view内部的image且设置明确的宽高否则懒加载会失效所有图片在初始渲染时全部请求首屏直接变成加载地狱。我当时首屏有30多张图片卡片全部走lazy-loadwebview下首屏只请求前6张skyline下一次性请求全部白屏等待时间直接翻了近3倍。处理办法在skyline下image必须设置明确的width和height哪怕是通过css class设置也可以就是不能没有预设尺寸。preview模式下如果图片宽高不确定可以用固定比例容器比如宽高比4:3套一层image用modeaspectFill填充。这样lazy-load就能正常工作。4.3 页面整体滚动与局部滚动的取舍skyline下页面的滚动行为和局部滚动行为差异极大。默认情况下skyline页面的根节点是不滚动的如果你设置了page的高度为100vh整个页面就不会滚动所有滚动必须依赖scroll-view。这个特性在webview下是没有的。webview下页面天然可以滚动你只需要把内容放进去就行。skyline下如果你不显式声明一个带滚动的容器页面内容超出屏幕后就是不可滑动的交互直接死掉。所以skyline页面的布局思路要彻底转变不要指望页面级滚动所有的长内容展示、列表、tab切换都必须包在scroll-view里。这也是skyline常见页面不能滚动问题的根本原因。处理办法改造页面结构为一个全屏的scroll-view内部再分层。如果页面内有多个tab每个tab页再嵌套scroll-view。这个结构一旦固定下来后续的滚动性能、懒加载、触底刷新就都有了解法。5. 开发者工具与真机的偏差以及上线前的验证5.1 开发者工具模拟与真机渲染不一致这是最容易让人误判的一个坑。skyline模式在微信开发者工具里的表现和真机存在明显差异尤其在iOS和安卓上的差异更大。我遇到两次典型的工具里好好的真机就崩的情况第一次是下拉刷新的回弹动画工具里正常真机上回弹距离过大导致视觉穿帮第二次是scroll-view嵌套使用scroll-into-view定位工具里能跳转真机上位置偏移。一个更隐蔽的问题是开发者工具对worklet动画的支持不够完整部分动画在工具里无法执行但真机上正常这会影响调试效率。处理办法优先用真机调试作为验证基准开发者工具只在非交互场景下用来快速查看布局和数据结构。开发阶段每天至少做一次真机调试不要等到最后才上真机。5.2 真机性能排查与卡顿定位skyline模式如果出现卡顿排查思路和webview完全不同。不要一上来就怀疑JS执行问题绝大多数卡顿来自渲染层。我常用的排查链路先在开发者工具里打开渲染性能面板看渲染帧率然后真机开启性能监控面板观察CPU占用和渲染线程的负载。如果渲染线程跑满节点数量过大或样式频繁变动是首因。skyline下还有一个隐藏坑节点数量过多会直接导致渲染性能指数级下降。webview下1000个节点可能没事skyline下300个节点就可能开始掉帧。所以页面结构要尽量扁平化能用wx:for循环的不要手写一堆重复节点能用一个view解决的不要嵌套三层。5.3 基础库版本与skyline特性的匹配最后一个坑必须单独提skyline的很多能力严重依赖基础库版本而且旧版本基础库下某些特性会静默降级或失效不会报错。比如safe-area的处理、component2的启用、worklet动画的API支持这些在不同基础库版本下表现差异极大。我遇到过component2没开导致组件事件绑定失效的问题排查了很久才发现是基础库版本太低。处理办法项目一定要锁定最低基础库版本避免用户使用旧版本库访问页面时出现静默错误。在app.json里配置{ lazyCodeLoading: requiredComponents, rendererOptions: { skyline: { defaultDisplayBlock: true, disableABTest: true, sdkVersionBegin: 3.0.0, sdkVersionEnd: 15.255.255 } } }同时在开发者工具里设置项目的最低基础库版本为3.0.0以上太低的版本直接提示升级而不是让页面带病运行。这个配置虽然会增加一部分老用户的升级成本但对比页面白屏和功能错乱的代价还是值得的。说到底skyline是一个收益非常明显的渲染引擎但它的代价是开发者需要重新理解小程序的渲染逻辑。迁移之前务必做好页面清单、组件盘点、样式结构梳理再动手逐页接入。如果你正在迁移或者已经在迁移路上希望这些坑能帮你省下几个加班的夜晚。
返回列表