A Vite Plugin Now Generates uni-app's pages.json from Vue Files, Eliminating Manual Config
Version: 1.2.0 | License: MIT | Dependencies: Vite >=5.0.0 <9.0.0
Foreword
The theme of v1.2.0 is: Added generatePages plugin, enabling automatic generation of pages.json.
Previously, generateRouter was "read pages.json → generate route configuration", but pages.json itself still required manual maintenance. v1.2.0 adds the 16th plugin generatePages, which does the opposite — scans Vue files + <route-config> custom blocks within pages, automatically generating / updating pages.json for pages / subPackages / tabBar, completely freeing you from manual page configuration.
Additionally, this version relaxes Vite's peerDependencies range to <9.0.0, ensuring compatibility with Vite 8.
Highlights of this version:
| Capability | One-sentence description | What you need to do |
|---|---|---|
New generatePages plugin |
Scans Vue files + <route-config> blocks to auto-generate pages.json |
Replace manual maintenance of pages.json page section |
| Main / sub-package pages | pagesDir + subPackages auto-scan to generate pages and subPackages |
Just place page files according to directory structure conventions |
| tabBar auto-collection | isTab flag + tabBar template auto-assembles list |
Declare isTab + tab icons within the page |
| Proximate configuration declaration | <route-config> custom block declares title / style / meta / name within the page |
Configuration follows the page file, no centralized maintenance needed |
| Entry page fixed | entryPage ensures the startup page is pages[0] |
Specify the entry page path |
| Non-page fields preserved | Only overwrites the page section, preserves globalStyle / condition etc. |
No extra configuration needed |
Upgrade method: Change the version number in devDependencies to ^1.2.0. No Breaking Changes, 1.x users can upgrade smoothly.
1. Quick Start in 5 Minutes
1.1 Installation and Upgrade
{
"devDependencies": {
"@meng-xi/vite-plugin": "^1.2.0"
}
}
1.2 Basic Usage
Scans src/pages as the main package and src/pages-sub as sub-packages by default, automatically generating src/pages.json:
// vite.config.ts
import { defineConfig } from 'vite'
import { generatePages } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [generatePages()]
})
1.3 Proximate Configuration Declaration within Pages
Declare title, meta, and tabBar affiliation in each page using the <route-config> custom block:
<!-- src/pages/index/index.vue -->
<route-config>
{
"title": "Home",
"isTab": true,
"tab": {
"iconPath": "static/tab/home.png",
"selectedIconPath": "static/tab/home-active.png"
}
}
</route-config>
Generated pages.json snippet:
{
"pages": [
{
"path": "pages/index/index",
"style": { "navigationBarTitleText": "Home" },
"meta": { "isTab": true }
}
],
"tabBar": {
"color": "#999999",
"list": [
{
"pagePath": "pages/index/index",
"text": "Home",
"iconPath": "static/tab/home.png",
"selectedIconPath": "static/tab/home-active.png"
}
]
}
}
1.4 Combining with generateRouter
generatePages generates pages.json, and generateRouter reads pages.json to generate route configuration — the two can be seamlessly combined:
// Note: generatePages should be placed before generateRouter
;(generatePages({ tabBar: { color: '#999999', selectedColor: '#42b883' } }), generateRouter({ pagesJsonPath: 'src/pages.json' }))
2. generatePages Plugin Details
2.1 Core Capabilities
| Capability | Configuration / Usage | Description |
|---|---|---|
| Main package page generation | pagesDir: 'src/pages' |
Recursively scans to generate pages, stable page path sorting |
| Sub-package page generation | subPackages: [{ root, dir }] |
Auto-scans to generate subPackages, skips if directory is missing |
| tabBar collection | <route-config> declares isTab + tabBar template |
Automatically collects tab pages into tabBar.list |
| Proximate configuration declaration | <route-config> custom block |
Title / style / meta / name / tab declared proximately within the page |
| Entry page fixed | entryPage: 'pages/index/index' |
Ensures pages[0] is the startup page, not overwritten by alphabetical sorting |
| tabBar sorting | <route-config>.tab.order |
Sorts list by order ascending, used only for sorting, not written to output |
| Merge strategy | Automatic (no configuration needed) | Only overwrites the page section, preserves globalStyle / condition etc. |
| Dev watch | watch: true (default) |
Auto-regenerates when page directory files change |
2.2 route-config Custom Block
Declare configuration proximately within the page (content is JSON, supports comments):
<route-config>
{
"title": "Details",
"name": "DetailPage",
"meta": { "requireAuth": true },
"isTab": true,
"tab": { "text": "Details", "iconPath": "static/tab/detail.png", "order": 1 }
}
</route-config>
| Field | Type | Description |
|---|---|---|
| title | string |
Page title, mapped to style.navigationBarTitleText |
| name | string |
Page name, written to the name field |
| style | object |
Written as-is to the style field |
| meta | object |
Written as-is to the meta field |
| isTab | boolean |
Whether it's a tabBar page, automatically collected into tabBar.list |
| tab | TabBarItemOverride |
tabBar icon / text / order sorting weight |
Note: tabBar pages are only allowed in the main package;
isTabflags in sub-packages will be ignored.
2.3 Entry Page Fixed
uni-app uses pages[0] as the startup page. If sorted directly by path, the entry page can drift alphabetically (e.g., the default entry pages/index/index might be replaced by pages/about/about). entryPage ensures the entry page is always fixed at the first position:
generatePages({
entryPage: 'pages/index/index' // Inherits existing pages.json's pages[0] if not configured
})
2.4 tabBar Template and Priority
After providing a tabBar template, the plugin automatically collects all main package pages with isTab: true into list:
generatePages({
tabBar: {
color: '#999999',
selectedColor: '#42b883',
iconPath: 'static/tab/home.png', // Global default icon, inherited by all tab items
selectedIconPath: 'static/tab/home-active.png',
overrides: {
// Override per page path (optional)
'pages/about/about': {
text: 'About Us',
iconPath: 'static/tab/about.png',
selectedIconPath: 'static/tab/about-active.png'
}
}
}
})
Icon and text priority (from high to low):
<route-config>.tabdeclaration within the pagetabBar.overrides[pagePath]tabBar.iconPath/selectedIconPath(global template)- Page title / filename (as text fallback)
list sorting: Sorted by each item's tab.order ascending (smaller values first), items without declared order are placed after declared ones, maintaining their original relative order; order is only used for sorting and will not be written into the generated tabBar.list.
2.5 Merge Strategy
The plugin "only generates the page section, preserves the rest":
- Always overwrites:
pages(main package pages) - Overwrites when sub-packages exist:
subPackages; otherwise preserves existing - Overwrites when template is provided:
tabBar; otherwise preserves existing - Preserves as-is:
globalStyle,condition,easycom, and other non-page fields
2.6 Dev Watch
In dev mode, watch: true (default) monitors main package and sub-package directories, auto-regenerating when pages are added / deleted / modified. Generation tasks are processed through a serial queue to avoid concurrent read-write race conditions during high-frequency changes.
3. Configuration Options
| Option | Type | Default Value | Description |
|---|---|---|---|
| pagesJsonPath | string |
'src/pages.json' |
pages.json file path |
| pagesDir | string |
'src/pages' |
Main package page directory |
| subPackages | SubPackageConfig[] |
[{ root:'pages-sub', dir:'src/pages-sub' }] |
Sub-package configuration list (skipped if directory is missing) |
| routeConfigBlock | string |
'route-config' |
Page configuration custom block name |
| entryPage | string |
Existing pages[0] |
Main package entry page path, fixed as pages[0] |
| titleFallback | 'filename' | 'none' |
'filename' |
Fallback strategy when title is missing |
| tabBar | TabBarTemplate |
- | tabBar template (only generated when provided) |
| includeExtensions | string[] |
['.vue'] |
Page file extension list |
| excludePatterns | string[] |
['node_modules'] |
Path pattern list to exclude |
| watch | boolean |
true |
Watch page directory changes and auto-regenerate |
Inherits BasePluginOptions:
enabled,logLevel,errorStrategy.
Type Exports
GeneratePagesOptions— Plugin configuration optionsRouteConfigBlock— Page configuration that can be declared in<route-config>blockSubPackageConfig— Sub-package configuration (root+dir)TabBarTemplate— tabBar template (overall style + global icons +overrides)TabBarItemOverride— Single tabBar item'stext/iconPath/selectedIconPath/orderScannedPage— Scanned page information
4. Sub-path Export Changes
New
@meng-xi/vite-plugin/plugins/generate/generate-pages: ExportsgeneratePagesand typesGeneratePagesOptions,RouteConfigBlock,SubPackageConfig,TabBarTemplate,TabBarItemOverride,ScannedPage@meng-xi/vite-plugin/plugins/generate: Aggregated export ofgeneratePagesand its key types
Dependency Range
peerDependencies.vite: Relaxed from>=5.0.0 <8.0.0to>=5.0.0 <9.0.0, compatible with Vite 8
5. Practical Scenarios
5.1 uni-app Project: Fully Automatic Page Configuration
// vite.config.ts
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { generatePages, generateRouter } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
uni(),
// 1. Generate pages.json (must be before generateRouter)
generatePages({
pagesDir: 'src/pages',
subPackages: [{ root: 'pages-sub', dir: 'src/pages-sub' }],
entryPage: 'pages/index/index',
tabBar: {
color: '#999999',
selectedColor: '#42b883',
backgroundColor: '#ffffff'
}
}),
// 2. Generate route configuration based on pages.json
generateRouter({ pagesJsonPath: 'src/pages.json' })
]
})
Proximate declaration within the page:
<!-- src/pages/mine/mine.vue -->
<route-config>
{
"title": "Mine",
"isTab": true,
"tab": {
"iconPath": "static/tab/mine.png",
"selectedIconPath": "static/tab/mine-active.png"
}
}
</route-config>
5.2 Multiple Sub-packages + Namespace
generatePages({
pagesDir: 'src/pages',
subPackages: [
{ root: 'pages-sub', dir: 'src/pages-sub' },
{ root: 'pages-home', dir: 'src/pages-home' }
],
// Note: sub-package dir should match the directory structure corresponding to root, otherwise uni-app cannot find sub-package files
excludePatterns: ['node_modules']
})
5.3 Zero-Configuration Experience
Without passing any parameters, the defaults cover most scenarios:
generatePages()
// Scans src/pages → pages, src/pages-sub → subPackages, auto-generates src/pages.json
6. Plugin List (Changes)
Total plugins increased from 15 to 16, groups remain at 7:
| Group | Plugins |
|---|---|
| generate | autoImport, generatePages, generateRouter, generateVersion (increased from 3 to 4) |
| analyze | buildProgress, bundleAnalyzer |
| compress | compressAssets, imageOptimizer |
| copy | assetManifest, copyFile |
| guard | envGuard |
| inject | faviconManager, htmlInject, loadingManager, versionUpdateChecker |
| proxy | proxyManager |
7. Notes
- Main package page paths are relative to the directory containing
pages.json; sub-package page paths are relative to the sub-package directory, and the sub-packagedirshould match therootdirectory structure - tabBar pages are only allowed in the main package;
isTabflags in sub-packages will be ignored - In dev mode,
watch: truemonitors main package and sub-package directories, auto-regenerating when pages are added / deleted / modified - Generation results are stably sorted by page path, ensuring
pages.jsonorder is consistent across different file systems - First-time generation will automatically create the directory containing
pages.json - When combining
generatePageswithgenerateRouter,generatePagesshould be placed beforegenerateRouter
Afterword
v1.2.0 completes the "page configuration" link in the uni-app development chain: generatePages is responsible for generating pages.json, and generateRouter is responsible for generating route configuration and type declarations based on pages.json, forming a complete "page file → page configuration → route configuration" automation loop.
- Proximate declaration makes page configuration follow the file itself, ending the era of centralized maintenance
- tabBar auto-collection + entry page fixed solve two major manual maintenance pain points
- Zero-breaking upgrade allows 1.x users to migrate smoothly
Future versions will focus on: more automation for uni-app scenarios (such as manifest.json configuration generation), deep integration of generatePages with uni-app conditional compilation. If you have any suggestions or questions, feel free to provide feedback on GitHub Issues.
Top 1 of 5 from juejin.cn, machine-translated. The original thread is authoritative.
Compared to the <route-config> configuration, a more elegant approach would be to declare a definePage in the page, for example: definePage({ name: 'mine', meta: { needLogin: false }, style: { navigationBarTitleText: '我的' } }) Also, files that don't declare route-config or definePage in the Vue page won't be generated into page.json, right?
Files that don't declare route-config or definePage in the Vue page won't be generated into page.json.
Yeah, that's what we expect. Adding a definePage would be perfect. One more question: definePage({ name: 'mine', meta: { needLogin: false }, style: { navigationBarTitleText: '我的' } }) In a case like this, where I haven't defined a title but have set navigationBarTitleText in style, will the final generation still preserve my style: { navigationBarTitleText: '我的' } and not fall back to a default strategy for a missing title?