• 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 the static member function **staticAdd()**, instance member function **instanceAdd()**, and global function **addHarLibrary()** of the **Calc** class.
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 method 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    | -                                                        |
80| Local module  | HAR module name          | -                                                        |
81| Local module  | HAR module file path    | -                                                        |
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 APIs of other modules 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  // You can use await to process dynamic import. (It must be used in an async function.)
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 dynamically imports a HAR module using a constant file path**
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 dynamically imports an HSP module using a constant module name**
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 dynamically imports an HSP module using a constant file path**
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 dynamically imports a remote HAR module using a constant module name**
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 dynamically imports an ohpm package using a constant**
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 dynamically imports its own single file using a constant**
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 dynamically imports its own native library using a constant**
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 dynamically imports APIs using a constant**
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### Dynamic Imports with Variable Expressions
292
293In 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.
294During 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.
295
296**Schema configuration format of the runtimeOnly field**
297
298If 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.
299
300```typescript
301// Dynamically import a module based on the module name myhar.
302let harName = 'myhar';
303import(harName).then((obj: ESObject) => {
304    console.info('DynamicImport I am a har');
305}
306
307// Dynamically import a file of the module itself based on the file path src/main/ets/index.ets.
308let filePath = './Calc';
309import(filePath).then((obj: ESObject) => {
310    console.info('DynamicImport I am a file');
311}
312```
313
314The corresponding **runtimeOnly** configuration is as follows:
315
316```json
317"buildOption": {
318  "arkOptions": {
319    "runtimeOnly": {
320      "packages": [ "myhar" ]  // Set the name of the module to dynamically import. It must be the same as the one specified under dependencies.
321      "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.
322    }
323  }
324}
325```
326
327**packages** of **runtimeOnly**: name of the module to dynamically import. It must be the same as the one specified under **dependencies**.
328**sources** of **runtimeOnly**: path of the file to dynamically import. The path is relative to the **build-profile.json5** file of the module.
329
330**Usage Examples**
331
332- **HAP dynamically imports a HAR module using a variable module name**
333
334  ```typescript
335  // HAR's Index.ets
336  export function add(a:number, b:number):number {
337    let c = a + b;
338    console.info('DynamicImport I am a HAR, %d + %d = %d', a, b, c);
339    return c;
340  }
341  ```
342  ```typescript
343  // HAP's src/main/ets/pages/Index.ets
344  let packageName = 'myhar';
345  import(packageName).then((ns:ESObject) => {
346    console.info(ns.add(3, 5));
347  });
348  ```
349  ```json5
350  // HAP's oh-package.json5
351  "dependencies": {
352    "myhar": "file:../myhar"
353  }
354  ```
355  ```json5
356  // HAP's build-profile.json5
357  "buildOption": {
358    "arkOptions": {
359      "runtimeOnly": {
360        "packages": [
361          "myhar"  // Applicable only when a variable is used to dynamically import a module.
362        ]
363      }
364    }
365  }
366  ```
367
368- **HAP dynamically imports an HSP module using a variable module name**
369
370  ```typescript
371  // HSP's Index.ets
372  export function add(a:number, b:number):number {
373    let c = a + b;
374    console.info('DynamicImport I am a HSP, %d + %d = %d', a, b, c);
375    return c;
376  }
377  ```
378  ```typescript
379  // HAP's src/main/ets/pages/Index.ets
380  let packageName = 'myHsp';
381  import(packageName).then((ns:ESObject) => {
382    console.info(ns.add(3, 5));
383  });
384  ```
385  ```json5
386  // HAP's oh-package.json5
387  "dependencies": {
388    "myHsp": "file:../myHsp"
389  }
390  ```
391  ```json5
392  // HAP's build-profile.json5
393  "buildOption": {
394    "arkOptions": {
395      "runtimeOnly": {
396        "packages": [
397          "myHsp"  // Applicable only when a variable is used to dynamically import a module.
398        ]
399      }
400    }
401  }
402  ```
403
404- **HAP dynamically imports a remote HAR module using a variable module name**
405
406  ```typescript
407  // HAP's src/main/ets/pages/Index.ets
408  let packageName = '@ohos/crypto-js';
409  import(packageName).then((ns:ESObject) => {
410    console.info('DynamicImport @ohos/crypto-js: ' + ns.CryptoJS.MD5(123456));
411  });
412  ```
413  ```json5
414  // HAP's oh-package.json5
415  "dependencies": {
416    "@ohos/crypto-js": "2.0.3-rc.0"
417  }
418  ```
419  ```json5
420  // HAP's build-profile.json5
421  "buildOption": {
422    "arkOptions": {
423      "runtimeOnly": {
424        "packages": [
425          "@ohos/crypto-js"  // Applicable only when a variable is used to dynamically import a module.
426        ]
427      }
428    }
429  }
430  ```
431
432- **HAP dynamically imports an ohpm package using a variable**
433
434  ```typescript
435  // HAP's src/main/ets/pages/Index.ets
436  let packageName = 'json5';
437  import(packageName).then((ns:ESObject) => {
438    console.info('DynamicImport json5');
439  });
440  ```
441  ```json5
442  // HAP's oh-package.json5
443  "dependencies": {
444    "json5": "1.0.2"
445  }
446  ```
447  ```json5
448  // HAP's build-profile.json5
449  "buildOption": {
450    "arkOptions": {
451      "runtimeOnly": {
452        "packages": [
453          "json5"  // Applicable only when a variable is used to dynamically import a module.
454        ]
455      }
456    }
457  }
458  ```
459
460- **HAP dynamically imports its own single file using a variable**
461
462  ```typescript
463  // HAP's src/main/ets/Calc.ets
464  export function add(a:number, b:number):number {
465    let c = a + b;
466    console.info('DynamicImport I am a HAP, %d + %d = %d', a, b, c);
467    return c;
468  }
469  ```
470  ```typescript
471  // HAP's src/main/ets/pages/Index.ets
472  let filePath = '../Calc';
473  import(filePath).then((ns:ESObject) => {
474    console.info(ns.add(3, 5));
475  });
476  ```
477  ```json5
478  // HAP's build-profile.json5
479  "buildOption": {
480    "arkOptions": {
481      "runtimeOnly": {
482        "sources": [
483          "./src/main/ets/Calc.ets"  // Applicable only when a variable is used to dynamically import a file of the module itself.
484        ]
485      }
486    }
487  }
488  ```
489
490- **HAP dynamically imports its own native library using a variable**
491
492  ```typescript
493  // libnativeapi.so's index.d.ts
494  export const add: (a:number, b:number) => number;
495  ```
496  ```typescript
497  // HAP's src/main/ets/pages/Index.ets
498  let soName = 'libnativeapi.so';
499  import(soName).then((ns:ESObject) => {
500    console.info('DynamicImport libnativeapi.so: ' + ns.default.add(2, 3));
501  });
502  ```
503  ```json5
504  // HAP's oh-package.json5
505  "dependencies": {
506    "libnativeapi.so": "file:./src/main/cpp/types/libnativeapi"
507  }
508  ```
509  ```json5
510  // HAP's build-profile.json5
511  "buildOption": {
512    "arkOptions": {
513      "runtimeOnly": {
514        "packages": [
515          "libnativeapi.so"  // Applicable only when a variable is used to dynamically import a module.
516        ]
517      }
518    }
519  }
520  ```
521
522- **HAP dynamically imports APIs using a variable**
523
524  ```typescript
525  // HAP's src/main/ets/pages/Index.ets
526  let packageName = '@system.app';
527  import(packageName).then((ns:ESObject) => { ns.default.terminate(); });
528  packageName = '@system.router';
529  import(packageName).then((ns:ESObject) => { ns.default.clear(); });
530  packageName = '@ohos.curves';
531  import(packageName).then((ns:ESObject) => { ns.default.springMotion(0.555, 0.75, 0.001); });
532  packageName = '@ohos.matrix4';
533  import(packageName).then((ns:ESObject) => { ns.default.identity(); });
534  packageName = '@ohos.hilog';
535  import(packageName).then((ns:ESObject) => { ns.default.info(0x0000, 'testTag', '%{public}s', 'DynamicImport @ohos.hilog.'); });
536  ```
537You do not need to set **runtimeOnly** when dynamically importing APIs with variables.
538
539### Decoupling Dynamic Imports Between HAR Modules
540When 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.
541
542![Circular dependency between HAR packages](figures/dynamicimport1.png)
543
544The figure shows the dependency graph after dependency conversion.
545
546![Dependency between HAR and HAP](figures/dynamicimport2.png)
547
548
549**Constraints**
550- This workaround is only applicable when circular dependencies occur between local HAR packages.
551- The transferred dependencies between HAR packages can only be through dynamic imports with variable expressions, not static imports or dynamic imports with constant expressions.
552- When transferring dependencies, both **dependencies** and **runtimeOnly** configurations must be transferred simultaneously.
553- 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.
554- The entire chain of transferred dependencies must consist only of HAR packages. Dependencies cannot be transferred across HSP packages. For example, in the chain HAP-> HAR1 -> HAR2 -> HSP -> HAR3 -> HAR4, the dependency of HAR1 on HAR2 can be transferred to HAP, and the dependency of HAR3 on HAR4 can be transferred to HSP. However, HAR3 or HAR4 cannot be transferred to HAP.
555- If there are references to other project modules, remote packages, or integrated HSP packages, you must ensure that the **useNormalizedOHMUrl** configuration is consistent and set to either **true** or **false**. Failure to do so may result in runtime errors such as **Cannot find dynamic-import module library**.
556
557
558**Usage Examples**
559
560In the following example, HAP dynamically imports HAR package har1, and har1 dynamically imports another HAR package har2 using variables.
561
562```json5
563// HAP's oh-package.json5
564"dependencies": {
565  "har1": "file:../har1"
566}
567```
568```json5
569// HAP's build-profile.json5
570"buildOption": {
571  "arkOptions": {
572    "runtimeOnly": {
573      "packages": [
574        "har1"  // Applicable only when a variable is used to dynamically import a module.
575      ]
576    }
577  }
578}
579```
580```typescript
581// HAP's src/main/ets/pages/Index.ets
582let harName = 'har1';
583import(harName).then((ns:ESObject) => {
584  console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5));
585});
586```
587```json5
588// har1's oh-package.json5
589"dependencies": {
590  "har2": "file:../har2"
591}
592```
593```json5
594// har1's build-profile.json5
595"buildOption": {
596  "arkOptions": {
597    "runtimeOnly": {
598      "packages": [
599        "har2"  // Applicable only when a variable is used to dynamically import a module.
600      ]
601    }
602  }
603}
604```
605```typescript
606// har1's Index.ets
607export { addHar1 } from './src/main/ets/utils/Calc'
608```
609```typescript
610// har1's src/main/ets/utils/Calc.ets
611export function addHar1(a:number, b:number):number {
612  let c = a + b;
613  console.info('DynamicImport I am har1, %d + %d = %d', a, b, c);
614
615  let harName = 'har2';
616  import(harName).then((ns:ESObject) => {
617    console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5));
618  });
619  return c;
620}
621```
622```typescript
623// har2's Index.ets
624export { addHar2 } from './src/main/ets/utils/Calc'
625```
626```typescript
627// har2's src/main/ets/utils/Calc.ets
628export function addHar2(a:number, b:number):number {
629  let c = a + b;
630  console.info('DynamicImport I am har2, %d + %d = %d', a, b, c);
631  return c;
632}
633```
634
635The **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.
636
637```json5
638// HAP's oh-package.json5
639"dependencies": {
640  "har1": "file:../har1",
641  "har2": "file:../har2"
642}
643```
644```json5
645// HAP's build-profile.json5
646"buildOption": {
647  "arkOptions": {
648    "runtimeOnly": {
649      "packages": [
650        "har1",
651        "har2"
652      ]
653    }
654  }
655}
656```
657```typescript
658// HAP's src/main/ets/pages/Index.ets
659let harName = 'har1';
660import(harName).then((ns:ESObject) => {
661  console.info('DynamicImport addHar1 4 + 5 = ' + ns.addHar1(4, 5));
662});
663```
664```typescript
665// har1's Index.ets
666export { addHar1 } from './src/main/ets/utils/Calc'
667```
668```typescript
669// har1's src/main/ets/utils/Calc.ets
670export function addHar1(a:number, b:number):number {
671  let c = a + b;
672  console.info('DynamicImport I am har1, %d + %d = %d', a, b, c);
673
674  let harName = 'har2';
675  import(harName).then((ns:ESObject) => {
676    console.info('DynamicImport addHar2 4 + 5 = ' + ns.addHar2(4, 5));
677  });
678  return c;
679}
680```
681```typescript
682// har2's Index.ets
683export { addHar2 } from './src/main/ets/utils/Calc'
684```
685```typescript
686// har2's src/main/ets/utils/Calc.ets
687export function addHar2(a:number, b:number):number {
688  let c = a + b;
689  console.info('DynamicImport I am har2, %d + %d = %d', a, b, c);
690  return c;
691}
692```
693