在开发大型 UniApp 项目时,随着业务功能不断增加,页面数量可能达到上百个。每次保存代码都要重新编译所有页面,编译时间可能长达 10分钟以上,严重影响了开发效率。
解决方案概述
通过动态生成 pages.json 实现按需编译,只编译当前开发所需的页面,大幅提升编译速度。
实现效果
优化前 yarn run dev
编译耗时201044ms
:

优化后 yarn run dev -m mode1
编译耗时32399ms
:

完整目录结构
首先,让我们看一下优化后的项目目录结构:
makefile
your-uniapp-project/
├── src/
│ ├── pages.json # 动态生成的文件(会被覆盖)
│ ├── pages/
│ │ ├── index/
│ │ ├── emp/
│ │ └── ...
│ └── ...
├── config/ # 新增的配置目录
│ ├── pages.json # 完整的页面配置(原src/pages.json的备份)
│ ├── pages.core.json # 核心页面配置
│ ├── pages.include.json # 模块化页面配置
│ └── pages-generate.js # 页面生成脚本
├── package.json
└── ...
第一步:创建配置目录和文件
1. 创建 config 目录
在项目根目录下创建 config
文件夹。
2. 备份原始 pages.json
将 src/pages.json
复制到 config/pages.json
:
json
// config/pages.json
{
"pages": [
{
"path": "pages/index/index",
"style": { ... }
},
// ... 所有页面配置
],
"subPackages": [
{
"root": "pages/emp",
"pages": [
{
"path": "my/login",
"style": { ... }
},
// ... 所有分包页面
]
}
],
"preloadRule": { ... },
// ... 其他配置
}
3. 创建核心页面配置
json
// config/pages.core.json
{
"modules": [
"pages/index/index",
"pages/emp/my/login",
"pages/emp/index",
"pages/emp/common",
"commonComponents/index",
"pages/emp/dept/resSelect",
"pages/emp/customerGroup/robotList",
"pages/emp/employee/edit",
"pages/emp/customer/index"
]
}
说明:这些是应用的基石页面,无论开发哪个模块都必须包含。(针对自己的项目进行配置)
4. 创建模块化页面配置
json
// config/pages.include.json
{
"modules": {
"mode1": [
"pages/emp/customer/index",
"pages/emp/order/index",
"pages/emp/order/detail"
],
"mode2": [
"pages/product/index",
"pages/product/detail"
]
}
}
说明:按业务模块组织页面,方便按需选择。
第二步:创建页面生成脚本
创建 config/pages-generate.js
:
javascript
const fs = require('fs')
require('dotenv').config()
// 1. 获取命令行参数
let moduleStr = ''
if (process.env.npm_config_argv) {
const args = JSON.parse(process.env.npm_config_argv).original
const i = args.findIndex(arg => arg.startsWith('-m'))
moduleStr = args[i + 1]
}
if (process.env.npm_config_message) {
moduleStr = process.env.npm_config_message
}
const enabledModules = moduleStr.split(/\s|,/g) || []
console.log(`启用的模块: ${enabledModules.join(', ')}`)
// 2. 读取所有配置文件
const pagesConfig = JSON.parse(fs.readFileSync('./config/pages.json', 'utf8'))
const moduleConfig = JSON.parse(fs.readFileSync('./config/pages.include.json', 'utf8'))
const commonConfig = JSON.parse(fs.readFileSync('./config/pages.core.json', 'utf8'))
// 3. 收集需要保留的页面路径
const retainedPaths = new Set()
// 3.1 添加选中的模块页面
enabledModules.forEach(module => {
const modulePages = moduleConfig.modules[module] || []
console.log(`模块 ${module} 包含 ${modulePages.length} 个页面`)
modulePages.forEach(path => retainedPaths.add(path))
})
// 3.2 如果有选中模块,自动加入核心页面
if (retainedPaths.size > 0) {
console.log(`添加 ${commonConfig.modules.length} 个核心页面`)
commonConfig.modules.forEach(path => {
retainedPaths.add(path)
})
}
console.log(`总共保留 ${retainedPaths.size} 个页面路径`)
// 4. 处理主包页面(pages 数组)
const retainedPages = pagesConfig.pages.filter(page => {
// 如果没有选中任何模块,保留所有页面
if (retainedPaths.size === 0) return true
// 否则只保留在 retainedPaths 中的页面
return retainedPaths.has(page.path)
})
console.log(`主包页面保留: ${retainedPages.length}/${pagesConfig.pages.length}`)
// 5. 处理分包页面(subPackages 数组)
const retainedSubPackages = pagesConfig.subPackages
.map(subPackage => {
// 如果没有选中任何模块,保留所有分包
if (retainedPaths.size === 0) return subPackage
// 过滤分包的页面
const filteredPages = subPackage.pages.filter(page => {
const fullPath = `${subPackage.root}/${page.path}`
// 检查页面路径是否在保留列表中
return [...retainedPaths].some(path => fullPath.includes(path))
})
// 如果分包还有页面,就保留这个分包
return filteredPages.length > 0
? { ...subPackage, pages: filteredPages }
: null
})
.filter(Boolean) // 过滤掉 null
console.log(`分包数量保留: ${retainedSubPackages.length}/${pagesConfig.subPackages.length}`)
// 6. 处理预加载规则(preloadRule)
const retainedSubPackageRoots = new Set(retainedSubPackages.map(sp => sp.root))
const retainedPreloadRule = {}
Object.entries(pagesConfig.preloadRule || {}).forEach(([key, rule]) => {
const validPackages = rule.packages.filter(pkg => retainedSubPackageRoots.has(pkg))
if (validPackages.length) {
retainedPreloadRule[key] = { ...rule, packages: validPackages }
}
})
// 7. 生成新的 pages.json
const generated = {
...pagesConfig,
pages: retainedPages,
subPackages: retainedSubPackages,
preloadRule: retainedPreloadRule
}
// 8. 写入到 src 目录
fs.writeFileSync('./src/pages.json', JSON.stringify(generated, null, 2))
console.log('✅ pages.json 已生成!编译时将只包含选中的页面。')
第三步:修改 package.json
修改 package.json
中的编译脚本:
json
{
"scripts": {
"dev": "node config/pages-generate.js && cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
}
}
第四步:使用方式
方式一:传统编译(所有页面)
bash
npm run dev
方式二:按模块编译
bash
# 编译 mode1 模块(核心页面 + mode1模块页面)
npm run dev -m mode1
# 编译多个模块
npm run dev -m mode1,mode2
效果对比
编译方式 | 页面数量 | 编译时间 | 开发体验 |
---|---|---|---|
传统编译 | 100+ 页面 | 10+ 分钟 | ❌ 极其糟糕 |
按模块编译 | 1~100+ 页面 | 0.1~10+ 分钟 | ✅ 流畅高效 |
常见问题解答
Q1:为什么要备份 config/pages.json?
A:因为生成脚本会覆盖 src/pages.json
,需要保留完整的配置作为模板。(因此需要注意:生成的脚本仅在本地开发时使用)
Q2:如何添加新的模块?
A:需要分别在 config/pages.include.json
和src/pages.json
的 modules
对象中添加新的模块配置。
Q3:如何确定页面路径?
A:页面路径就是 pages.json
中配置的 path
,分包页面需要加上分包根路径。
Q4:这个方案会影响生产环境吗?
A:不会。生产环境构建时使用完整的 pages.json
,这个方案只用于开发环境。
总结
通过这个完整的配置方案,你可以:
- 大幅减少编译时间:从10分钟降到1-2分钟
- 按需编译:只编译当前开发的模块
- 保持灵活性:随时切换编译模式