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 911. 当前所有import中使用的模块名是依赖方oh-package.json5的dependencies中的别名; 922. 本地模块在依赖方的dependencies中配置的别名建议与moduleName以及packageName三者一致。moduleName指的是被依赖的HSP/HAR的module.json5中配置的名字,packageName指的是被依赖的HSP/HAR的oh-package.json5中配置的名字。 933. 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 121 ```json5 122 // HAP's oh-package.json5 123 "dependencies": { 124 "myHar": "file:../myHar" 125 } 126 ``` 127 128- **HAP常量动态import HAR模块文件路径** 129 130 ```typescript 131 // HAR's Index.ets 132 export function add(a:number, b:number):number { 133 let c = a + b; 134 console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c); 135 return c; 136 } 137 ``` 138 139 ```typescript 140 // HAP's src/main/ets/pages/Index.ets 141 import('myHar/Index').then((ns:ESObject) => { 142 console.info(ns.add(3, 5)); 143 }); 144 ``` 145 146 ```json5 147 // HAP's oh-package.json5 148 "dependencies": { 149 "myHar": "file:../myHar" 150 } 151 ``` 152 153- **HAP常量动态import HSP模块名** 154 155 ```typescript 156 // HSP's Index.ets 157 export function add(a:number, b:number):number { 158 let c = a + b; 159 console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 160 return c; 161 } 162 ``` 163 164 ```typescript 165 // HAP's src/main/ets/pages/Index.ets 166 import('myHsp').then((ns:ESObject) => { 167 console.info(ns.add(3, 5)); 168 }); 169 ``` 170 171 ```json5 172 // HAP's oh-package.json5 173 "dependencies": { 174 "myHsp": "file:../myHsp" 175 } 176 ``` 177 178- **HAP常量动态import HSP模块名文件路径** 179 180 ```typescript 181 // HSP's Index.ets 182 export function add(a:number, b:number):number { 183 let c = a + b; 184 console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 185 return c; 186 } 187 ``` 188 189 ```typescript 190 // HAP's src/main/ets/pages/Index.ets 191 import('myHsp/Index').then((ns:ESObject) => { 192 console.info(ns.add(3, 5)); 193 }); 194 ``` 195 196 ```json5 197 // HAP's oh-package.json5 198 "dependencies": { 199 "myHsp": "file:../myHsp" 200 } 201 ``` 202 203- **HAP常量动态import远程HAR模块名** 204 205 ```typescript 206 // HAP's src/main/ets/pages/Index.ets 207 import('@ohos/crypto-js').then((ns:ESObject) => { 208 console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456)); 209 }); 210 ``` 211 212 ```json5 213 // HAP's oh-package.json5 214 "dependencies": { 215 "@ohos/crypto-js": "2.0.3-rc.0" 216 } 217 ``` 218 219- **HAP常量动态import ohpm包** 220 221 ```typescript 222 // HAP's src/main/ets/pages/Index.ets 223 import('json5').then((ns:ESObject) => { 224 console.info('DynamicImport json5'); 225 }); 226 ``` 227 228 ```json5 229 // HAP's oh-package.json5 230 "dependencies": { 231 "json5": "1.0.2" 232 } 233 ``` 234 235- **HAP常量动态import自己的单文件** 236 237 ```typescript 238 // HAP's src/main/ets/Calc.ets 239 export function add(a:number, b:number):number { 240 let c = a + b; 241 console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c); 242 return c; 243 } 244 ``` 245 246 ```typescript 247 // HAP's src/main/ets/pages/Index.ets 248 import('../Calc').then((ns:ESObject) => { 249 console.info(ns.add(3, 5)); 250 }); 251 ``` 252 253- **HAP常量动态import自己的Native库** 254 255 ```typescript 256 // libnativeapi.so's index.d.ts 257 export const add: (a:number, b:number) => number; 258 ``` 259 260 ```typescript 261 // HAP's src/main/ets/pages/Index.ets 262 import('libnativeapi.so').then((ns:ESObject) => { 263 console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3)); 264 }); 265 ``` 266 267 ```json5 268 // HAP's oh-package.json5 269 "dependencies": { 270 "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi" 271 } 272 ``` 273 274- **HAP常量动态import加载API** 275 276 ```typescript 277 // HAP's src/main/ets/pages/Index.ets 278 import('@system.app').then((ns:ESObject) => { ns.default.terminate(); }); 279 import('@system.router').then((ns:ESObject) => { ns.default.clear(); }); 280 import('@ohos.curves').then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); }); 281 import('@ohos.matrix4').then((ns:ESObject) => { ns.default.identity(); }); 282 import('@ohos.hilog').then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); }); 283 ``` 284 285### 动态import变量表达式 286 287DevEco Studio中模块间的依赖关系通过oh-package.json5中的dependencies进行配置。dependencies列表中所有模块默认都会进行安装(本地模块)或下载(远程模块),但是不会默认参与编译。HAP/HSP编译时会以入口文件(一般为Index.ets/ts)开始搜索依赖关系,搜索到的模块或文件才会加入编译。 288在编译期,静态import和常量动态import可以被打包工具rollup及其插件识别解析,加入依赖树中,参与到编译流程,最终生成方舟字节码。但是如果是变量动态import,该变量值可能需要进行运算或者外部传入才能得到,在编译态无法解析出其内容,也就无法加入编译。为了将这部分模块/文件加入编译,还需要额外增加一个runtimeOnly的buildOption配置,用于配置动态import的变量实际的模块名或者文件路径。 289 290**1. runtimeOnly字段schema配置格式** 291 292在HAP/HSP/HAR的build-profile.json5中的buildOption中增加runtimeOnly配置项,仅在通过变量动态import时配置,静态import和常量动态import无需配置;并且,通过变量动态import加载API时也无需配置runtimeOnly。 293如下实例说明如何配置通过变量动态import其他模块,以及变量动态import本模块自己的单文件: 294 295```typescript 296// 变量动态import其他模块myHar 297let harName = 'myHar'; 298import(harName).then(……); 299 300// 变量动态import本模块自己的单文件src/main/ets/index.ets 301let filePath = './Calc'; 302import(filePath).then(……); 303``` 304 305对应的runtimeOnly配置: 306 307```typescript 308"buildOption": { 309 "arkOptions": { 310 "runtimeOnly": { 311 "packages": [ "myHar" ] // 配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。 312 "sources": [ "./src/main/ets/utils/Calc.ets" ] // 配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。 313 } 314 } 315} 316``` 317 318"runtimeOnly"的"packages":用于配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。 319"runtimeOnly"的"sources":用于配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。 320 321**2. 使用实例** 322 323- **HAP变量动态import HAR模块名** 324 325 ```typescript 326 // HAR's Index.ets 327 export function add(a:number, b:number):number { 328 let c = a + b; 329 console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c); 330 return c; 331 } 332 ``` 333 ```typescript 334 // HAP's src/main/ets/pages/Index.ets 335 let packageName = 'myHar'; 336 import(packageName).then((ns:ESObject) => { 337 console.info(ns.add(3, 5)); 338 }); 339 ``` 340 ```json5 341 // HAP's oh-package.json5 342 "dependencies": { 343 "myHar": "file:../myHar" 344 } 345 ``` 346 ```json5 347 // HAP's build-profile.json5 348 "buildOption": { 349 "arkOptions": { 350 "runtimeOnly": { 351 "packages": [ 352 "myHar" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 353 ] 354 } 355 } 356 } 357 ``` 358 359- **HAP变量动态import HSP模块名** 360 361 ```typescript 362 // HSP's Index.ets 363 export function add(a:number, b:number):number { 364 let c = a + b; 365 console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 366 return c; 367 } 368 ``` 369 ```typescript 370 // HAP's src/main/ets/pages/Index.ets 371 let packageName = 'myHsp'; 372 import(packageName).then((ns:ESObject) => { 373 console.info(ns.add(3, 5)); 374 }); 375 ``` 376 ```json5 377 // HAP's oh-package.json5 378 "dependencies": { 379 "myHsp": "file:../myHsp" 380 } 381 ``` 382 ```json5 383 // HAP's build-profile.json5 384 "buildOption": { 385 "arkOptions": { 386 "runtimeOnly": { 387 "packages": [ 388 "myHsp" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 389 ] 390 } 391 } 392 } 393 ``` 394 395- **HAP变量动态import远程HAR模块名** 396 397 ```typescript 398 // HAP's src/main/ets/pages/Index.ets 399 let packageName = '@ohos/crypto-js'; 400 import(packageName).then((ns:ESObject) => { 401 console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456)); 402 }); 403 ``` 404 ```json5 405 // HAP's oh-package.json5 406 "dependencies": { 407 "@ohos/crypto-js": "2.0.3-rc.0" 408 } 409 ``` 410 ```json5 411 // HAP's build-profile.json5 412 "buildOption": { 413 "arkOptions": { 414 "runtimeOnly": { 415 "packages": [ 416 "@ohos/crypto-js" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 417 ] 418 } 419 } 420 } 421 ``` 422 423- **HAP变量动态import ohpm包** 424 425 ```typescript 426 // HAP's src/main/ets/pages/Index.ets 427 let packageName = 'json5'; 428 import(packageName).then((ns:ESObject) => { 429 console.info('DynamicImport json5'); 430 }); 431 ``` 432 ```json5 433 // HAP's oh-package.json5 434 "dependencies": { 435 "json5": "1.0.2" 436 } 437 ``` 438 ```json5 439 // HAP's build-profile.json5 440 "buildOption": { 441 "arkOptions": { 442 "runtimeOnly": { 443 "packages": [ 444 "json5" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 445 ] 446 } 447 } 448 } 449 ``` 450 451- **HAP变量动态import自己的单文件** 452 453 ```typescript 454 // HAP's src/main/ets/Calc.ets 455 export function add(a:number, b:number):number { 456 let c = a + b; 457 console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c); 458 return c; 459 } 460 ``` 461 ```typescript 462 // HAP's src/main/ets/pages/Index.ets 463 let filePath = '../Calc'; 464 import(filePath).then((ns:ESObject) => { 465 console.info(ns.add(3, 5)); 466 }); 467 ``` 468 ```json5 469 // HAP's build-profile.json5 470 "buildOption": { 471 "arkOptions": { 472 "runtimeOnly": { 473 "sources": [ 474 "./src/main/ets/Calc.ets" // 仅用于使用变量动态import模块自己单文件场景,静态import或常量动态import无需配置。 475 ] 476 } 477 } 478 } 479 ``` 480 481- **HAP变量动态import自己的Native库** 482 483 ```typescript 484 // libnativeapi.so's index.d.ts 485 export const add: (a:number, b:number) => number; 486 ``` 487 ```typescript 488 // HAP's src/main/ets/pages/Index.ets 489 let soName = 'libnativeapi.so'; 490 import(soName).then((ns:ESObject) => { 491 console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3)); 492 }); 493 ``` 494 ```json5 495 // HAP's oh-package.json5 496 "dependencies": { 497 "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi" 498 } 499 ``` 500 ```json5 501 // HAP's build-profile.json5 502 "buildOption": { 503 "arkOptions": { 504 "runtimeOnly": { 505 "packages": [ 506 "libnativeapi.so" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 507 ] 508 } 509 } 510 } 511 ``` 512 513- **HAP变量动态import加载API** 514 515 ```typescript 516 // HAP's src/main/ets/pages/Index.ets 517 let packageName = '@system.app'; 518 import(packageName).then((ns:ESObject) => { ns.default.terminate(); }); 519 packageName = '@system.router'; 520 import(packageName).then((ns:ESObject) => { ns.default.clear(); }); 521 packageName = '@ohos.curves'; 522 import(packageName).then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); }); 523 packageName = '@ohos.matrix4'; 524 import(packageName).then((ns:ESObject) => { ns.default.identity(); }); 525 packageName = '@ohos.hilog'; 526 import(packageName).then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); }); 527 ``` 528变量动态import加载API时无需配置runtimeOnly。 529 530### HAR模块间动态import依赖解耦 531当应用包含多个HAR包,且HAR包之间依赖关系比较复杂。在DevEco Studio中配置依赖关系时,可能会形成循环依赖。这时,如果HAR之间的依赖关系中仅有变量动态import,可以将HAR包之间直接依赖关系转移到HAP/HSP中配置,HAR包之间无需配置依赖关系,从而达到HAR包间依赖解耦的目的。如下示意图: 532 533 534 535HAR之间依赖关系转移到HAP/HSP后: 536 537 538 539 540**1. 使用限制** 541- 仅限本地源码HAR包之间形成循环依赖时可使用该规避方案。 542- 被转移依赖的HAR之间只能通过变量动态import,不能有静态import或常量动态import。 543- 转移依赖时,dependencies和runtimeOnly依赖配置要同时转移。 544- HSP不支持转移依赖。即:HAP->HSP1->HSP2->HSP3,这里的HSP2和HSP3不能转移到HAP上面。 545- 转移依赖的整个链路上只能有HAR,不能跨越HSP转移。即:HAP->HAR1->HAR2->HSP->HAR3->HAR4。 546 547 HAR1对HAR2的依赖可以转移到HAP上,HAR3对HAR4的依赖可以转移到HSP上,但是,不能将HAR3或HAR4转移到HAP上。 548 549 550**2. 使用实例** 551 552下面的实例HAP变量动态import HAR包har1,har1变量动态import另一个HAR包har2。 553 554```json5 555// HAP's oh-package.json5 556"dependencies": { 557 "har1": "file:../har1" 558} 559``` 560```json5 561// HAP's build-profile.json5 562"buildOption": { 563 "arkOptions": { 564 "runtimeOnly": { 565 "packages": [ 566 "har1" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 567 ] 568 } 569 } 570} 571``` 572```typescript 573// HAP's src/main/ets/pages/Index.ets 574let harName = 'har1'; 575import(harName).then((ns:ESObject) => { 576 console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5)); 577}); 578``` 579```json5 580// har1's oh-package.json5 581"dependencies": { 582 "har2": "file:../har2" 583} 584``` 585```json5 586// har1's build-profile.json5 587"buildOption": { 588 "arkOptions": { 589 "runtimeOnly": { 590 "packages": [ 591 "har2" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 592 ] 593 } 594 } 595} 596``` 597```typescript 598// har1's Index.ets 599export { addHar1 } from './src/main/ets/utils/Calc' 600``` 601```typescript 602// har1's src/main/ets/utils/Calc.ets 603export function addHar1(a:number, b:number):number { 604 let c = a + b; 605 console.info('DynamicImport I am har1, %d + %d = %d', a, b, c); 606 607 let harName = 'har2'; 608 import(harName).then((ns:ESObject) => { 609 console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5)); 610 }); 611 return c; 612} 613``` 614```typescript 615// har2's Index.ets 616export { addHar2 } from './src/main/ets/utils/Calc' 617``` 618```typescript 619// har2's src/main/ets/utils/Calc.ets 620export function addHar2(a:number, b:number):number { 621 let c = a + b; 622 console.info('DynamicImport I am har2, %d + %d = %d', a, b, c); 623 return c; 624} 625``` 626 627har1对har2的依赖dependencies和runtimeOnly配置转移到HAP中,har1不需要配置对har2的dependencies和runtimeOnly配置: 628 629```json5 630// HAP's oh-package.json5 631"dependencies": { 632 "har1": "file:../har1", 633 "har2": "file:../har2" 634} 635``` 636```json5 637// HAP's build-profile.json5 638"buildOption": { 639 "arkOptions": { 640 "runtimeOnly": { 641 "packages": [ 642 "har1", 643 "har2" 644 ] 645 } 646 } 647} 648``` 649```typescript 650// HAP's src/main/ets/pages/Index.ets 651let harName = 'har1'; 652import(harName).then((ns:ESObject) => { 653 console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5)); 654}); 655``` 656```typescript 657// har1's Index.ets 658export { addHar1 } from './src/main/ets/utils/Calc' 659``` 660```typescript 661// har1's src/main/ets/utils/Calc.ets 662export function addHar1(a:number, b:number):number { 663 let c = a + b; 664 console.info('DynamicImport I am har1, %d + %d = %d', a, b, c); 665 666 let harName = 'har2'; 667 import(harName).then((ns:ESObject) => { 668 console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5)); 669 }); 670 return c; 671} 672``` 673```typescript 674// har2's Index.ets 675export { addHar2 } from './src/main/ets/utils/Calc' 676``` 677```typescript 678// har2's src/main/ets/utils/Calc.ets 679export function addHar2(a:number, b:number):number { 680 let c = a + b; 681 console.info('DynamicImport I am har2, %d + %d = %d', a, b, c); 682 return c; 683} 684``` 685 686