概览:本文介绍了启动时长优化的四大方向,减少耗时操作、资源提前加载、操作延时触发、并发能力使用。并以Web页面为例,通过预解析、预连接、预下载、预渲染四大策略,实现启动时长优化。
什么是启动时长?
启动时长分为两类,我们把应用被拉起至应用显示页面的执行时间,称之为应用启动时长 。把页面被拉起至显示页面的执行时间,称之为页面启动时长。
启动时长优化的方向
1、耗时操作较少
减少debug日志、冗余空回调
减少Trace打点
避免耗时接口
减少数据库API调用次数,冗余的操作
2、资源提前加载
网络请求优化:同步任务(在同步任务里发起网络请求) > 微任务 > 宏任务
Web组件加载性能优化:网址 > 解析 > 连接 > 下载 > 渲染
媒体资源预下载:在前一个页面下载图片存入AppStorage,下个页面可以直接使用, @StorageLink('imageUrl')
3、操作延时触发
延迟加载:import lazy {A} from "./A" lazy的模块解析在冷启动依旧会触发遍历,导入的变量A被使用到时,触发模块的源码执行。
动态加载:let A = await import("./A) 创建异步任务开销,执行到动态加载时,触发依赖模块的模块解析+源码执行。
4、并发能力应用
使用多线程能力:taskpool(关注耗时推荐taskpool)、worker(关注内存情况推荐使用worker,相对taskpool重量级一些)
使用异步能力:const res = await Promise.allSettled([getData1()],[getData2()],[getData3()])
普通使用web组件
从index页面跳转到ArkWebIndex页面,首先会有白屏等待情况并有一个抖动。
index 页面
@Entry
@Component
struct Index {
message: string = '性能优化-web页面启动时常优化'
navPathStack: NavPathStack = new NavPathStack()
build() {
Navigation(this.navPathStack) {
Button(this.message).onClick(() => {
this.navPathStack.pushPathByName('ArkWebIndexPage', null);
})
}
.hideTitleBar(true)
.mode(NavigationMode.Stack)
}
}
ArkWebIndex页面
import webview from "@ohos.web.webview"
export const WEB_URL = 'http://developer.huawei.com/consumer/cn'
@Builder
export function ArkWebIndexBuilder(name: string, param: Object) {
ArkWebIndex()
}
@Component
export struct ArkWebIndex {
webviewController: webview.WebviewController = new webview.WebviewController()
navPathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
Web({
src: WEB_URL,
controller: this.webviewController
})
.domStorageAccess(true)
.zoomAccess(true)
.fileAccess(true)
.mixedMode(MixedMode.All)
.width('100%')
.height('100%')
}
.hideTitleBar(true)
.onReady((context: NavDestinationContext) => {
this.navPathStack = context.pathStack
console.info('ArkWebIndex onReady')
})
}
}
配置:resources>base>profile>router_map.json
{
"routerMap": [
{
"name": "ArkWebIndexPage",
"pageSourceFile": "src/main/ets/pages/ArkWebIndex.ets",
"buildFunction": "ArkWebIndexBuilder"
}
]
}
配置:module.json5
"routerMap": "$profile:router_map",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
]
优化方案
通过预解析、预连接、预下载、预渲染,将Web页面的网络请求和组件创建等操作提前执行,从而消除页面跳转后的白屏等待时间。
index 页面
import { webview } from '@kit.ArkWeb'
import { creatWeb } from '../utils/ArkWebUtil'
import { WEB_URL } from './ArkWebIndex'
@Entry
@Component
struct Index {
message: string = '性能优化-web页面启动时常优化'
navPathStack: NavPathStack = new NavPathStack()
aboutToAppear(): void {
// 预解析、预连接
webview.WebviewController.initializeWebEngine() // 初始化引擎
webview.WebviewController.prepareForPageLoad(WEB_URL, true, 1) //(预解析的web地址,是否要进行预连接,socket数量)
// 预下载
// 预渲染
creatWeb(WEB_URL, this.getUIContext()) // 创建web组件
}
build() {
Navigation(this.navPathStack) {
Button(this.message).onClick(() => {
this.navPathStack.pushPathByName('ArkWebIndexPage', null);
})
}
.hideTitleBar(true)
.mode(NavigationMode.Stack)
}
}
ArkWebIndex页面
import webview from "@ohos.web.webview"
import { getWeb } from "../utils/ArkWebUtil"
export const WEB_URL = 'http://developer.huawei.com/consumer/cn'
@Builder
export function ArkWebIndexBuilder(name: string, param: Object) {
ArkWebIndex()
}
@Component
export struct ArkWebIndex {
webviewController: webview.WebviewController = new webview.WebviewController()
navPathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
NodeContainer(getWeb(WEB_URL)) // 自定义节点,将创建好的web组件放进节点
}
.hideTitleBar(true)
.onReady((context: NavDestinationContext) => {
this.navPathStack = context.pathStack
console.info('ArkWebIndex onReady')
})
}
}
ArkWebUtil
import { BuilderNode, FrameNode, NodeController } from '@ohos.arkui.node'
import { UIContext } from '@ohos.arkui.UIContext'
import { webview } from '@kit.ArkWeb'
interface Data {
url: string
controller: WebviewController
}
@Builder
function WebBuilder(data: Data) {
Web({
src: data.url,
controller: data.controller
})
.domStorageAccess(true)
.zoomAccess(true)
.fileAccess(true)
.mixedMode(MixedMode.All)
.width('100%')
.height('100%')
.onAppear(() => {
// 预下载
data.controller.prefetchPage(data.url)
})
.onPageBegin(() => {
// 页面后台渲染默认为非激活状态需手动激活
data.controller.onActive()
})
}
let wrap = wrapBuilder(WebBuilder)
class myNodeController extends NodeController {
// 要渲染节点的内容
rootNode: BuilderNode<Data[]> | null = null
// 返回要渲染的节点
makeNode(uiContext: UIContext): FrameNode | null {
if (this.rootNode != null) {
const parent = this.rootNode.getFrameNode()?.getParent() // 判断有没有把内容挂载到root上
if (parent) {
parent.clearChildren() // 如果有parent,parent清空一下子节点
}
let root = new FrameNode(uiContext) // 要渲染的节点
root.appendChild(this.rootNode?.getFrameNode()) // 把rootNode添加到要渲染的节点上
return root
}
return null
}
// 挂载web组件到要渲染的节点上
initWeb(url: string, uiContext: UIContext) {
this.rootNode = new BuilderNode(uiContext)
this.rootNode.build(wrap, { url, controller: new webview.WebviewController })
}
}
let NodeMap: Map<string, myNodeController | null> = new Map()
// 创建web组件
export const creatWeb = (url: string, uiContext: UIContext) => {
let baseNode = new myNodeController()
baseNode.initWeb(url, uiContext)
NodeMap.set(url, baseNode) // 存起来,渲染web时取baseNode
}
// 返回创建好的web组件
export const getWeb = (url: string) => {
return NodeMap.get(url)
}