知识储备
小程序的架构原理中知道小程序是双线程模式,分为渲染层和逻辑层,那么渲染层用的是webview模式。随着版本的更新升级,随后出现了skyline模式,对webview模式的升级版。
WebView 渲染模式下,一个小程序页面对应一个 WebView 实例,并且每个页面会重复注入一些公共资源。而 Skyline 只有 AppService 线程,且多个 Skyline 页面会运行在同一个渲染引擎实例下,因此页面占用内存能够降低很多,还能做到更细粒度的页面间资源共享(如全局样式、公共代码、缓存资源等)。 —–摘自官网
Skyline 创建了一条渲染线程来负责 Layout, Composite 和 Paint 等渲染任务,并在 AppService 中划出一个独立的上下文,来运行之前 WebView 承担的 JS 逻辑、DOM 树创建等逻辑。这种新的架构相比原有的 WebView 架构,有以下特点:
- 界面更不容易被逻辑阻塞,进一步减少卡顿
- 无需为每个页面新建一个 JS 引擎实例(WebView),减少了内存、时间开销
- 框架可以在页面之间共享更多的资源,进一步减少运行时内存、时间开销
- 框架的代码之间无需再通过 JSBridge 进行数据交换,减少了大量通信时间开销。
背景
小程序页面具有分享功能,希望能够增加截图长截图功能,将信息详情页面的所有内容,通过分享功能,转发给通讯录中的朋友查看以做推广。
问题
小程序的原理解析中我们知道小程序的两大系统区分便是ios和安卓的区别,长截图功能其实在安卓手机上,系统具有自带长截图功能,然后ios没有,并且小程序无法直接操作dom元素。
项目技术背景
- taro V3.5.4 ===》 升级到taro.3.6+ 才支持到skyline模式单页面开启
- React V 17+
- Webpack V 3.6+
方案预研
方案A:
了解到有一个插件html2Canvas可以实现长截图功能,但是由于无法小程序无法直接操作dom元素,不能直接在小程序中使用。可以通过将需要长截图的页面,写成一个h5页面,然后通过webview页面引入,然后在h5中利用插件做长截图功能。
结果:不可行❌,因为小程序需要长截图页面已经在小程序中存在,再花费时间去写一个h5页面,存在太多浪费的资源和精力,淘汰此方案,如果你是要在h5的项目操作,你可以尝试。
方案B
通过canvas实现将整个页面的内容通过原稿画出来,然后通过canvas.createImage()。画出来之后通过 wx.canvasToTempFilePath 接口,可以将 canvas 上的内容生成图片临时文件,然后进行图片保存功能。
const image = canvas.createImage()
// 图片加载完成回调
image.onload = () => {
// 将图片绘制到 canvas 上
ctx.drawImage(image, 0, 0)
}
image.src = 'xxx图片url'
结果:小程序功能页面太多元素,canvas画图太耗费人力和时间,不符合当前需求开发时间内,淘汰❌,如果是单纯的海报,可以值得一试。
方案C
小程序官方文档中提出了一个组件叫做snapshot截图组件,不过需要注意的是,该组件的有一个必要的条件就是必须开启skyline渲染模式才能使用。
经过调研开启skyline模式必须基础库版本>= 3.0.1,微信版本必须8.0.31以上。根据微信提供的数据,大部分的用户的微信版本还是支持skyline模式。
结果:采取snapshot组件实现小程序长截图功能。改动相对较小,同时满足现在开发的周期。
缺点:存在兼容性问题,需要同时兼容两种模式:webview以及skyline模式的样式。
解决思路:
-
在小程序单页面配置下开启
skyline渲染模式// page.config.ts { renderer: 'skyline', // 开启渲染模式为skyline componentFramework: 'glass-easel', // 相关框架分为exparser、glass-easel rendererOptions: { skyline: { defaultDisplayBlock: true, disableABTest: true, } }关于
componentFramework的中的glass-easel 介绍。Skyline 渲染引擎的相关配置项
属性 类型 默认值 说明 defaultDisplayBlock boolean false 开启默认 Block 布局 defaultContentBox boolean false 开启默认 ContentBox 盒模型 disableABTest boolean false 关闭 Skyline AB 实验 -
在全局配置上开启按需注入,同时设置skyline下节点默认布局模式
{ "lazyCodeLoading": "requiredComponents", // 开启按需注入 "rendererOptions": { "skyline": { "defaultDisplayBlock": true // skyline 下节点默认为 flex 布局,可以在此切换为默认 block 布局 } } } -
使用
snapshot包裹需要放入长图元素。然后通过snapshot.takeSnapshot()对组件树进行截屏。将截屏获取的内容生成的临时文件,可以让用户选择是否下载到本地,即可完成长截图保存功能。// demo 举例,基于taro框架 import { View, Snapshot } from '@tarojs/components'; ... // 省略部分代码 // 将截图内容用snapshot元素包裹 <Snapshot id='snapshot'> <View>{list}</View> </Snapshot> // 省略部分代码 // less文件中,需要对截图组件元素设置离屏渲染。 // 离屏渲染导出,可将 snapshot 组件移动到屏幕外或设置 width: 100%; position: absolute; transform: scale(0) 即可,但不能设置为 display: none 或 visibility: hidden ... // 触发生成长截图事件 Taro.createSelectorQuery() .select('#snapshot') .node() .exec((res) => { res?.[0]?.node?.takeSnapshot?.({ type: 'arraybuffer', format: 'png', success: (res) => { const userPath = Taro.env.USER_DATA_PATH; const randomName = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36); const path = `${userPath}/${randomName}.png`; // 生成临时图片 // ..... }, fail(error) { console.log('fail:', error); }, }); });
开发者调试
-
微信开发者工具选择基础版本库>=3.0.1以上
-
开启
skyline渲染调试,否则在真机上无法调试skyline模式下页面的样式,不会生效。
QA
开启skyline模式之后,页面的兼容性会出现很多问题,可以查阅文档常见的兼容性问题
- display的默认属性为inline,暂不支持block
- iconfont图标无法展示,最新版本已经支持伪元素
- ……
总结:开启skyline模式出现的问题其实很多,毕竟内部的渲染模式大有不同,所以开发的时候需要调研清楚是否改动比较大,或者难以实现的地方。