目录
对于长列表出现的白屏、卡顿、界面跳动等问题,小程序提供了新 scroll-view 来解决这一系列问题。
怎么做到的?
大家肯定好奇为什么新 scroll-view 可以解决这个头疼的问题呢?
我们来对比一下新旧 scroll-view 有什么区别就可以知道答案啦~
旧 scroll-view
- 逻辑层与渲染层的通信需要通过 JSBridge 进行转换,需要一定的时间开销
- 渲染采用异步分块光栅化,当渲染赶不上滚动的速度,来不及渲染则会出现白屏
- 渲染大量节点内存占用高,需要开发者自行优化只渲染在屏节点,开发成本高
新 scroll-view
- 逻辑层与渲染层的通信无需通过 JSBridge 进行转换,减少了大量通信时间开销
- 渲染采用同步光栅化,滚动与渲染在同一线程,不会出现白屏
- 针对长列表进行优化,只渲染在屏节点,内存占用低,减少了一些渲染耗时,且开发接入成本低
除此之外,新 scroll-view 后续将提供 type="custom" 支持 sticky 吸顶效果、网格布局、瀑布流布局等能力便于开发者接入使用~
环境准备
Skyline 具体支持版本如下:
- 微信安卓客户端 8.0.33 或以上版本(对应基础库为 2.30.4 或以上版本)
- 微信 iOS 客户端 8.0.34 或以上版本(对应基础库为 2.31.1 或以上版本)
- 开发者工具 Stable 1.06.2307260 或以上版本(建议使用 Nightly 最新版)
使用开发者工具调试
开发者工具提供了对齐移动端的 Skyline 渲染引擎,支持 WXML 调试、 WXSS 样式错误提示、新增的特性等
按以下步骤切换到 Skyline 模式:
- 在 app.json 或 page.json 中配上
renderer: skyline
,并按下一节添加好配置项,或者按开发者工具的提示逐个加上配置项 - 确保右上角 > 详情 > 本地设置里的
开启 Skyline 渲染调试
选项被勾选上 - 使用
worklet
动画特性时,确保右上角 > 详情 > 本地设置里的编译 worklet 代码
选项被勾选上 (代码包体积会少量增加) - 调试基础库切到 3.0.0 或以上版本
开始迁移
迁移到 Skyline,无需大动干弋,我们保持了上层框架的语法、接口基本不变,只需要做局部的调整,主要是加强了 WXSS 样式、内置组件及部分特性的约束,基本流程如下:
- Skyline 依赖 按需注入 特性,需在 app.json 配上
"lazyCodeLoading": "requiredComponents"
- 在全局或页面配置中声明为 Skyline 渲染,即 app.json 或 page.json 配上
"renderer": "skyline"
- 在全局或页面配置中声明使用新版 glass-easel 组件框架,即
{ "componentFramework": "glass-easel" }
- Skyline 不支持页面全局滚动,需在页面配置加上
"disableScroll": true
(使之与 WebView 保持兼容),在需要滚动的区域使用 scroll-view 实现 - Skyline 不支持原生导航栏,需在页面配置加上
"navigationStyle": "custom"
(使之与 WebView 保持兼容),并自行实现自定义导航栏 - 在全局配置中声明默认
block
布局,即app.json
配上"rendererOptions": { "skyline": { "defaultDisplayBlock": true } }
,详见开启默认 Block 布局 - 在全局配置中声明默认
content-box
布局,即app.json
配上"rendererOptions": { "skyline": { "defaultContentBox": true } }
,详见开启默认 ContentBox 盒模型 - 组件适配,参考 Skyline 基础组件支持与差异
- WXSS 适配,参考 Skyline WXSS 样式支持与差异
页面jsom
{
"usingComponents": {
"screenss": "../components/screenssnew/screenssnew",
"navbar": "/components/navbar/navbar"
},
"renderer": "skyline",
"componentFramework": "glass-easel",
"disableScroll": true,
"navigationStyle": "custom"
}
app.json
"lazyCodeLoading": "requiredComponents",
"rendererOptions": {
"skyline": {
"defaultDisplayBlock": true
}
}
在真机上预览效果
由于 Skyline 默认接入 We 分析的 AB 实验,未配置的情况下,页面渲染仍为 WebView 引擎,可通过以下方式正确切到 Skyline 渲染
- 配置 We 分析 AB 实验,加上白名单,操作步骤详见下节
- 关闭 We 分析 AB 实验,默认启用 Skyline 渲染,配置方式详见此处第 2 点
- 通过快捷切换入口,强切到 Skyline 渲染,操作步骤详见下节
配置 We 分析 AB 实验
迁移完 Skyline 之后,为了让开发者能够针对 Skyline 逐步灰度放量,并且与 WebView 对比性能表现,我们在 We 分析 提供了 AB 实验机制。
因此,需要在 We 分析 配置之后,小程序用户才可以命中 Skyline 渲染,需要注意的是,小程序开发者也会受 AB 实验影响。操作步骤如下:
首先,进入 We 分析,在 AB 实验 > 实验看板,点击“新建实验”
接着,实验类型选择 小程序基础库实验
,然后按需选择实验层级并分配流量,如果是小范围调试,可分配 0% 流量,并在 Skyline 渲染
的实验分组里填上测试微信号
最后,创建实验即可生效。后续经 AB 实验验证稳定后,需在 We 分析上先关闭实验再选择 Skyline 全量
快捷切换入口
考虑到本地调试时,配置 AB 实验会稍微繁琐一些,并且也会需要对比 WebView 的表现,我们提供了快捷切换渲染引擎的入口。
该入口只对开发版/体验版小程序生效,入口为:小程序菜单 > 开发调试 > Switch Render,会出现三个选项,说明如下:
- Auto :跟随 AB 实验,即对齐小程序正式用户的表现
- WebView :强制切为 WebView 渲染。强切后,开发版、体验版、正式版均为 WebView 渲染,重启微信后恢复为 Auto
- Skyline :若当前页面已迁移到 Skyline,则强制切为 Skyline 渲染。强切后,开发版、体验版、正式版均为 Skyline 渲染,重启微信后恢复为 Auto
如何识别当前页面是否使用 Skyline
-
通过客户端菜单:
打开开发版/体验版小程序,点击菜单即可查看当前页面是否使用 Skyline 渲染。
-
通过 vConsole 按钮的右上角的红底文案识别
-
vConsole 的路由日志
路由日志中会包含页面路由的目标页面、路由类型和目标页面的渲染后端。
一个可能的日志形如:
On app route: pages/index/index (navigateTo), renderer: skyline
,代表通过navigateTo
跳转到了pages/index/index
,渲染后端为 skyline -
通过接口判断
页面和自定义组件示例上有属性
renderer
,可以用于判断当前组件的实际渲染后端,如:Page({ onLoad() { console.log(this.renderer) } })
滚动容器及其应用场景
流畅的滚动对于提升用户体验至关重要。为了达到原生级别的滚动效果和降低开发成本,Skyline
扩展了旧的 ScrollView
组件,同时针对部分场景,新增了一些滚动容器。诸多的新能力有时会让开发者选择困难,下面对其典型应用场景进行介绍。
长列表
WebView
下的 ScrollView
组件,在快速滑动时容易出现白屏和渲染卡顿。对于长列表的优化,通常离不开按需渲染,即理想状态下仅渲染在屏节点,超出可视区域的节点及时进行回收。
Skyline
下内置了按需渲染的能力,但对于写法有一定要求,列表项需作为直接子节点,形如下面的结构。
<scroll-view type="list" scroll-y>
<view> a </view>
<view> b </view>
<view> c </view>
</scroll-view>
视口大小
ScrollView
的视口大小 = ScrollView
的高度 + 指定的上下缓冲区 CacheExtent
。
指定 CacheExtent
可优化滚动体验和加载速度,但会提高内存占用且影响首屏速度,可按需启用。
节点进入视口区域时开始渲染,离开视口时回收资源。资源回收的粒度为其直接子节点。当 ScrollView
仅有单个子节点时,为保证其渲染,所有的资源都无法回收,需全量布局和绘制所有内容,性能较差,因此才需要摊平子节点。
出于业务需要 ScrollView
的内容常被封装成组件,导致无法作为直接子节点。这里有一个小技巧,可将封装的组件设为虚拟的,开启 virtualHost: true
。真正渲染时,virtual-comp
节点并不存在,列表项仍是摊平的。
<scroll-view type="list" scroll-y>
<virtual-comp>
<view> a </view>
<view> b </view>
<view> c </view>
</virtual-comp>
</scroll-view>
完全的按需渲染
小程序内的按需渲染分为两个阶段。
- 列表项按需创建其
DOM 节点
; - 列表项按需绘制上屏;
ScrollView
的 list
模式实现了按需绘制,但列表项的 DOM 节点
仍是全量创建的。随着节点数增多,会带来内存压力。
为此框架提供了新的 builder
模式,可使用 list-builder
、grid-builder
等组件实现 DOM 节点
的按需创建。
上图是 builder
模式在开发者工具中 wxml
的渲染结果,仅在屏列表项会被真正创建节点,离屏后列表项会被回收,滚动时始终几个子节点。
示例代码片段
ScrollView 的三种模式
ScrollView
提供了列表 list
、自定义 custom
和 嵌套 nested
三种渲染模式,实际开发时如何选择呢?
列表模式
默认模式,实现了内置的按需渲染能力,但没有进行节点回收。当列表项比较简单,不会带来明显的内存压力时使用。
非长列表时,即使不摊平列表项也不会有明显性能问题,可使用单子节点写法。
<!-- 单子节点写法,全量绘制 -->
<scroll-view type="list" scroll-y>
<view>
<view> a </view>
<view> b </view>
<view> c </view>
</view>
</scroll-view>
<!-- 列表项作为直接子节点,有按需绘制优化 -->
<scroll-view type="list" scroll-y>
<view> a </view>
<view> b </view>
<view> c </view>
</scroll-view>
<!-- 列表项作为 list-view 直接子节点,有按需绘制优化,同上 -->
<scroll-view type="custom" scroll-y>
<list-view>
<view> a </view>
<view> b </view>
<view> c </view>
</list-view>
</scroll-view>
自定义模式
列表滚动时常会和特殊布局能力结合使用,如滚动到顶部时自动吸顶 sticky
效果,或瀑布流布局。
Skyline
内置了这部分能力,可直接使用 sticky-header
和 grid-view
组件实现。
list-view
组件的效果跟列表模式是等价的,如果不需要这些特殊布局能力,可任意选择写法。
需要注意的是自定义模式下,ScrollView
直接子节点本身并没有按需绘制优化,按需绘制的能力是 list-view
组件实现的,custom
模式组合了这些能力。
<scroll-view type="custom" scroll-y>
<sticky-section>
<sticky-header>
<view> h </view>
</sticky-header>
<!-- 非 list-view 子节点,无按需绘制优化 -->
<view> 1</view>
<view> 2 </view>
<!-- 列表项作为 list-view 直接子节点,有按需绘制优化 -->
<list-view>
<view> a </view>
<view> b </view>
<view> c </view>
</list-view>
</sticky-section>
</scroll-view>
<scroll-view type="custom" scroll-y>
<sticky-section>
<sticky-header>
<view> h </view>
</sticky-header>
<!-- 列表项作为 grid-view 直接子节点,有按需绘制优化 -->
<<grid-view type="masonry">
<view> a </view>
<view> b </view>
<view> c </view>
</<grid-view>
</sticky-section>
</scroll-view>
示例代码片段
嵌套模式
这主要是针对一类嵌套滚动场景。如下图所示,SwiperItem
内也有纵向滚动的 ScrollView
组件,当在内部 ScrollView
上滑动时,会与外层 ScrollView
产生手势冲突,导致外层的页面始终无法滚动。
- 纵轴+横轴+纵轴的嵌套组合
- 同一方向的滚动容器存在手势冲突
- 可使用手势协商解决,但过程较为烦琐
为使得内外的滚动衔接更为流畅,框架新增了 <nested-scroll-header
和 nested-scroll-body
组件结合嵌套模式使用,省去了开发者解决手势的麻烦。
<scroll-view type="nested" scroll-y>
<nested-scroll-header>
<view></view>
</nested-scroll-header>
<nested-scroll-body>
<swiper>
<swiper-item>
<scroll-view
type="list"
associative-container="nested-scroll-view"
>
<view>a</view>
<view>b</view>
</scroll-view>
</swiper-item>
<swiper-item>...</swiper-item>
<swiper-item>...</swiper-item>
</swiper>
</nested-scroll-body>
</scroll-view>
示例代码片段
可拖拽容器
页面内的半屏可拖拽容器是很常见的一种交互,用户可通过滚动扩大列表范围。以往开发者可通过手势协商的能力来实现,但较为繁琐。
框架提供了 draggable-sheet
组件,封装了这一能力,包括
- 隐藏滚动条
- 滚动回弹效果
- 滚动到指定位置(
snap
到关键点) - 滚动帧回调(实现滚动驱动动画)
<draggable-sheet
class="sheet"
initial-child-size="0.5"
min-child-size="0.2"
max-child-size="0.8"
snap="{{true}}"
snap-sizes="{{[0.4, 0.6]}}"
worklet:onsizeupdate="onSizeUpdate"
>
<scroll-view
scroll-y="{{true}}"
type="list"
associative-container="draggable-sheet"
bounces="{{true}}"
/>
</draggable-sheet>