适用版本 :HarmonyOS 6.1(API 12)及以上
验证环境 :Pura 90 Pro 模拟器(HarmonyOS 6.1.1,API 24)
关键 API :动态
import()表达式、ESObject类型
前言
每一行 import 语句都有代价:Bundle 启动时,运行时需要解析和执行所有静态导入的模块。对于大型工具库(PDF 解析、图表渲染、复杂加密算法),如果写在文件顶部的 import 里,即使用户从未触发相关功能,这些模块也会在每次启动时占用时间和内存。
动态 import() 是解决方案:首次真正需要时才异步加载模块,首屏渲染不受影响。
静态 import vs 动态 import()
typescript
// ── 静态 import(写在文件顶部)──
// Bundle 启动时立即解析和执行,无论是否用到
import { heavyFunction } from '../utils/HeavyUtils'
// ── 动态 import()(写在函数内)──
// 返回 Promise,首次调用时才加载
async function loadOnDemand(): Promise<void> {
const mod: ESObject = await import('../utils/HeavyUtils')
const result = (mod.heavyFunction as Function)(args)
}
| 维度 | 静态 import | 动态 import() |
|---|---|---|
| 加载时机 | Bundle 启动时 | 首次调用时 |
| 首屏影响 | 增加冷启动耗时 | 不影响首屏 |
| 使用方式 | 直接用符号名 | 通过 ESObject 访问 |
| 适用场景 | 核心功能 | 非首屏、大型工具库 |
完整代码
HeavyUtils 模块(被懒加载的模块)
typescript
// utils/HeavyUtils.ets
// 模块级变量:记录模块初始化时刻(模块加载时执行一次)
const MODULE_LOAD_TIME = Date.now()
// 计算 limit 以内质数个数(CPU 密集型,适合懒加载)
export function computeHeavy(limit: number): number {
const sieve: boolean[] = new Array<boolean>(limit + 1).fill(true)
sieve[0] = false
if (limit >= 1) { sieve[1] = false }
for (let i = 2; i * i <= limit; i++) {
if (sieve[i]) {
for (let j = i * i; j <= limit; j += i) { sieve[j] = false }
}
}
let count = 0
for (let k = 0; k <= limit; k++) { if (sieve[k]) { count++ } }
return count
}
// 数字格式化(用逗号分隔千位)
export function formatNumber(n: number): string {
return n.toLocaleString()
}
// 返回模块初始化时刻(用于对比与应用启动的时间差)
export function getModuleLoadTime(): number {
return MODULE_LOAD_TIME
}
主页面:动态加载与缓存复用
typescript
@Entry
@Component
struct LazyImportDemo {
@State dynamicStatus: LoadStatus = 'idle'
@State dynamicResult: string = ''
@State results: DemoResult[] = []
// 已加载的模块缓存(ESObject 接收 import() 结果)
private cachedModule: ESObject = null
private appStartTime: number = Date.now()
private async loadModuleDynamic(): Promise<void> {
// 首次加载后缓存复用
if (this.cachedModule !== null) {
this.dynamicResult = '模块已缓存,直接复用(无需重新加载)'
return
}
this.dynamicStatus = 'loading'
const start = Date.now()
try {
// 路径相对于当前 .ets 文件
const mod: ESObject = await import('../utils/HeavyUtils')
this.cachedModule = mod
// 通过 ESObject 访问导出函数(需要类型断言)
const primeCount: number = (mod.computeHeavy as Function)(100000) as number
const formatted: string = (mod.formatNumber as Function)(primeCount) as string
const loadTime: number = (mod.getModuleLoadTime as Function)() as number
this.dynamicResult =
`动态加载成功!耗时 ${Date.now() - start}ms\n` +
`10万以内质数:${formatted} 个\n` +
`模块初始化时刻:+${loadTime - this.appStartTime}ms(相对应用启动)`
this.dynamicStatus = 'loaded'
} catch (e) {
const err = e as Error
this.dynamicStatus = 'error'
this.dynamicResult = `加载失败:${err.message}`
}
}
}
ESObject 类型注意事项
typescript
// ✅ 正确:用 ESObject 接收 import() 返回值
const mod: ESObject = await import('../utils/HeavyUtils')
// ✅ 正确:通过 as Function 类型断言调用导出函数
const count = (mod.computeHeavy as Function)(100000) as number
// ❌ 错误:直接调用不经类型断言(ArkTS 不允许隐式调用 ESObject)
mod.computeHeavy(100000) // 编译错误
// ❌ 错误:用具体类型接收(import 返回的是模块命名空间,非具体类型)
import type { HeavyModule } from '../utils/HeavyUtils'
const mod: HeavyModule = await import('../utils/HeavyUtils') // 类型不匹配
模拟器运行截图
初始状态
模块尚未加载,按钮显示"动态 import() 加载模块","复用缓存"按钮为不可用状态。

动态加载完成
点击"动态 import() 加载模块"后,运行时按需加载 HeavyUtils 模块,计算出 10 万以内质数共 9,592 个,并显示模块初始化相对应用启动的时间差。

实测数据:
| 指标 | 数值 |
|---|---|
| 动态加载耗时 | 0ms(ArkTS 运行时预解析优化) |
| 10万以内质数 | 9,592 个 |
| 模块初始化相对应用启动 | +820,819ms(延迟加载,远离启动路径) |
加载耗时 0ms 是 ArkTS 运行时的预编译优化结果------模块的 ABC(Ark ByteCode)已预先编译,import() 触发时只需实例化模块而非重新解析,实际延迟极低。
关键模式:延迟加载 + 缓存复用
typescript
private cachedModule: ESObject = null
private async getModule(): Promise<ESObject> {
if (this.cachedModule !== null) {
return this.cachedModule // 直接返回缓存,0开销
}
this.cachedModule = await import('../utils/HeavyUtils')
return this.cachedModule
}
// 调用方:无论调用几次,模块只加载一次
private async doSomething(): Promise<void> {
const mod = await this.getModule()
const result = (mod.someFunction as Function)(args) as string
}
适合懒加载的模块类型
| 模块类型 | 懒加载理由 |
|---|---|
| PDF/Office 文档处理 | 体积大,仅特定场景用 |
| 图表渲染库(如折线图) | 非首屏,用户主动触发 |
| 复杂加密/哈希算法 | 偶尔调用,无需常驻内存 |
| 地图/导航功能 | 有独立页面,进入时再加载 |
| 数据导出(CSV/Excel) | 用户明确触发导出操作 |
不适合懒加载的:首页必需的 UI 组件、网络层基础库、登录鉴权模块(会造成可见的加载延迟)。
常见问题
Q:import() 的路径写法有什么规则?
A:路径相对于当前 .ets 文件,与静态 import 写法相同。字符串必须是静态字面量(编译器需要在构建期分析依赖图),不能是运行时拼接的变量:
typescript
// ✅ 正确:字面量路径
const mod = await import('../utils/HeavyUtils')
// ❌ 错误:变量路径(编译器报错)
const path = '../utils/HeavyUtils'
const mod = await import(path)
Q:ESObject 有什么安全风险?
A:通过 (mod.fn as Function)(args) as ReturnType 访问导出函数时,类型断言绕过了编译器检查,函数名拼错或参数类型错误会在运行时才报错。建议为模块编写类型声明文件(.d.ets),或封装一个类型安全的 wrapper 函数。