1# 动态import 2 3动态import支持条件延迟加载,支持部分反射功能,可以提升页面的加载速度;动态import支持加载HSP模块/HAR模块/OHPM包/Native库等,并且HAR模块间只有变量动态import时还可以进行模块解耦。 4 5## 技术适用场景介绍 6应用开发的有些场景中,如果希望根据条件导入模块或者按需导入模块,可以使用动态导入代替[静态导入](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.log('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.log('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.log('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 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根据入参是常量还是变量,分成两个大的特性规格。 73以下是动态import支持的规格列表: 74 75| 动态import场景 | 动态import详细分类 | 说明 | 76| :------------- | :----------------------------- | :--------------------------- | 77| 本地工程模块 | 动态import模块内文件路径 | 要求路径以./开头 | 78| 本地工程模块 | 动态import HSP模块名 | - | 79| 本地工程模块 | 动态import HSP模块文件路径 | 暂不支持动态import变量表达式 | 80| 本地工程模块 | 动态import HAR模块名 | - | 81| 本地工程模块 | 动态import HAR模块文件路径 | 暂不支持动态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变量表达式 98DevEco IDE中模块间的依赖关系通过oh-package.json5中的dependencies进行配置。dependencies列表中所有模块默认都会进行安装(本地模块)或下载(远程模块),但是不会默认参与编译。HAP/HSP编译时会以入口文件(一般为Index.ets/ts)开始搜索依赖关系,搜索到的模块或文件才会加入编译。 99在编译期,静态import和常量动态import可以被打包工具rollup及其插件识别解析,加入依赖树中,参与到编译流程,最终生成方舟字节码。但是如果是变量动态import,该变量值可能需要进行运算或者外部传入才能得到,在编译态无法解析出其内容,也就无法加入编译。为了将这部分模块/文件加入编译,还需要额外增加一个runtimeOnly的buildOption配置,用于配置动态import的变量实际的模块名或者文件路径。 100 1011. **runtimeOnly字段schema配置格式** 102 103在HAP/HSP/HAR的build-profile.json5中的buildOption中增加runtimeOnly配置项,仅在通过变量动态import时配置,静态import和常量动态import无需配置;并且,通过变量动态import加载API时也无需配置runtimeOnly。 104如下实例说明如何配置通过变量动态import其他模块,以及变量动态import本模块自己的单文件: 105 106```typescript 107// 变量动态import其他模块myHar 108let harName = 'myHar'; 109import(harName).then(……); 110 111// 变量动态import本模块自己的单文件src/main/ets/index.ets 112let filePath = './Calc'; 113import(filePath).then(……); 114``` 115 116对应的runtimeOnly配置: 117 118```typescript 119"buildOption": { 120 "arkOptions": { 121 "runtimeOnly": { 122 "packages": [ "myHar" ] // 配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。 123 "sources": [ "./src/main/ets/utils/Calc.ets" ] // 配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。 124 } 125 } 126} 127``` 128 129"runtimeOnly"的"packages":用于配置本模块变量动态import其他模块名,要求与dependencies中配置的名字一致。 130"runtimeOnly"的"sources":用于配置本模块变量动态import自己的文件路径,路径相对于当前build-profile.json5文件。 131 1322. **使用实例** 133 134- **HAP变量动态import HAR模块名** 135 136```typescript 137// HAR's Index.ets 138export function add(a:number, b:number):number { 139 let c = a + b; 140 console.log('DynamicImport I am a HAR, %d + %d = %d', a, b, c); 141 return c; 142} 143``` 144```typescript 145// HAP's Index.ets 146let packageName = 'myHar'; 147import(packageName).then((ns:ESObject) => { 148 console.log(ns.add(3, 5)); 149}); 150``` 151```json5 152// HAP's oh-package.json5 153"dependencies": { 154 "myHar": "file:../myHar" 155} 156``` 157```json5 158// HAP's build-profile.json5 159"buildOption": { 160 "arkOptions": { 161 "runtimeOnly": { 162 "packages": [ 163 "myHar" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 164 ] 165 } 166 } 167} 168``` 169 170- **HAP变量动态import HSP模块名** 171 172```typescript 173// HSP's Index.ets 174export function add(a:number, b:number):number { 175 let c = a + b; 176 console.log('DynamicImport I am a HSP, %d + %d = %d', a, b, c); 177 return c; 178} 179``` 180```typescript 181// HAP's Index.ets 182let packageName = 'myHsp'; 183import(packageName).then((ns:ESObject) => { 184 console.log(ns.add(3, 5)); 185}); 186``` 187```json5 188// HAP's oh-package.json5 189"dependencies": { 190 "myHsp": "file:../myHsp" 191} 192``` 193```json5 194// HAP's build-profile.json5 195"buildOption": { 196 "arkOptions": { 197 "runtimeOnly": { 198 "packages": [ 199 "myHsp" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 200 ] 201 } 202 } 203} 204``` 205 206- **HAP变量动态import远程HAR模块名** 207 208```typescript 209// HAP's Index.ets 210let packageName = '@ohos/crypto-js'; 211import(packageName).then((ns:ESObject) => { 212 console.log('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456)); 213}); 214``` 215```json5 216// HAP's oh-package.json5 217"dependencies": { 218 "@ohos/crypto-js": "2.0.3-rc.0" 219} 220``` 221```json5 222// HAP's build-profile.json5 223"buildOption": { 224 "arkOptions": { 225 "runtimeOnly": { 226 "packages": [ 227 "@ohos/crypto-js" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 228 ] 229 } 230 } 231} 232``` 233 234- **HAP变量动态import ohpm包** 235 236```typescript 237// HAP's Index.ets 238let packageName = 'json5'; 239import(packageName).then((ns:ESObject) => { 240 console.log('DynamicImport json5'); 241}); 242``` 243```json5 244// HAP's oh-package.json5 245"dependencies": { 246 "json5": "1.0.2" 247} 248``` 249```json5 250// HAP's build-profile.json5 251"buildOption": { 252 "arkOptions": { 253 "runtimeOnly": { 254 "packages": [ 255 "json5" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 256 ] 257 } 258 } 259} 260``` 261 262- **HAP变量动态import自己的单文件** 263 264```typescript 265// HAP's src/main/ets/Calc.ets 266export function add(a:number, b:number):number { 267 let c = a + b; 268 console.log('DynamicImport I am a HAP, %d + %d = %d', a, b, c); 269 return c; 270} 271``` 272```typescript 273// HAP's Index.ets 274let filePath = './src/main/ets/Calc'; 275import(filePath).then((ns:ESObject) => { 276 console.log(ns.add(3, 5)); 277}); 278``` 279```json5 280// HAP's build-profile.json5 281"buildOption": { 282 "arkOptions": { 283 "runtimeOnly": { 284 "sources": [ 285 "./src/main/ets/Calc.ets" // 仅用于使用变量动态import模块自己单文件场景,静态import或常量动态import无需配置。 286 ] 287 } 288 } 289} 290``` 291 292- **HAP变量动态import自己的Native库** 293 294```typescript 295// libnativeapi.so's index.d.ts 296export const add: (a:number, b:number) => number; 297``` 298```typescript 299// HAP's Index.ets 300let soName = 'libnativeapi.so'; 301import(soName).then((ns:ESObject) => { 302 console.log('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3)); 303}); 304``` 305```json5 306// HAP's oh-package.json5 307"dependencies": { 308 "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi" 309} 310``` 311```json5 312// HAP's build-profile.json5 313"buildOption": { 314 "arkOptions": { 315 "runtimeOnly": { 316 "packages": [ 317 "libnativeapi.so" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 318 ] 319 } 320 } 321} 322``` 323 324- **HAP变量动态import加载API** 325 326```typescript 327// HAP's Index.ets 328let packageName = '@system.app'; 329import(packageName).then((ns:ESObject) => { ns.default.terminate(); }); 330packageName = '@system.router'; 331import(packageName).then((ns:ESObject) => { ns.default.clear(); }); 332packageName = '@ohos.curves'; 333import(packageName).then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); }); 334packageName = '@ohos.matrix4'; 335import(packageName).then((ns:ESObject) => { ns.default.identity(); }); 336packageName = '@ohos.hilog'; 337import(packageName).then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); }); 338``` 339变量动态import加载API时无需配置runtimeOnly。 340 341### HAR模块间动态import依赖解耦 342当应用包含多个HAR包,且HAR包之间依赖关系比较复杂。在IDE中配置依赖关系时,可能会形成循环依赖。这时,如果HAR之间的依赖关系中仅有变量动态import,可以将HAR包之间直接依赖关系转移到HAP/HSP中配置,HAR包之间无需配置依赖关系,从而达到HAR包间依赖解耦的目的。如下示意图: 343 344 345 346HAR之间依赖关系转移到HAP/HSP后: 347 348 349 350转移依赖时,dependencies和runtimeOnly依赖配置要同时转移。下面以实例进行说明。 351 3521. **使用实例** 353 354下面的实例HAP变量动态import HAR包har1,har1变量动态import另一个HAR包har2。 355 356```json5 357// HAP's oh-package.json5 358"dependencies": { 359 "har1": "file:../har1" 360} 361``` 362```json5 363// HAP's build-profile.json5 364"buildOption": { 365 "arkOptions": { 366 "runtimeOnly": { 367 "packages": [ 368 "har1" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 369 ] 370 } 371 } 372} 373``` 374```typescript 375// HAP's Index.ets 376let harName = 'har1'; 377import(harName).then((ns:ESObject) => { 378 console.log('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5)); 379}); 380``` 381```json5 382// har1's oh-package.json5 383"dependencies": { 384 "har2": "file:../har2" 385} 386``` 387```json5 388// har1's build-profile.json5 389"buildOption": { 390 "arkOptions": { 391 "runtimeOnly": { 392 "packages": [ 393 "har2" // 仅用于使用变量动态import其他模块名场景,静态import或常量动态import无需配置。 394 ] 395 } 396 } 397} 398``` 399```typescript 400// har1's Index.ets 401export { addHar1 } from './src/main/ets/utils/Calc' 402``` 403```typescript 404// har1's src/main/ets/utils/Calc.ets 405export function addHar1(a:number, b:number):number { 406 let c = a + b; 407 console.log('DynamicImport I am har1, %d + %d = %d', a, b, c); 408 409 let harName = 'har2'; 410 import(harName).then((ns:ESObject) => { 411 console.log('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5)); 412 }); 413 return c; 414} 415``` 416```typescript 417// har2's Index.ets 418export { addHar2 } from './src/main/ets/utils/Calc' 419``` 420```typescript 421// har2's src/main/ets/utils/Calc.ets 422export function addHar2(a:number, b:number):number { 423 let c = a + b; 424 console.log('DynamicImport I am har2, %d + %d = %d', a, b, c); 425 return c; 426} 427``` 428 429har1对har2的依赖dependencies和runtimeOnly配置转移到HAP中,har1不需要配置对har2的dependencies和runtimeOnly配置: 430 431```json5 432// HAP's oh-package.json5 433"dependencies": { 434 "har1": "file:../har1", 435 "har2": "file:../har2" 436} 437``` 438```json5 439// HAP's build-profile.json5 440"buildOption": { 441 "arkOptions": { 442 "runtimeOnly": { 443 "packages": [ 444 "har1", 445 "har2" 446 ] 447 } 448 } 449} 450``` 451```typescript 452// HAP's Index.ets 453let harName = 'har1'; 454import(harName).then((ns:ESObject) => { 455 console.log('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5)); 456}); 457``` 458```typescript 459// har1's Index.ets 460export { addHar1 } from './src/main/ets/utils/Calc' 461``` 462```typescript 463// har1's src/main/ets/utils/Calc.ets 464export function addHar1(a:number, b:number):number { 465 let c = a + b; 466 console.log('DynamicImport I am har1, %d + %d = %d', a, b, c); 467 468 let harName = 'har2'; 469 import(harName).then((ns:ESObject) => { 470 console.log('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5)); 471 }); 472 return c; 473} 474``` 475```typescript 476// har2's Index.ets 477export { addHar2 } from './src/main/ets/utils/Calc' 478``` 479```typescript 480// har2's src/main/ets/utils/Calc.ets 481export function addHar2(a:number, b:number):number { 482 let c = a + b; 483 console.log('DynamicImport I am har2, %d + %d = %d', a, b, c); 484 return c; 485} 486``` 487 4882. **使用限制** 489 490- 被转移依赖的HAR之间只能通过变量动态import,不能有静态import或常量动态import。 491- HSP不支持转移依赖 492 即: 493 HAP->HSP1->HSP2->HSP3,这里的HSP2和HSP3不能转移到HAP上面。 494 495- 转移依赖的整个链路上只能有HAR,不能跨越HSP转移。 496 即: 497 HAP->HAR1->HAR2->HSP->HAR3->HAR4 498 HAR1对HAR2的依赖可以转移到HAP上,HAR3对HAR4的依赖可以转移到HSP上,但是,不能将HAR3或HAR4转移到HAP上。 499