1# 动态加载 2 3动态import支持条件延迟加载,支持部分反射功能,可以提升页面的加载速度;动态import支持加载HSP模块/HAR模块/OHPM包/Native库等,并且HAR模块间只有变量动态import时还可以进行模块解耦。 4 5## 技术适用场景介绍 6应用开发的有些场景中,如果希望根据条件导入模块或者按需导入模块,可以使用动态导入代替[静态导入](../quick-start/introduction-to-arkts.md#静态导入)。下面是可能会需要动态导入的场景: 7 8* 当静态导入的模块明显降低了代码的加载速度且被使用的可能性低,或者并不需要马上使用它。 9* 当静态导入的模块明显占用了大量的系统内存且被使用的可能性低。 10* 当被导入的模块,在加载时并不存在,需要异步获取。 11* 当被导入的模块说明符,需要动态构建。静态导入只能使用静态说明符。 12* 当被导入的模块有副作用(这里的副作用,可以理解为模块中会直接运行的代码),这些副作用只有在触发了某些条件才被需要时。 13 14## 业务扩展场景介绍 15动态import在业务上除了能实现条件延迟加载,还可以实现部分反射功能。实例如下,HAP动态import HAR包harlibrary,并调用静态成员函数staticAdd()、成员函数instanceAdd(),以及全局方法addHarlibrary()。 16```typescript 17// harlibrary's src/main/ets/utils/Calc.ets 18export class Calc { 19 public static staticAdd(a:number, b:number):number { 20 let c = a + b; 21 console.info('DynamicImport I am harlibrary in staticAdd, %d + %d = %d', a, b, c); 22 return c; 23 } 24 25 public instanceAdd(a:number, b:number):number { 26 let c = a + b; 27 console.info('DynamicImport I am harlibrary in instanceAdd, %d + %d = %d', a, b, c); 28 return c; 29 } 30} 31 32export function addHarlibrary(a:number, b:number):number { 33 let c = a + b; 34 console.info('DynamicImport I am harlibrary in addHarlibrary, %d + %d = %d', a, b, c); 35 return c; 36} 37``` 38 39```typescript 40// harlibrary's Index.ets 41export { Calc, addHarlibrary } from './src/main/ets/utils/Calc' 42``` 43 44```json5 45// HAP's oh-package.json5 46"dependencies": { 47 "harlibrary": "file:../harlibrary" 48} 49``` 50 51```typescript 52// HAP's src/main/ets/pages/Index.ets 53import('harlibrary').then((ns:ESObject) => { 54 ns.Calc.staticAdd(8, 9); // 调用静态成员函数staticAdd() 55 let calc:ESObject = new ns.Calc(); // 实例化类Calc 56 calc.instanceAdd(10, 11); // 调用成员函数instanceAdd() 57 ns.addHarlibrary(6, 7); // 调用全局方法addHarlibrary() 58 59 // 使用类、成员函数和方法的字符串名字进行反射调用 60 let className = 'Calc'; 61 let methodName = 'instanceAdd'; 62 let staticMethod = 'staticAdd'; 63 let functionName = 'addHarlibrary'; 64 ns[className][staticMethod](12, 13); // 调用静态成员函数staticAdd() 65 let calc1:ESObject = new ns[className](); // 实例化类Calc 66 calc1[methodName](14, 15); // 调用成员函数instanceAdd() 67 ns[functionName](16, 17); // 调用全局方法addHarlibrary() 68}); 69``` 70 71## 动态import实现方案介绍 72动态import根据入参是常量还是变量,分成动态import常量表达式和动态import变量表达式两大特性规格。 73以下是动态import支持的规格列表: 74 75| 动态import场景 | 动态import详细分类 | 说明 | 76| :------------- | :----------------------------- | :------------------------------------------------------- | 77| 本地工程模块 | 动态import模块内文件路径 | 要求路径以./或../开头。 | 78| 本地工程模块 | 动态import HSP模块名 | - | 79| 本地工程模块 | 动态import HSP模块文件路径 | 暂仅支持动态import常量表达式,不支持动态import变量表达式。 | 80| 本地工程模块 | 动态import HAR模块名 | - | 81| 本地工程模块 | 动态import HAR模块文件路径 | 暂仅支持动态import常量表达式,不支持动态import变量表达式。 | 82| 远程包 | 动态import远程HAR模块名 | - | 83| 远程包 | 动态import ohpm包名 | - | 84| API | 动态import @system.* | - | 85| API | 动态import @ohos.* | - | 86| API | 动态import @arkui-x.* | - | 87| 模块Native库 | 动态import libNativeLibrary.so | - | 88 89>**说明:** 90> 91> 1.当前所有import中使用的模块名是依赖方oh-package.json5的dependencies中的别名。 92> 2.本地模块在依赖方的dependencies中配置的别名建议与moduleName以及packageName三者一致。moduleName指的是被依赖的HSP/HAR的module.json5中配置的名字,packageName指的是被依赖的HSP/HAR的oh-package.json5中配置的名字。 93> 3.import一个模块名,实际的行为是import该模块的入口文件,一般为index.ets/ts。 94 95## 动态import实现中的关键点 96 97### 动态import常量表达式 98 99动态import常量表达式是指动态import的入参为常量的场景。下面以HAP引用其他模块或API的示例来说明典型用法。 100 101说明:本文示例代码中Index.ets等路径是按照当前DevEco Studio的模块配置设置,如后续发生变化,请调整位置及其他文件相对路径。 102 103- **HAP常量动态import HAR模块名** 104 105 ```typescript 106 // HAR's Index.ets 107 export function add(a:number, b:number):number { 108 let c = a + b; 109 console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c); 110 return c; 111 } 112 ``` 113 114 ```typescript 115 // HAP's src/main/ets/pages/Index.ets 116 import('myHar').then((ns:ESObject) => { 117 console.info(ns.add(3, 5)); 118 }); 119 120 // 可使用 await 处理动态导入 (必须在 async 函数内使用) 121 async function asyncDynamicImport() { 122 let ns:ESObject = await import('myHar'); 123 console.info(ns.add(3, 5)); 124 } 125 ``` 126 127 ```json5 128 // HAP's oh-package.json5 129 "dependencies": { 130 "myHar": "file:../myHar" 131 } 132 ``` 133 134- **HAP常量动态import HAR模块文件路径** 135 136 ```typescript 137 // HAR's Index.ets 138 export function add(a:number, b:number):number { 139 let c = a + b; 140 console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c); 141 return c; 142 } 143 ``` 144 145 ```typescript 146 // HAP's src/main/ets/pages/Index.ets 147 import('myHar/Index').then((ns:ESObject) => { 148 console.info(ns.add(3, 5)); 149 }); 150 ``` 151 152 ```json5 153 // HAP's oh-package.json5 154 "dependencies": { 155 "myHar": "file:../myHar" 156 } 157 ``` 158 159- **HAP常量动态import HSP模块名** 160 161 ```typescript 162 // HSP's Index.ets 163 export function add(a:number, b:number):number { 164 let c = a + b; 165 console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 166 return c; 167 } 168 ``` 169 170 ```typescript 171 // HAP's src/main/ets/pages/Index.ets 172 import('myHsp').then((ns:ESObject) => { 173 console.info(ns.add(3, 5)); 174 }); 175 ``` 176 177 ```json5 178 // HAP's oh-package.json5 179 "dependencies": { 180 "myHsp": "file:../myHsp" 181 } 182 ``` 183 184- **HAP常量动态import HSP模块名文件路径** 185 186 ```typescript 187 // HSP's Index.ets 188 export function add(a:number, b:number):number { 189 let c = a + b; 190 console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 191 return c; 192 } 193 ``` 194 195 ```typescript 196 // HAP's src/main/ets/pages/Index.ets 197 import('myHsp/Index').then((ns:ESObject) => { 198 console.info(ns.add(3, 5)); 199 }); 200 ``` 201 202 ```json5 203 // HAP's oh-package.json5 204 "dependencies": { 205 "myHsp": "file:../myHsp" 206 } 207 ``` 208 209- **HAP常量动态import远程HAR模块名** 210 211 ```typescript 212 // HAP's src/main/ets/pages/Index.ets 213 import('@ohos/crypto-js').then((ns:ESObject) => { 214 console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456)); 215 }); 216 ``` 217 218 ```json5 219 // HAP's oh-package.json5 220 "dependencies": { 221 "@ohos/crypto-js": "2.0.3-rc.0" 222 } 223 ``` 224 225- **HAP常量动态import ohpm包** 226 227 ```typescript 228 // HAP's src/main/ets/pages/Index.ets 229 import('json5').then((ns:ESObject) => { 230 console.info('DynamicImport json5'); 231 }); 232 ``` 233 234 ```json5 235 // HAP's oh-package.json5 236 "dependencies": { 237 "json5": "1.0.2" 238 } 239 ``` 240 241- **HAP常量动态import自己的单文件** 242 243 ```typescript 244 // HAP's src/main/ets/Calc.ets 245 export function add(a:number, b:number):number { 246 let c = a + b; 247 console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c); 248 return c; 249 } 250 ``` 251 252 ```typescript 253 // HAP's src/main/ets/pages/Index.ets 254 import('../Calc').then((ns:ESObject) => { 255 console.info(ns.add(3, 5)); 256 }); 257 ``` 258 259- **HAP常量动态import自己的Native库** 260 261 ```typescript 262 // libnativeapi.so's index.d.ts 263 export const add: (a:number, b:number) => number; 264 ``` 265 266 ```typescript 267 // HAP's src/main/ets/pages/Index.ets 268 import('libnativeapi.so').then((ns:ESObject) => { 269 console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3)); 270 }); 271 ``` 272 273 ```json5 274 // HAP's oh-package.json5 275 "dependencies": { 276 "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi" 277 } 278 ``` 279 280- **HAP常量动态import加载API** 281 282 ```typescript 283 // HAP's src/main/ets/pages/Index.ets 284 import('@system.app').then((ns:ESObject) => { ns.default.terminate(); }); 285 import('@system.router').then((ns:ESObject) => { ns.default.clear(); }); 286 import('@ohos.curves').then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); }); 287 import('@ohos.matrix4').then((ns:ESObject) => { ns.default.identity(); }); 288 import('@ohos.hilog').then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); }); 289 ``` 290 291### 动态import变量表达式 292 293DevEco Studio中模块间的依赖关系通过oh-package.json5中的dependencies进行配置。dependencies列表中所有模块默认都会进行安装(本地模块)或下载(远程模块),但是不会默认参与编译。HAP/HSP编译时会以入口文件(一般为Index.ets/ts)开始搜索依赖关系,搜索到的模块或文件才会加入编译。 294在编译期,静态import和常量动态import可以被打包工具rollup及其插件识别解析,加入依赖树中,参与编译流程,最终生成方舟字节码。但是,如果是变量动态import,该变量值可能需要进行运算或外部传入才能得到,在编译态无法解析其内容,也就无法加入编译。为了将这部分模块/文件加入编译,还需要额外增加一个runtimeOnly的buildOption配置,用于配置动态import的变量实际的模块名或文件路径。 295 296**1. runtimeOnly字段schema配置格式** 297 298在HAP/HSP/HAR的build-profile.json5中的buildOption中增加runtimeOnly配置项,仅在通过变量动态import时配置,静态import和常量动态import无需配置;并且,通过变量动态import加载API时也无需配置runtimeOnly。如下实例说明如何配置通过变量动态import其他模块,以及变量动态import本模块自己的单文件: 299 300```typescript 301// 变量动态import其他模块myHar 302let harName = 'myHar'; 303import(harName).then(……); 304 305// 变量动态import本模块自己的单文件src/main/ets/index.ets 306let filePath = './Calc'; 307import(filePath).then(……); 308``` 309 310对应的runtimeOnly配置: 311 312```json 313"buildOption": { 314 "arkOptions": { 315 "runtimeOnly": { 316 "packages": [ "myHar" ] // 配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。 317 "sources": [ "./src/main/ets/utils/Calc.ets" ] // 配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。 318 } 319 } 320} 321``` 322 323"runtimeOnly"的"packages":用于配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。 324"runtimeOnly"的"sources":用于配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。 325 326**2. 使用实例** 327 328- **HAP变量动态import HAR模块名** 329 330 ```typescript 331 // HAR's Index.ets 332 export function add(a:number, b:number):number { 333 let c = a + b; 334 console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c); 335 return c; 336 } 337 ``` 338 ```typescript 339 // HAP's src/main/ets/pages/Index.ets 340 let packageName = 'myHar'; 341 import(packageName).then((ns:ESObject) => { 342 console.info(ns.add(3, 5)); 343 }); 344 ``` 345 ```json5 346 // HAP's oh-package.json5 347 "dependencies": { 348 "myHar": "file:../myHar" 349 } 350 ``` 351 ```json5 352 // HAP's build-profile.json5 353 "buildOption": { 354 "arkOptions": { 355 "runtimeOnly": { 356 "packages": [ 357 "myHar" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 358 ] 359 } 360 } 361 } 362 ``` 363 364- **HAP变量动态import HSP模块名** 365 366 ```typescript 367 // HSP's Index.ets 368 export function add(a:number, b:number):number { 369 let c = a + b; 370 console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 371 return c; 372 } 373 ``` 374 ```typescript 375 // HAP's src/main/ets/pages/Index.ets 376 let packageName = 'myHsp'; 377 import(packageName).then((ns:ESObject) => { 378 console.info(ns.add(3, 5)); 379 }); 380 ``` 381 ```json5 382 // HAP's oh-package.json5 383 "dependencies": { 384 "myHsp": "file:../myHsp" 385 } 386 ``` 387 ```json5 388 // HAP's build-profile.json5 389 "buildOption": { 390 "arkOptions": { 391 "runtimeOnly": { 392 "packages": [ 393 "myHsp" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 394 ] 395 } 396 } 397 } 398 ``` 399 400- **HAP变量动态import远程HAR模块名** 401 402 ```typescript 403 // HAP's src/main/ets/pages/Index.ets 404 let packageName = '@ohos/crypto-js'; 405 import(packageName).then((ns:ESObject) => { 406 console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456)); 407 }); 408 ``` 409 ```json5 410 // HAP's oh-package.json5 411 "dependencies": { 412 "@ohos/crypto-js": "2.0.3-rc.0" 413 } 414 ``` 415 ```json5 416 // HAP's build-profile.json5 417 "buildOption": { 418 "arkOptions": { 419 "runtimeOnly": { 420 "packages": [ 421 "@ohos/crypto-js" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 422 ] 423 } 424 } 425 } 426 ``` 427 428- **HAP变量动态import ohpm包** 429 430 ```typescript 431 // HAP's src/main/ets/pages/Index.ets 432 let packageName = 'json5'; 433 import(packageName).then((ns:ESObject) => { 434 console.info('DynamicImport json5'); 435 }); 436 ``` 437 ```json5 438 // HAP's oh-package.json5 439 "dependencies": { 440 "json5": "1.0.2" 441 } 442 ``` 443 ```json5 444 // HAP's build-profile.json5 445 "buildOption": { 446 "arkOptions": { 447 "runtimeOnly": { 448 "packages": [ 449 "json5" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 450 ] 451 } 452 } 453 } 454 ``` 455 456- **HAP变量动态import自己的单文件** 457 458 ```typescript 459 // HAP's src/main/ets/Calc.ets 460 export function add(a:number, b:number):number { 461 let c = a + b; 462 console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c); 463 return c; 464 } 465 ``` 466 ```typescript 467 // HAP's src/main/ets/pages/Index.ets 468 let filePath = '../Calc'; 469 import(filePath).then((ns:ESObject) => { 470 console.info(ns.add(3, 5)); 471 }); 472 ``` 473 ```json5 474 // HAP's build-profile.json5 475 "buildOption": { 476 "arkOptions": { 477 "runtimeOnly": { 478 "sources": [ 479 "./src/main/ets/Calc.ets" // 仅用于使用变量动态import模块自己单文件场景,静态import或常量动态import无需配置。 480 ] 481 } 482 } 483 } 484 ``` 485 486- **HAP变量动态import自己的Native库** 487 488 ```typescript 489 // libnativeapi.so's index.d.ts 490 export const add: (a:number, b:number) => number; 491 ``` 492 ```typescript 493 // HAP's src/main/ets/pages/Index.ets 494 let soName = 'libnativeapi.so'; 495 import(soName).then((ns:ESObject) => { 496 console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3)); 497 }); 498 ``` 499 ```json5 500 // HAP's oh-package.json5 501 "dependencies": { 502 "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi" 503 } 504 ``` 505 ```json5 506 // HAP's build-profile.json5 507 "buildOption": { 508 "arkOptions": { 509 "runtimeOnly": { 510 "packages": [ 511 "libnativeapi.so" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 512 ] 513 } 514 } 515 } 516 ``` 517 518- **HAP变量动态import加载API** 519 520 ```typescript 521 // HAP's src/main/ets/pages/Index.ets 522 let packageName = '@system.app'; 523 import(packageName).then((ns:ESObject) => { ns.default.terminate(); }); 524 packageName = '@system.router'; 525 import(packageName).then((ns:ESObject) => { ns.default.clear(); }); 526 packageName = '@ohos.curves'; 527 import(packageName).then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); }); 528 packageName = '@ohos.matrix4'; 529 import(packageName).then((ns:ESObject) => { ns.default.identity(); }); 530 packageName = '@ohos.hilog'; 531 import(packageName).then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); }); 532 ``` 533变量动态import加载API时无需配置runtimeOnly。 534 535### HAR模块间动态import依赖解耦 536当应用包含多个HAR包,且HAR包之间依赖关系比较复杂。在DevEco Studio中配置依赖关系时,可能会形成循环依赖。这时,如果HAR之间的依赖关系中仅有变量动态import,可以将HAR包之间直接依赖关系转移到HAP/HSP中配置,HAR包之间无需配置依赖关系,从而达到HAR包间依赖解耦的目的。如下示意图: 537 538 539 540HAR之间的依赖关系转移至HAP/HSP后: 541 542 543 544 545**1. 使用限制** 546- 仅限本地源码HAR包之间形成循环依赖时可使用该规避方案。 547- 被转移依赖的HAR之间只能通过变量动态import,不能有静态import或常量动态import。 548- 转移依赖时,dependencies和runtimeOnly依赖配置要同时转移。 549- HSP不支持转移依赖。即:HAP->HSP1->HSP2->HSP3,这里的HSP2和HSP3不能转移到HAP上面。 550- 转移依赖的整个链路上只能有HAR,不能跨越HSP转移。即:HAP->HAR1->HAR2->HSP->HAR3->HAR4。 551 552 HAR1对HAR2的依赖可以转移到HAP上,HAR3对HAR4的依赖可以转移到HSP上,但是,不能将HAR3或HAR4转移到HAP上。 553 554 555**2. 使用实例** 556 557下面的实例HAP变量动态import HAR包har1,har1变量动态import另一个HAR包har2。 558 559```json5 560// HAP's oh-package.json5 561"dependencies": { 562 "har1": "file:../har1" 563} 564``` 565```json5 566// HAP's build-profile.json5 567"buildOption": { 568 "arkOptions": { 569 "runtimeOnly": { 570 "packages": [ 571 "har1" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 572 ] 573 } 574 } 575} 576``` 577```typescript 578// HAP's src/main/ets/pages/Index.ets 579let harName = 'har1'; 580import(harName).then((ns:ESObject) => { 581 console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5)); 582}); 583``` 584```json5 585// har1's oh-package.json5 586"dependencies": { 587 "har2": "file:../har2" 588} 589``` 590```json5 591// har1's build-profile.json5 592"buildOption": { 593 "arkOptions": { 594 "runtimeOnly": { 595 "packages": [ 596 "har2" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 597 ] 598 } 599 } 600} 601``` 602```typescript 603// har1's Index.ets 604export { addHar1 } from './src/main/ets/utils/Calc' 605``` 606```typescript 607// har1's src/main/ets/utils/Calc.ets 608export function addHar1(a:number, b:number):number { 609 let c = a + b; 610 console.info('DynamicImport I am har1, %d + %d = %d', a, b, c); 611 612 let harName = 'har2'; 613 import(harName).then((ns:ESObject) => { 614 console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5)); 615 }); 616 return c; 617} 618``` 619```typescript 620// har2's Index.ets 621export { addHar2 } from './src/main/ets/utils/Calc' 622``` 623```typescript 624// har2's src/main/ets/utils/Calc.ets 625export function addHar2(a:number, b:number):number { 626 let c = a + b; 627 console.info('DynamicImport I am har2, %d + %d = %d', a, b, c); 628 return c; 629} 630``` 631 632har1对har2的依赖dependencies和runtimeOnly配置转移到HAP中,har1不需要配置对har2的dependencies和runtimeOnly配置: 633 634```json5 635// HAP's oh-package.json5 636"dependencies": { 637 "har1": "file:../har1", 638 "har2": "file:../har2" 639} 640``` 641```json5 642// HAP's build-profile.json5 643"buildOption": { 644 "arkOptions": { 645 "runtimeOnly": { 646 "packages": [ 647 "har1", 648 "har2" 649 ] 650 } 651 } 652} 653``` 654```typescript 655// HAP's src/main/ets/pages/Index.ets 656let harName = 'har1'; 657import(harName).then((ns:ESObject) => { 658 console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5)); 659}); 660``` 661```typescript 662// har1's Index.ets 663export { addHar1 } from './src/main/ets/utils/Calc' 664``` 665```typescript 666// har1's src/main/ets/utils/Calc.ets 667export function addHar1(a:number, b:number):number { 668 let c = a + b; 669 console.info('DynamicImport I am har1, %d + %d = %d', a, b, c); 670 671 let harName = 'har2'; 672 import(harName).then((ns:ESObject) => { 673 console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5)); 674 }); 675 return c; 676} 677``` 678```typescript 679// har2's Index.ets 680export { addHar2 } from './src/main/ets/utils/Calc' 681``` 682```typescript 683// har2's src/main/ets/utils/Calc.ets 684export function addHar2(a:number, b:number):number { 685 let c = a + b; 686 console.info('DynamicImport I am har2, %d + %d = %d', a, b, c); 687 return c; 688} 689``` 690 691