在鸿蒙应用开发过程中,随着项目规模扩大,模块数量往往会快速增长。一个典型的中大型鸿蒙应用可能包含数十个模块,包括业务模块、公共库模块、测试模块等。当我们需要构建应用安装包(HAP或APP)时,往往不需要将所有模块都打包进去。比如:
这时候如果全量打包,会导致安装包体积过大,影响安装效率和运行性能。传统做法是手动注释掉不需要的模块依赖,但这种方式容易出错且难以维护。
hvigor是鸿蒙专用的构建工具,类似于Android的Gradle。hvigorfile.js则是项目构建的核心配置文件,采用JavaScript语法编写。一个典型的hvigorfile结构如下:
javascript复制// 应用级配置
app = {
// 应用基本信息
packageName: "com.example.myapp",
// 模块依赖配置
dependencies: {
// 本地模块依赖
local: [
":feature:home",
":feature:detail",
":lib:network"
],
// 远程依赖
remote: []
}
}
在模块级的hvigorfile中,我们可以定义模块的具体构建行为:
javascript复制// 模块级配置
module = {
// 模块类型
type: "feature",
// 构建配置
buildTypes: {
debug: {
// 调试模式配置
},
release: {
// 发布模式配置
}
}
}
这是最灵活的模块排除方式,通过在hvigorfile中动态控制依赖项:
javascript复制// 获取当前构建类型
const buildType = process.env.BUILD_TYPE || "debug"
app = {
dependencies: {
local: [
":feature:home",
":feature:detail",
// 测试模块仅在debug模式包含
...(buildType === "debug" ? [":test:mock"] : []),
// 实验性模块需要显式开启
...(process.env.INCLUDE_EXPERIMENTAL === "true" ? [":experimental:new"] : [])
]
}
}
提示:可以通过环境变量控制模块包含逻辑,如:
BUILD_TYPE=release hvigor clean assemble
鸿蒙支持通过productFlavors定义不同的构建变体:
javascript复制app = {
productFlavors: {
// 基础版本(排除高级功能)
basic: {
excludeModules: [":feature:premium"]
},
// 完整版本
full: {
// 包含所有模块
}
},
dependencies: {
local: [
":feature:home",
":feature:premium",
":lib:analytics"
]
}
}
构建时指定变体:
bash复制hvigor assembleBasicRelease # 基础版本
hvigor assembleFullRelease # 完整版本
对于更复杂的排除逻辑,可以使用JavaScript脚本动态处理:
javascript复制// 获取项目配置
const projectConfig = require('./project.config.json')
app = {
dependencies: {
local: (() => {
const modules = [
":feature:home",
":feature:detail",
":lib:utils"
]
// 根据配置文件排除模块
if (projectConfig.excludeExperimental) {
modules = modules.filter(m => !m.includes("experimental"))
}
return modules
})()
}
}
下面展示一个企业级项目中完整的模块管理方案:
javascript复制// 环境配置
const environments = {
dev: {
features: ["home", "detail", "profile"],
libs: ["network", "utils", "mock"]
},
staging: {
features: ["home", "detail", "preview"],
libs: ["network", "utils", "analytics"]
},
production: {
features: ["home", "detail"],
libs: ["network", "utils", "analytics"]
}
}
// 当前环境(通过命令行参数获取)
const currentEnv = process.env.APP_ENV || "dev"
app = {
packageName: "com.example.myapp",
dependencies: {
local: [
// 动态加载功能模块
...environments[currentEnv].features.map(f => `:feature:${f}`),
// 动态加载库模块
...environments[currentEnv].libs.map(l => `:lib:${l}`)
],
remote: [
// 远程依赖
"@ohos/router:1.0.0"
]
},
// 构建后处理
afterAll: {
// 移除未使用的资源
removeUnusedResources: true,
// 排除未引用的代码
shrinkCode: currentEnv === "production"
}
}
使用方式:
bash复制# 开发环境构建
APP_ENV=dev hvigor assembleDebug
# 生产环境构建
APP_ENV=production hvigor assembleRelease
现象:排除某个模块后,其他模块出现"无法解析符号"错误。
原因:被排除的模块可能被其他模块隐式依赖。
解决方案:
bash复制hvigor dependencies --configuration compileClasspath
javascript复制module = {
dependencies: {
local: [":lib:utils"],
remote: ["@ohos/router:1.0.0"]
}
}
现象:模块虽然被排除了,但其资源文件仍被打包。
原因:资源文件可能被其他模块引用或未正确配置。
解决方案:
javascript复制module = {
buildTypes: {
release: {
resourceExcludes: ["src/main/resources/rawfile/excluded/**"]
}
}
}
现象:不同模块依赖了同一库的不同版本。
解决方案:
javascript复制app = {
dependencies: {
constraints: {
// 强制使用指定版本
"@ohos/router": "1.0.0"
}
}
}
javascript复制module = {
dependencies: {
remote: [{
name: "@ohos/net",
exclude: ["@ohos/logger"] // 排除不需要的传递依赖
}]
}
}
对于大型应用,可以使用按需加载机制:
javascript复制// 在主模块的hvigorfile中
module = {
abilities: {
main: {
// 配置延迟加载的模块
onDemandModules: [
":feature:premium",
":feature:experimental"
]
}
}
}
通过合理排除模块可以显著提升构建速度:
bash复制hvigor incrementalBuild --parallel
javascript复制app = {
buildCache: {
// 启用构建缓存
enabled: true,
// 缓存位置
cacheDir: ".hvigor/cache"
}
}
code复制:feature:home
:feature:home:api // 接口定义
:feature:detail
:feature:detail:api
查看完整的模块依赖关系:
bash复制hvigor dependencies --graph
验证最终打包内容是否包含排除的模块:
bash复制# 解压HAP文件检查内容
unzip -l build/outputs/hap/debug/app-debug.hap
创建验证任务确保模块正确排除:
javascript复制task("verifyExcludedModules", () => {
const hapContent = fs.readdirSync("build/outputs/hap/debug")
if (hapContent.includes("excluded_module.dex")) {
throw new Error("排除模块验证失败")
}
})