1# 运行时动态加载页面提升性能
2
3## 简介
4
5应用在加载页面时,如果引入暂时不需要加载的模块,会导致页面加载缓慢和不必要的内存占用。例如当页面使用Navigation组件时,主页默认加载子页面,此时若子页面使用了Web组件,则会提前加载Web相关的so库,即使并没有进入子页面。
6
7本文推荐使用动态加载解决上述问题,不在进入主页面时就将所有模块都加载进来,而是按需加载模块,增加应用灵活性,提升应用性能。
8
9## 场景示例
10
11![场景示例图](figures/dynamic_import_1.gif)
12
13下面示例应用通过Navigation组件常规加载与动态加载的对比,介绍如何在跳转时触发加载方法,实现按需加载子模块。
14
15### 常规加载
16
17开发者使用Navigation组件时,通常会在主页引入子页面组件,在按钮中添加方法实现跳转。下述代码展示常规加载示例,通过import引入子组件。
18
191. 创建子页面,添加一个Web组件,并加载一个在线的H5页面。
20
21   ```
22   import { webview } from '@kit.ArkWeb'
23
24   @Builder
25   export function buildPage() {
26     WebViewPage()
27   }
28
29   @Component
30   export struct WebViewPage {
31     webController: WebviewController = new webview.WebviewController();
32     url: string = 'https://gitee.com/harmonyos-cases/cases';
33
34     aboutToAppear(): void {
35       webview.WebviewController.initializeWebEngine();
36       webview.WebviewController.prepareForPageLoad(this.url, true, 2);
37     }
38
39     build() {
40       Column() {
41         Web({ src: this.url, controller: this.webController })
42       }
43     }
44   }
45   ```
46
472. 在主页面的Navigation中添加上一步创建的web组件作为子页面。
48
49   ```
50   import { WebViewPage } from './WebViewPage'
51
52   @Entry
53   @Component
54   export struct Page1 {
55     pageStack: NavPathStack = new NavPathStack();
56
57     @Builder
58     pageMap() {
59       NavDestination() {
60         WebViewPage()
61       }
62     }
63
64     build() {
65       Stack() {
66         Navigation(this.pageStack) {
67           Column() {
68             Button('加载页面')
69               .onClick(() => {
70                 this.pageStack.pushPath({ name: "" })
71               })
72               .margin({
73                 top:30
74               })
75           }
76           .height('100%')
77           .width('100%')
78         }.navDestination(this.pageMap)
79       }
80     }
81   }
82   ```
83
84编译运行后,通过DevEco Studio中的[Profiler工具](application-performance-analysis.md)抓取Trace,可以得到图1。通过图中泳道可以看到,主页面加载完成共耗时22.9ms(从DispatchTouchEvent标签到sendCommands标签,即从点击进入页面到通知系统开始渲染页面)。其中,load page标签表示加载整个页面的时间,共耗时19ms。继续向下可以看到,虽然主页面并没有使用Web组件,但是依旧加载了libwebview_napi.z.so,耗时大概12ms左右。如果用户只是在主页面停留,并没有继续进入子页面,那么这个so库的初始化就是没有必要的,但是依旧产生了耗时,并且占用了一部分的内存,会降低应用的性能。
85
86图1 常规加载主页面泳道图
87
88![image-20240725204758662](figures/dynamic_import_normal_1.PNG)
89
90### 动态加载
91
92由于Navigation组件一次性加载所有模块,使用常规加载会导致主页加载耗时变长。为了减少主页面加载耗时,可以使用动态加载,在实际页面跳转时再按需动态引入子组件,优化用户的首次加载速度体验。下面将使用动态import的方式实现常规加载的功能。
93
94```
95@Entry
96@Component
97export struct Page2 {
98  pageStack: NavPathStack = new NavPathStack();
99  @BuilderParam page: ESObject;
100
101  @Builder
102  pageMap() {
103    NavDestination() {
104      this.page();
105    }
106  }
107
108  build() {
109    Navigation(this.pageStack) {
110      Column() {
111        Button('加载页面')
112          .onClick(async () => {
113            import('./WebViewPage').then((result: ESObject) => {
114              this.page = result.buildPage;
115              this.pageStack.pushPath({ name: '' })
116            })
117          })
118          .margin({
119            top: 30
120          })
121      }
122      .height('100%')
123      .width('100%')
124    }
125    .navDestination(this.pageMap)
126  }
127}
128```
129
130通过代码可以看到,在主页面中并没有直接import子页面,而是在点击事件中使用了动态import的方式加载子页面,再通过NavPathStack.pushPath方法进行了跳转。编译运行后,通过DevEco Studio中的Profiler工具抓取Trace,可以得到图2。通过图中泳道可以看到,使用动态import后,主页面加载耗时只有7.9ms(图2中红框所示),其中load page标签耗时只有4.3ms左右,相较于常规加载,耗时减少了15ms。
131
132继续查看下面的泳道,可以发现相较于常规加载,并没有加载libwebview_napi.z.so的耗时,而是直接开始创建主页中的组件(Build[page]标签)。因为代码里没有直接使用import引入子页面,而是通过动态import的方式将加载子页面的逻辑放在了点击事件里面。只有在进入子页面时才会触发WebView库的加载,如图3中白框所示,实现了按需加载,减少了主页面不必要的耗时和内存占用。
133
134图2 动态加载主页面泳道图
135
136![di1](figures/dynamic_import_dynamic_1.PNG)
137
138图3 动态加载子页面泳道图
139
140![di2](figures/dynamic_import_dynamic_2.PNG)
141
142### 性能对比
143
144|         | 主页面加载耗时 | 主页面so加载耗时 |
145| ------  | :--------------:   | :--------------:   |
146| 常规加载 |     22.9ms     |     12ms     |
147| 动态加载 |      7.9ms      |      0ms      |
148
149## 总结
150
151通过上面的示例可以看到,使用动态import能够更灵活地按需加载子页面,减少主页面的加载耗时,提升应用性能和用户体验。当子页面不会被马上使用或者可能占用大量内存时,可以通过动态import的方式进行性能方面的优化。
152
153## 参考链接
154
155[动态import](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-dynamic-import-V5)
156