• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ArkTS语言模块化加载异常常见问题
2
3
4##  ArkTS 应用运行时出现模块化加载相关的异常报错提示,可能导致报错原因以及解决方法
5### "Cannot find dynamic-import module 'xxxx'"
6
7报错表示当前加载的模块未被编译到当前应用包内
8
9**报错原因:**
10
11通过动态加载传入表达式作为入参时,模块路径参数书写有误。
12``` typescript
13  import(module).then(m=>{m.foo();}).catch((e: Error)=>{console.info(e.message)});
14```
15
16**定位方法:**
17
18将待加载module路径信息打印出来,评估模块路径是否计算有误。
19
20### "Cannot find module 'xxxx' , which is application Entry Point"
21报错表示拉起应用abc时,执行入口文件查找失败
22
23**报错原因:**
24
25应用拉起时,应用入口文件模块查找失败。
26
27**定位方法:**
28
29(1) 打开应用工程级编译构建文件: entry > src/main/module.json5
30
31([OpenHarmony工程管理介绍](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V3/ohos-project-overview-0000001218440650-V3))
32module.json5部分参数示例如下:
33```
34{
35  "module": {
36    "name": "entry",
37    "type": "entry",
38    ...
39    "abilities": [
40      {
41        "name": "EntryAbility", // 模块名称
42        "srcEntry": "./ets/entryability/EntryAbility.ts",  // 标明src目录相对工程根目录的相对路径
43        "description": "$string:EntryAbility_desc",
44        "icon": "$media:icon",
45        "label": "$string:EntryAbility_label",
46        "startWindowIcon": "$media:icon",
47        "startWindowBackground": "$color:start_window_background",
48        "exported": true,
49        "skills": [
50          {
51            "entities": [
52              "entity.system.home"
53            ],
54            "actions": [
55              "action.system.home"
56            ]
57          }
58        ]
59      }
60    ]
61  }
62}
63```
64(2) 其中,"abilities":"srcEntry" 参数标记了该应用拉起的入口文件。如报错入口文件加载失败,请检查module.json5内的"srcEntry"参数是否书写正确。
65
66### "No export named 'xxxx' which exported by 'xxxx'"
67报错表示加载应用hap或har包内so时,该模块内未查找到特定对象
68
69**报错原因:**
70
71ets在模块化静态编译阶段,会预解析模块间依赖关系。ets文件内的导入变量名书写错误时,ide编译器与应用编译阶段均会报错提示。但目前对于应用内native C++模块的依赖关系检测会在运行阶段。
72
73**定位方法:**
74
75检查应用内so是否存在报错提示的导出变量,与加载应用内so导入变量处进行比较,不一致则适配修改。
76
77
78## ArkTS/Ts/Js加载so失败,表现行为是怎么样的
79
80加载so失败后,不显式抛出加载失败的js异常。开发者可以通过导出对象是否为undefined判断so的加载状态。
81
82**加载失败具体表现**
83
84| 加载类型 | ts/js模块 | 系统库so或应用so |
85| -------- | -------- | -------- |
86| 静态加载 | 虚拟机自动抛出异常,进程退出 | 无异常抛出,加载到的对象为undefined |
87| 动态加载 | 不主动抛出异常,走到reject分支,开发者可以调用catch方法来捕获这个错误 | 不主动抛出异常,依然进入resolve分支,开发者可以在resolve分支中检查模块导出变量是否为undefined |
88
89**示例1:系统库so或应用so静态加载失败**
90
91```
92import testNapi from 'libentry.so'
93
94if (testNapi == undefined) {
95  console.error('load libentry.so failed.');
96}
97```
98
99执行结果
100```
101load libentry.so failed.
102```
103
104**示例2:系统库so或应用so动态加载失败**
105
106```
107import('libentry.so')
108  .then(m => {
109    if (typeof m.default === 'undefined') {
110      console.warn(`load libentry.so failed.`);
111    } else {
112      console.info('load libentry.so success:', m);
113    }
114    return m;
115  })
116  .catch((e: Error) => {
117    console.error('load libentry.so error:', e);
118  });
119```
120
121执行结果
122```
123load libentry.so failed.
124```
125
126**so加载失败可能的原因、定位方式以及解决方法**
127
128参考([Node-API常见问题](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/napi/use-napi-faqs.md))文档
129
130
131## 模块间循环依赖导致运行时未初始化异常问题定位
132### 循环依赖问题定位方法
133报错xxx is not initialized不一定是循环依赖问题,需要打开开关进一步确认:
134```
135打开option: hdc shell param set persist.ark.properties 0x2000105c
136打开后必须重启机器:hdc shell reboot
137如果需要关闭option: hdc shell param set persist.ark.properties 0x0000105c
138关闭后必须重启机器:hdc shell reboot
139```
140开关打开后复现报错就是第一现场报错,解决即可。如果报错仍是xxx is not initialized, 就是文件存在循环依赖。
141
142### 循环依赖原理
1431. 模块加载顺序:
144根据ECMA规范,模块的执行顺序是深度遍历加载。
145假设应用存在加载链路A->B->C,那么ArkTs模块化会先执行C文件,再执行B文件,最后执行A文件,执行顺序为C->B->A。
1462. 循环依赖:
147如果应用存在加载链路A->B->A,根据深度遍历执行顺序,执行流程会先标记A的状态为加载中,然后去加载B,标记B的状态为加载中,然后去加载A,由于A文件已经标记加载中,根据规范定义,识别到加载中模块会直接返回,就会先执行B文件。
148**为什么有的循环依赖没有影响,有的就会产生crash?**
149由上面的叙述可知,B文件虽然依赖A文件变量,但是B文件先执行,如果B文件导入的A文件变量没有在全局或者类静态中被使用,B文件就会正常执行。如果B文件在全局或者实例化某个类等其他方法,导致文件执行时就会用到A的变量,就会产生xxx is not initialized的crash,即循环依赖导致变量未被初始化。
150
151示例:
152``` typescript
153// A.ets
154import { Animal } from './B'
155export let a = "this is A";
156export function A() {
157  return new Animal;
158}
159
160// ---------------------
161
162// B.ets
163import { a } from './A'
164export class Animal {
165  static {
166    console.log("this is in class");
167    let str = a; // 报错信息:a is not initialized
168  }
169}
170```
171**正例**
172
173``` typescript
174// B.ets
175import { a } from './A'
176export class Animal {
177  static {
178    console.log("this is in class");
179  }
180  str = a;  // 修改点
181}
182```
183
184### 循环依赖的解决方法:
185[安全规则@security/no-cycle](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide_no-cycle-V5)
186
187
188##  ArkTS 符号未初始化报错场景示例
189
190ArkTS语言规范是基于ECMAScript规范的子集,根据语言规范,当访问一个还未完成初始化的符号时,运行时会抛出异常,为便于开发者定位解决源码问题,提供以下常见报错场景示例用于参考。
191
192### const/let声明前访问
193
194``` typescript
195console.log(a); // 报错信息:Variable 'a' is used before being assigned.
196console.log(b); // 报错信息:Variable 'b' is used before being assigned.
197
198let a = '1';
199const b = '2';
200```
201**正例**
202
203```
204let a = '1';
205const b = '2';
206
207console.log(a);
208console.log(b);
209```
210
211
212### class声明前实例化
213
214``` typescript
215let a = new A(); // 报错信息:Class 'A' used before its declaration.
216
217class A {}
218```
219
220**正例**
221
222```
223class A {}
224
225let a = new A();
226```
227
228### class声明前访问其静态属性
229
230``` typescript
231let a = A.a; // 报错信息:Class 'A' used before its declaration.
232
233class A {
234  static a = 1;
235}
236```
237
238**正例**
239
240```
241class A {
242  static a = 1;
243}
244
245let a = A.a;
246```
247
248### 在函数中访问未提前声明的let和const变量
249
250``` typescript
251foo(); // 报错信息:Error message:a is not initialized
252
253let a = 1;
254const b = 2;
255
256function foo() {
257  let v = a + b;
258}
259```
260
261**正例**
262
263```
264let a = 1;
265const b = 2;
266
267function foo() {
268  let v = a + b;
269}
270
271foo();
272```
273
274### 在函数中访问未提前声明的类的静态属性
275
276``` typescript
277foo(); // 报错信息:Error message:A is not initialized
278
279class A {
280  static a = 1;
281}
282
283function foo() {
284  let v = A.a;
285  let w = new A();
286}
287```
288
289**正例**
290
291```
292class A {
293  static a = 1;
294}
295
296function foo() {
297  let v = A.a;
298  let w = new A();
299}
300
301foo();
302```
303
304### 模块间循环依赖 - const/let
305
306``` typescript
307// module1.ets
308import { a, b } from './module2'
309
310export let i = 1;
311export let m = a;
312export const j = 2;
313export const n = b;
314
315// ---------------------
316
317// module2.ets
318import { i, j } from './module1'
319
320export let a = i; // 报错信息:Error message:i is not initialized
321export const b = j; // 报错信息:Error message:j is not initialized
322```
323
324**解决方法**
325
326详见[循环依赖的解决方法](#循环依赖的解决方法)
327
328
329### 模块间循环依赖 - class
330
331``` typescript
332// class1.ets
333import { b } from './class2'
334
335export class A {
336  static a = b;
337}
338
339// ---------------------
340
341// class2.ets
342import { A } from './class1'
343export let b = 1;
344
345const i = A.a; // 报错信息:Error message:A is not initialized
346const j = new A(); // 报错信息:Error message:A is not initialized
347```
348
349**解决方法**
350
351详见[循环依赖的解决方法](#循环依赖的解决方法)