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