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/HARmodule.json5中配置的名字,packageName指的是被依赖的HSP/HARoh-package.json5中配置的名字。
933. import一个模块名,实际的行为是import该模块的入口文件,一般为index.ets/ts94
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
292HAP/HSP/HARbuild-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![变量动态import HAR包形成循环依赖](figures/dynamicimport1.png)
534
535HAR之间依赖关系转移到HAP/HSP后:
536
537![变量动态import HAR包依赖转移到HAP](figures/dynamicimport2.png)
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