快应用性能优化之减少js执行时间
快应用是一种新的应用形态,用户在使用国内厂商的手机时无需下载安装,即能流畅的体验应用内容。
本文主要指导开发者对javascript执行时间进行优化,让用户获得更好的体验。
脚本执行时间
脚本执行时间是指 JS 脚本在一次同步执行中消耗的时间,比如生命周期回调、事件处理函数的同步执行时间。执行脚本的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要确认并优化脚本的逻辑
快应用生命周期
快应用在启动过程中有几个主要的生命周期,用户在其中执行的脚本会直接影响启动时间。关于生命周期的描述可以查看官方文档。
启动过程中开发者主要需要关注的生命周期:
- APP 的生命周期:onCreate
- 页面的生命周期:onInit、onReady
- 自定义组件的生命周期:onInit、onReady
各个生命周期在快应用启动过程中会依次执行,我们可以通过火焰图去找到耗时比较多的生命周期,并对其进行优化。关于火焰图可以查看MDN文档
下面主要有三块内容:
- 两种生成火焰图的方法
- 根据生成的火焰图优化生命周期中的脚本
- 其他优化手段
生成火焰图
和传统 H5 类似,在快应用中也可以使用火焰图观察各个方法执行的时间。
在快应用中有两种方式查看火焰图,开发者选择其中一种即可。
使用 inspector 接口生成火焰图
inspector 接口的说明可以查看官方文档
该节主要包括的内容
- 生成并保存 chrome devtools performance 面板所需的 profile.cpuprofile
- PC 获取 profile.cpuprofile 文件
- 通过 profile.cpuprofile 文件查看火焰图
生成 profile.cpuprofile 文件
- 在项目的 app.ux 中添加代码,这段代码主要做了两件事:记录四秒的执行信息,将信息存到手机内存的文件里
import file from '@system.file'
import prompt from '@system.prompt'
// 注意需要在manifest.json的features中声明system.file和system.prompt
const session = new Session()
session.connect()
session.post('Profiler.enable', () => {
session.post('Profiler.start', () => {
// 记录四秒的执行信息
setTimeout(() => {
session.post('Profiler.stop', (error, { profile }) => {
session.disconnect()
// 将记录的信息存起来
file.writeText({
uri: 'internal://mass/profile.cpuprofile',
text: JSON.stringify(profile),
success: function () {
prompt.showToast({ message: 'handling success' })
},
fail: function (data, code) {
prompt.showToast({ message: `handling fail, code = ${code}` })
},
})
})
}, 4000)
})
})
将保存的文件提取到电脑里
获取保存的文件前,需要确定运行的快应用引擎包名(appName)和快应用的包名(rpkName),本例中用到的 OPPO 快应用引擎包名为 com.nearme.instant.platform,快应用包名为 com.example.demo
- 如果电脑安装了 adb,可以执行下面命令直接获取刚才保存到手机中的 profile.cpuprofile 文件。先在D盘根目录中新建一个文件夹,命名为test,然后执行下面的命令。该命令会将 profile.cpuprofile 文件从手机中拉取到电脑里D盘的test文件夹下(开发者可以自定义拉取到电脑中的位置,记得修改命令即可)。
adb pull /storage/emulated/0/Android/data/com.nearme.instant.platform/files/com.example.demo/profile.cpuprofile D:\test
- 若没有 adb 也可以手动将手机内部存储/Android/data/com.nearme.instant.platform/files/com.example.demo/profile.cpuprofile 目录下的 profile.cpuprofile 文件拷贝到电脑上
使用 chrome 的 performance 面板打开 profile.cpuprofile 文件即可
通过 H5 预览查看火焰图
快应用除了在真机上进行原生渲染外,也支持在浏览器中查看 H5 模拟渲染的结果。虽然渲染的方式不同,但是各个 js 耗时相对的执行耗时不会相差太多,所以可以直接使用 chrome 的 performance 面板查看火焰图。
- 在命令行中执行以下命令
hap server
- 执行后可以在命令行中看到生成 HTTP 服务器的地址,将其复制到 chrome 浏览器中打开,并点击打开页面中的打开 web 预览
- 若操作无误的话即可在浏览器中看到 web 预览的快应用页面,如下图。此后调试和调试优化 H5 无异。
点击页面中的 reload 按钮,等待 6s 左右停止录制即可
获得的火焰图
通过火焰图优化耗时脚本
以 inspect 接口生成的火焰图为例,可以看到快应用启动有两个主要流程,createApplication 和 cratePage,都能在火焰图中找到。
开发者不需要关注其中陌生的函数执行,主要关注在火焰图底部是否有各个生命周期的宽度很宽,如果有的话则需要进行优化。
需要关注的生命周期:
- createApplication 中的 onCreate
- createPage 中的 onInit、onReady
例如在 demo 生成的火焰图中,放大 createApplication 部分可以明显看到 onCreate 占用 CPU 时间很多
可以直接根据火焰图继续看调用栈或者直接看源码,看是哪个函数导致函数执行时间太长
export default {
showMenu: $utils.showMenu,
createShortcut: $utils.createShortcut,
onCreate() {
APP_STATISTICS.app_sta_init(this)
},
}
demo 中 onCreate 执行时间过长是因为调用了一个统计工具的初始化方法,开发者在开发过程中可能是别的原因导致的。通常的做法是将初始化时不需要或非必要的方法,延后到 onShow 等生命周期中。
本例中直接将该代码注释掉,模拟优化后的效果
onCreate() {
// APP_STATISTICS.app_sta_init(this)
},
重新获取火焰图对比,可以明显看到 createApp 过程中 js 的执行时间从 29ms 降到了 10ms
其他优化手段
console.log 语句
在快应用运行时,控制台大量打印日志语可能会拖累 JavaScript 线程。注意有些第三方调试库也可能包含控制台打印语句,所以在发布应用前请务必仔细检查,删除不必要的 console 语句,或者直接将 manifest.json 中将 debug 选项改为 false。该场景与react native类似。
优化 createPage 的时间
若 createPage 耗时过长且没有明显耗时的业务代码,调用栈中有较多的 appendChild 方法,则可能时节点数过多导致的,可以参考 dom 优化文档进行优化。
简化 ViewModel 的数据
在 ViewModel 的定义中,属性 public、protected、private 主要承担数据驱动的数据定义与改造功能,会对赋值的数据中每个属性进行递归式的定义。因此,属性个数的定义越少越好,尤其是数组类型数据,建议过滤不需要用到的对象属
合理使用后代选择器
框架支持 CSS 中的后代选择、支持 less 预编译,方便开发者开发,提升代码可维护性。然而,过多的使用后代选择器,也会在节点匹配上带来性能损耗,尤其是当一个节点满足多个选择时。