• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Dynamic Import
2
3Dynamic imports support conditional loading and partial reflection, enhancing page load speed. It allows loading HSP modules, HAR modules, ohpm packages, and native libraries. It also enables module decoupling when only variables are dynamically imported between HAR modules.
4
5## When to Use
6Dynamic imports can be used in application development when you need to import modules conditionally or on-demand, as an alternative to [static import](../quick-start/introduction-to-arkts.md#static-import). The following are some cases where you might want to use dynamic import:
7
8* Statically imported modules significantly slow the loading of your code and are rarely used or not immediately needed.
9* Statically imported modules consume a large amount of system memory and are rarely used.
10* The imported module does not exist at load time and needs to be fetched asynchronously.
11* The import specifier string needs to be constructed dynamically. Static imports only support static specifiers.
12* The imported module has side effects (which can be understood as code that runs directly in the module) that are only needed when certain conditions are triggered.
13
14## Service Expansion Scenarios
15As aforementioned, in addition to conditional loading, dynamic imports can implement partial reflection. In the following example, an HAP dynamically imports a HAR package (**harlibrary**) and calls its static member function **staticAdd()**, instance member function **instanceAdd()**, and global function **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);  // Call the static member function staticAdd().
55  let calc:ESObject = new ns.Calc();  // Instantiate the class Calc.
56  calc.instanceAdd(10, 11);  // Call the instance member function instanceAdd().
57  ns.addHarlibrary(6, 7);  // Call the global function addHarlibrary().
58
59  // Reflection using class, member function, and method names as strings.
60  let className = 'Calc';
61  let methodName = 'instanceAdd';
62  let staticMethod = 'staticAdd';
63  let functionName = 'addHarlibrary';
64  ns[className][staticMethod](12, 13);  // Call the static member function staticAdd().
65  let calc1:ESObject = new ns[className]();  // Instantiate the class Calc.
66  calc1[methodName](14, 15);  // Call the instance member function instanceAdd().
67  ns[functionName](16, 17);  // Call the global function addHarlibrary().
68});
69```
70
71## Implementation of Dynamic Import
72A dynamic import expression accepts a constant or variable as its argument.
73The following table lists the specifications of dynamic import.
74
75| Scenario| Module Specifier            | Description                                                    |
76| :------------- | :----------------------------- | :------------------------------------------------------- |
77| Local module  | File path      | The path must start with **./** or **../**.                                   |
78| Local module  | HSP module name          | -                                                        |
79| Local module  | HSP module file path    | Currently, dynamic imports with constants are supported, but not ones with variables.|
80| Local module  | HAR module name          | -                                                        |
81| Local module  | HAR module file path    | Currently, dynamic imports with constants are supported, but not ones with variables.|
82| Remote module        | Remote HAR module name       | -                                                        |
83| Remote module        | ohpm package name           | -                                                        |
84| API            | @system.*          | -                                                        |
85| API            | @ohos.*            | -                                                        |
86| API            | @arkui-x.*         | -                                                        |
87| Native library module  | libNativeLibrary.so| -                                                        |
88
89>**NOTE**
90>
91> 1. Module names used in all imports are the aliases defined under **dependencies** in the **oh-package.json5** file.
92> 2. It is recommended that the alias configured under **dependencies** be the same as the values of **moduleName** and **packageName**, both of which indicate the name of the module to import. **moduleName** is set in the **module.json5** file of the module, and **packageName** is set in the **oh-package.json5** file.
93> 3. Importing a module by name is importing the module's entry file, generally **index.ets/ts**.
94
95## Key Points in Dynamic Import Implementation
96
97### Dynamic Imports with Constant Expressions
98
99Dynamic imports with constant expressions refer to scenarios where the input to **import** is a constant. The following examples show how to use this type of dynamic import to import a module or API into a HAP module.
100
101Note: In the examples, the paths, such as the path to **Index.ets**, are set based on the current DevEco Studio module configuration and are subject to change.
102
103- **HAP dynamically imports a HAR module using a constant module name**
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 dynamically imports a HAR module using a constant file path**
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 dynamically imports an HSP module using a constant module name**
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 dynamically imports an HSP module using a constant file path**
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 dynamically imports a remote HAR module using a constant module name**
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 dynamically imports an ohpm package using a constant**
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 dynamically imports its own single file using a constant**
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 dynamically imports its own native library using a constant**
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 dynamically imports APIs using a constant**
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### Dynamic Imports with Variable Expressions
286
287In DevEco Studio, module dependencies are configured through **dependencies** in the **oh-package.json5** file. By default, all modules listed under **dependencies** are installed (for local modules) or downloaded (for remote modules), but are not built. During a HAP/HSP build, the dependency relationship is searched from the entry file (generally **Index.ets/ts**), and only the dependencies found are added to the build.
288During compilation, static imports and dynamic imports with constant expressions can be identified and parsed by the packaging tool rollup and its plug-ins. This means that the related dependencies can be added to the dependency tree, participate in the build process, and finally generate Ark bytecode. However, dynamic imports with variable expressions cannot be resolved at compile-time because their values may depend on runtime calculations or external inputs. To add these dependencies to the build process, add **runtimeOnly** under **buildOption** and set it to the actual module name or file path pertaining to the variable.
289
290**Schema configuration format of the runtimeOnly field**
291
292If you are using dynamic imports with variable expressions to import modules or files, but not APIs, you need to add the **runtimeOnly** field under **buildOption** in the **build-profile.json5** file of the HAP/HSP/HAR module. The following are some examples.
293
294```typescript
295// Dynamically import a module based on the module name myHar.
296let harName = 'myHar';
297import(harName).then(......);
298
299// Dynamically import a file of the module itself based on the file path src/main/ets/index.ets.
300let filePath = './Calc';
301import(filePath).then(......);
302```
303
304The corresponding **runtimeOnly** configuration is as follows:
305
306```typescript
307"buildOption": {
308  "arkOptions": {
309    "runtimeOnly": {
310      "packages": [ "myHar" ]  // Set the name of the module to dynamically import. It must be the same as the one specified under dependencies.
311      "sources": ["./src/main/ets/utils/Calc.ets"] // Set the path of the file to dynamically import. The path is relative to the build-profile.json5 file of the module.
312    }
313  }
314}
315```
316
317**packages** of **runtimeOnly**: name of the module to dynamically import. It must be the same as the one specified under **dependencies**.
318**sources** of **runtimeOnly**: path of the file to dynamically import. The path is relative to the **build-profile.json5** file of the module.
319
320**Usage Examples**
321
322- **HAP dynamically imports a HAR module using a variable module name**
323
324  ```typescript
325  // HAR's Index.ets
326  export function add(a:number, b:number):number {
327    let c = a + b;
328    console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c);
329    return c;
330  }
331  ```
332  ```typescript
333  // HAP's src/main/ets/pages/Index.ets
334  let packageName = 'myHar';
335  import(packageName).then((ns:ESObject) => {
336    console.info(ns.add(3, 5));
337  });
338  ```
339  ```json5
340  // HAP's oh-package.json5
341  "dependencies": {
342    "myHar": "file:../myHar"
343  }
344  ```
345  ```json5
346  // HAP's build-profile.json5
347  "buildOption": {
348    "arkOptions": {
349      "runtimeOnly": {
350        "packages": [
351          "myHar" // Applicable only when a variable is used to dynamically import a module or file.
352        ]
353      }
354    }
355  }
356  ```
357
358- **HAP dynamically imports an HSP module using a variable module name**
359
360  ```typescript
361  // HSP's Index.ets
362  export function add(a:number, b:number):number {
363    let c = a + b;
364    console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c);
365    return c;
366  }
367  ```
368  ```typescript
369  // HAP's src/main/ets/pages/Index.ets
370  let packageName = 'myHsp';
371  import(packageName).then((ns:ESObject) => {
372    console.info(ns.add(3, 5));
373  });
374  ```
375  ```json5
376  // HAP's oh-package.json5
377  "dependencies": {
378    "myHsp": "file:../myHsp"
379  }
380  ```
381  ```json5
382  // HAP's build-profile.json5
383  "buildOption": {
384    "arkOptions": {
385      "runtimeOnly": {
386        "packages": [
387          "myHsp"  // Applicable only when a variable is used to dynamically import a module.
388        ]
389      }
390    }
391  }
392  ```
393
394- **HAP dynamically imports a remote HAR module using a variable module name**
395
396  ```typescript
397  // HAP's src/main/ets/pages/Index.ets
398  let packageName = '@ohos/crypto-js';
399  import(packageName).then((ns:ESObject) => {
400    console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456));
401  });
402  ```
403  ```json5
404  // HAP's oh-package.json5
405  "dependencies": {
406    "@ohos/crypto-js": "2.0.3-rc.0"
407  }
408  ```
409  ```json5
410  // HAP's build-profile.json5
411  "buildOption": {
412    "arkOptions": {
413      "runtimeOnly": {
414        "packages": [
415          "@ohos/crypto-js"  // Applicable only when a variable is used to dynamically import a module.
416        ]
417      }
418    }
419  }
420  ```
421
422- **HAP dynamically imports an ohpm package using a variable**
423
424  ```typescript
425  // HAP's src/main/ets/pages/Index.ets
426  let packageName = 'json5';
427  import(packageName).then((ns:ESObject) => {
428    console.info('DynamicImport json5');
429  });
430  ```
431  ```json5
432  // HAP's oh-package.json5
433  "dependencies": {
434    "json5": "1.0.2"
435  }
436  ```
437  ```json5
438  // HAP's build-profile.json5
439  "buildOption": {
440    "arkOptions": {
441      "runtimeOnly": {
442        "packages": [
443          "json5"  // Applicable only when a variable is used to dynamically import a module.
444        ]
445      }
446    }
447  }
448  ```
449
450- **HAP dynamically imports its own single file using a variable**
451
452  ```typescript
453  // HAP's src/main/ets/Calc.ets
454  export function add(a:number, b:number):number {
455    let c = a + b;
456    console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c);
457    return c;
458  }
459  ```
460  ```typescript
461  // HAP's src/main/ets/pages/Index.ets
462  let filePath = '../Calc';
463  import(filePath).then((ns:ESObject) => {
464    console.info(ns.add(3, 5));
465  });
466  ```
467  ```json5
468  // HAP's build-profile.json5
469  "buildOption": {
470    "arkOptions": {
471      "runtimeOnly": {
472        "sources": [
473          "./src/main/ets/Calc.ets"  // Applicable only when a variable is used to dynamically import a file of the module itself.
474        ]
475      }
476    }
477  }
478  ```
479
480- **HAP dynamically imports its own native library using a variable**
481
482  ```typescript
483  // libnativeapi.so's index.d.ts
484  export const add: (a:number, b:number) => number;
485  ```
486  ```typescript
487  // HAP's src/main/ets/pages/Index.ets
488  let soName = 'libnativeapi.so';
489  import(soName).then((ns:ESObject) => {
490    console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3));
491  });
492  ```
493  ```json5
494  // HAP's oh-package.json5
495  "dependencies": {
496    "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi"
497  }
498  ```
499  ```json5
500  // HAP's build-profile.json5
501  "buildOption": {
502    "arkOptions": {
503      "runtimeOnly": {
504        "packages": [
505          "libnativeapi.so"  // Applicable only when a variable is used to dynamically import a module.
506        ]
507      }
508    }
509  }
510  ```
511
512- **HAP dynamically imports APIs using a variable**
513
514  ```typescript
515  // HAP's src/main/ets/pages/Index.ets
516  let packageName = '@system.app';
517  import(packageName).then((ns:ESObject) => { ns.default.terminate(); });
518  packageName = '@system.router';
519  import(packageName).then((ns:ESObject) => { ns.default.clear(); });
520  packageName = '@ohos.curves';
521  import(packageName).then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); });
522  packageName = '@ohos.matrix4';
523  import(packageName).then((ns:ESObject) => { ns.default.identity(); });
524  packageName = '@ohos.hilog';
525  import(packageName).then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); });
526  ```
527You do not need to set **runtimeOnly** when dynamically importing APIs with variables.
528
529### Decoupling Dynamic Imports Between HAR Modules
530When an application contains multiple HAR packages with complex dependency relationships, circular dependencies may occur when configuring dependencies in DevEco Studio. If the dependencies between HAR packages are only through dynamic imports with variable expressions, the direct dependency relationships between HAR packages can be transferred to the HAP/HSP configuration. This decouples the dependencies between HAR packages, as shown in the figure below.
531
532![Circular dependency between HAR packages](figures/dynamicimport1.png)
533
534The figure shows the dependency graph after dependency conversion.
535
536![Dependency between HAR and HAP](figures/dynamicimport2.png)
537
538
539**Constraints**
540- This workaround is only applicable when circular dependencies occur between local HAR packages.
541- The transferred dependencies between HAR packages can only be through dynamic imports with variable expressions, not static imports or dynamic imports with constant expressions.
542- When transferring dependencies, both **dependencies** and **runtimeOnly** configurations must be transferred simultaneously.
543- HSP does not support transferring dependencies. For example, in the chain HAP -> HSP1 -> HSP2 -> HSP3, dependency between HSP2 and HSP3 cannot be transferred to HAP.
544- The entire chain of transferred dependencies must consist only of HAR packages. Dependencies cannot be transferred across HSP packages. An incorrect use is as follows: HAP -> HAR1 -> HAR2 -> HSP -> HAR3 -> HAR4.
545
546  The dependency of HAR1 on HAR2 can be transferred to a HAP, and the dependency of HAR3 on HAR4 can be transferred to an HSP. However, HAR3 or HAR4 cannot be transferred to the HAP.
547
548
549**Usage Examples**
550
551In the following example, HAP dynamically imports HAR package har1, and har1 dynamically imports another HAR package har2 using variables.
552
553```json5
554// HAP's oh-package.json5
555"dependencies": {
556  "har1": "file:../har1"
557}
558```
559```json5
560// HAP's build-profile.json5
561"buildOption": {
562  "arkOptions": {
563    "runtimeOnly": {
564      "packages": [
565        "har1"  // Applicable only when a variable is used to dynamically import a module.
566      ]
567    }
568  }
569}
570```
571```typescript
572// HAP's src/main/ets/pages/Index.ets
573let harName = 'har1';
574import(harName).then((ns:ESObject) => {
575  console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5));
576});
577```
578```json5
579// har1's oh-package.json5
580"dependencies": {
581  "har2": "file:../har2"
582}
583```
584```json5
585// har1's build-profile.json5
586"buildOption": {
587  "arkOptions": {
588    "runtimeOnly": {
589      "packages": [
590        "har2"  // Applicable only when a variable is used to dynamically import a module.
591      ]
592    }
593  }
594}
595```
596```typescript
597// har1's Index.ets
598export { addHar1 } from './src/main/ets/utils/Calc'
599```
600```typescript
601// har1's src/main/ets/utils/Calc.ets
602export function addHar1(a:number, b:number):number {
603  let c = a + b;
604  console.info('DynamicImport I am har1, %d + %d = %d', a, b, c);
605
606  let harName = 'har2';
607  import(harName).then((ns:ESObject) => {
608    console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5));
609  });
610  return c;
611}
612```
613```typescript
614// har2's Index.ets
615export { addHar2 } from './src/main/ets/utils/Calc'
616```
617```typescript
618// har2's src/main/ets/utils/Calc.ets
619export function addHar2(a:number, b:number):number {
620  let c = a + b;
621  console.info('DynamicImport I am har2, %d + %d = %d', a, b, c);
622  return c;
623}
624```
625
626The **dependencies** and **runtimeOnly** configurations of har1 on har2 are transferred to the HAP. In this way, the **dependencies** and **runtimeOnly** configuration of har2 do not need to be configured for har1.
627
628```json5
629// HAP's oh-package.json5
630"dependencies": {
631  "har1": "file:../har1",
632  "har2": "file:../har2"
633}
634```
635```json5
636// HAP's build-profile.json5
637"buildOption": {
638  "arkOptions": {
639    "runtimeOnly": {
640      "packages": [
641        "har1",
642        "har2"
643      ]
644    }
645  }
646}
647```
648```typescript
649// HAP's src/main/ets/pages/Index.ets
650let harName = 'har1';
651import(harName).then((ns:ESObject) => {
652  console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5));
653});
654```
655```typescript
656// har1's Index.ets
657export { addHar1 } from './src/main/ets/utils/Calc'
658```
659```typescript
660// har1's src/main/ets/utils/Calc.ets
661export function addHar1(a:number, b:number):number {
662  let c = a + b;
663  console.info('DynamicImport I am har1, %d + %d = %d', a, b, c);
664
665  let harName = 'har2';
666  import(harName).then((ns:ESObject) => {
667    console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5));
668  });
669  return c;
670}
671```
672```typescript
673// har2's Index.ets
674export { addHar2 } from './src/main/ets/utils/Calc'
675```
676```typescript
677// har2's src/main/ets/utils/Calc.ets
678export function addHar2(a:number, b:number):number {
679  let c = a + b;
680  console.info('DynamicImport I am har2, %d + %d = %d', a, b, c);
681  return c;
682}
683```
684