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