• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/HARmodule.json5中配置的名字,packageName指的是被依赖的HSP/HARoh-package.json5中配置的名字。
93> 3.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  // 可使用 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
298HAP/HSP/HARbuild-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![变量动态import HAR包形成循环依赖](figures/dynamicimport1.png)
539
540HAR之间的依赖关系转移至HAP/HSP后:
541
542![变量动态import HAR包依赖转移到HAP](figures/dynamicimport2.png)
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