1# 运行时动态加载页面提升性能 2 3## 简介 4 5应用在加载页面时,如果引入大量暂不需要加载的模块,会因过多模块导致页面加载缓慢。比如当页面在使用Navigation组件时,主页默认先加载所有页面,此时若包含大量子页面,仅加载主页这一项就需要很长时间,但这些复杂的子页面与主页渲染无关。 6本文推荐使用动态加载解决上述问题,不在应用程序加载时就将所有模块都加载进来,而是按需加载模块,增加应用灵活性,提升应用性能。 7 8## 场景示例 9 10| 主页 | 子页面 | 11|------------------------------------------|--------------------------------------------| 12|  |  | 13 14下面示例应用通过Navigation组件常规加载与动态加载的对比,介绍如何在跳转时触发加载方法,实现按需加载子模块。 15 16### 常规加载 17 18开发者使用Navigation组件时,通常会在主页引入各子页面组件,在按钮中添加方法实现跳转。下述代码展示常规加载示例,通过import引入子组件。 19 201. 加载模块引入子页面,Navigation组件使用这些子模块实现跳转子页面功能,但用户进入主页时并不会马上使用到这些模块,存在冗余加载影响性能的可能。 21 ```ts 22 import { pageOne, pageOneData } from './pageOne'; 23 import { pageTwo, pagesTwoData } from './pageTwo'; 24 ... 25 import router from '@ohos.router'; 26 ``` 272. 主页通过Navigation组件实现点击Button跳转到不同的子页面。 28 ```ts 29 @Provide('pathInfos') pageInfos: NavPathStack = new NavPathStack(); 30 31 @Builder 32 PageMap(name: string) { 33 if (name === 'pageOne') { 34 pageOne(new pagesOneData(name, this.pageInfos)); 35 } else if (name === 'pageTwo') { 36 pageTwo(new pagesTwoData(name, this.pageInfos)); 37 } 38 ... 39 } 40 41 build() { 42 Navigation(this.pageInfos) { 43 Button('返回', { stateEffect: true, type: ButtonType.Capsule }) 44 .onClick(() => { 45 router.back(); 46 }) 47 Column() { 48 Button('pageOne', { stateEffect: true, type: ButtonType.Capsule }) 49 .onClick(() => { 50 this.pageInfos.pushPath({ name: 'pageOne' }); // 将name指定的NavDestination页面信息入栈 51 }) 52 Button('pageTwo', { stateEffect: true, type: ButtonType.Capsule }) 53 .onClick(() => { 54 this.pageInfos.pushPath({ name: 'pageTwo' }); 55 }) 56 ... 57 } 58 }.title('主页').navDestination(this.PageMap) 59 } 60 ``` 61 62### 动态加载 63 64当子组件较多、较复杂时,由于Navigation组件一次性加载所有模块,使用常规加载会导致主页加载缓慢。为了减少主页渲染时间,可以使用动态加载,在实际页面跳转时再按需动态引入子组件,优化用户的首次加载速度体验。以下是动态加载实现步骤: 65 661. 将需要被动态加载的组件pageOne组件用PageOneLoader函数封装,当PageOneLoader被调用时,会渲染pageOne页面。 67 ```ts 68 import { pageOne } from './pageOne'; 69 70 @Builder 71 export function PageOneLoader() { 72 pageOne(); 73 } 74 ``` 752. 由于在Navigation的PageMap中navDestination无法直接动态加载组件(import是函数,组件中无法引用函数),此处采用声明 @BuilderParam PageOneLoader 函数,在点击时初始化此函数,此时navDestination中可以调用 this.PageOneLoader() 从而加载组件pageOne。 76在主页DynamicHome实现动态加载pageOne的步骤如下: 77 a) 在主页DynamicHome中定义组件加载函数 `@BuilderParam PageOneLoader: () => void` ,用来承接await import异步导入pageOneLoader的结果。 78 ```ts 79 @BuilderParam PageOneLoader: () => void; 80 ``` 81 b) 定义异步函数,点击时为PageOneLoader初始化。 82 ```ts 83 async loadPageOne(key: string){ 84 if (key === "pageOne") { 85 let PageObj: ESObject = await import("../pages/PageOneLoader"); 86 this.PageOneLoader = PageObj.PageOneLoader; 87 } 88 } 89 ``` 90 c) 点击按钮触发点击函数,调用loadPageOne,此时真正的初始化 @BuilderParam PageOneLoader ,并使用Navigation加载组件。 91 ```ts 92 private onEntryClick(): void { 93 try { 94 this.loadPageOne('pageOne'); 95 this.pageInfos.clear(); 96 this.pageInfos.pushPathByName('pageOne', ''); 97 logger.info('DynamicImport Success'); 98 } catch (error) { 99 logger.info('DynamicImport Fail'); 100 } 101 } 102 ``` 103 d) 触发PageMap中已经被初始化的PageOneLoader函数,动态加载pageOne组件。 104 ```ts 105 @Builder 106 PageMap(name: string) { 107 if (name === 'pageOne') { 108 this.PageOneLoader(); 109 } 110 } 111 ``` 112完整的DynamicHome主页代码如下: 113```ts 114import router from '@ohos.router'; 115import { logger } from '../../ets/utils/Logger'; 116 117@Entry 118@Component 119struct DynamicHome { 120 @Provide('pathInfos') pageInfos: NavPathStack = new NavPathStack(); 121 @State active: boolean = false; 122 @BuilderParam PageOneLoader: () => void; 123 124 @Builder 125 PageMap(name: string) { 126 if (name === 'pageOne') { 127 this.PageOneLoader(); 128 } 129 } 130 131 build() { 132 Navigation(this.pageInfos) { 133 Column() { 134 Button('返回', { stateEffect: true, type: ButtonType.Capsule }) 135 .width('80%') 136 .height(40) 137 .margin(20) 138 .onClick(() => { 139 router.back(); 140 }) 141 Button('PageOne-动态', { stateEffect: true, type: ButtonType.Capsule }) 142 .width('80%') 143 .height(40) 144 .margin(20) 145 .onClick(() => { 146 this.onEntryClick(); 147 }) 148 } 149 }.title('HOME').navDestination(this.PageMap) 150 } 151 152 async loadPageOne(key: String) { 153 if (key === "pageOne") { 154 let PageObj: ESObject = await import("../pages/PageOneLoader"); 155 this.PageOneLoader = PageObj.PageOneLoader; 156 } 157 } 158 159 // 触发动态加载 160 private onEntryClick(): void { 161 try { 162 this.loadPageOne('pageOne'); 163 this.pageInfos.clear(); 164 this.pageInfos.pushPathByName('pageOne', ''); 165 logger.info('DynamicImport Success'); 166 } catch (error) { 167 logger.info('DynamicImport Fail'); 168 } 169 } 170} 171``` 172当子页面逐渐增多时,Navigation组件使用常规加载会因默认加载所有子页面导致性能开销增大,主页内存占用增加,加载时间变长。此时改用动态加载不再提前加载子页面,可以有效地避免这些任务阻塞主线程,从而降低整体资源消耗,提高主页的加载速度。 173