@uni-helper/vite-plugin-uni-pages 0.4.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +388 -50
- package/client.d.ts +2 -4
- package/dist/index.d.mts +708 -357
- package/dist/index.mjs +1737 -870
- package/package.json +27 -35
- package/dist/index.cjs +0 -1401
- package/dist/index.d.cts +0 -569
- package/index.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# @uni-helper/vite-plugin-uni-pages
|
|
2
2
|
|
|
3
|
-
<
|
|
3
|
+
<p style="text-align: center">
|
|
4
|
+
<a href="https://github.com/uni-helper/vite-plugin-uni-pages/stargazers"><img src="https://img.shields.io/github/stars/uni-helper/vite-plugin-uni-pages?colorA=005947&colorB=eee&style=for-the-badge" alt="Stars"></a>
|
|
5
|
+
<a href="https://npmx.dev/package/@uni-helper/vite-plugin-uni-pages"><img src="https://img.shields.io/npm/dm/@uni-helper/vite-plugin-uni-pages?colorA=005947&colorB=eee&style=for-the-badge" alt="Downloads"></a>
|
|
6
|
+
<a href="https://npmx.dev/package/@uni-helper/vite-plugin-uni-pages"><img src="https://img.shields.io/npm/v/@uni-helper/vite-plugin-uni-pages?colorA=005947&colorB=eee&style=for-the-badge" alt="NPM Version"></a>
|
|
7
|
+
</p>
|
|
8
|
+
<p style="text-align: center">
|
|
9
|
+
<a href="https://github.com/kejunmao"><img src="https://img.shields.io/badge/Author-KeJun-blue?style=for-the-badge" alt="Author"></a>
|
|
10
|
+
<a href="https://github.com/ModyQyW"><img src="https://img.shields.io/badge/Maintainer-ModyQyW-blue?style=for-the-badge" alt="Author"></a>
|
|
11
|
+
</p>
|
|
4
12
|
|
|
5
13
|
使用 TypeScript 编写 `uni-app` 的 `pages.json`。支持约定式路由。
|
|
6
14
|
|
|
7
|
-
不想看文档?直接问 AI 🤖 <a href="https://deepwiki.com/uni-helper/vite-plugin-uni-pages"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
15
|
+
不想看文档?直接问 AI 🤖 <a href="https://deepwiki.com/uni-helper/vite-plugin-uni-pages"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
16
|
+
|
|
17
|
+
> **请考虑持续[赞助](https://github.com/ModyQyW/sponsors)以维持该项目的持续健康发展,非常感谢!🙏**
|
|
8
18
|
|
|
9
19
|
## 安装
|
|
10
20
|
|
|
@@ -15,18 +25,23 @@ pnpm i -D @uni-helper/vite-plugin-uni-pages
|
|
|
15
25
|
## 使用
|
|
16
26
|
|
|
17
27
|
```ts
|
|
18
|
-
// vite.config.
|
|
19
|
-
import Uni from '@
|
|
28
|
+
// vite.config.mts
|
|
29
|
+
import Uni from '@uni-helper/plugin-uni'
|
|
30
|
+
// 或
|
|
31
|
+
// import dcloudioUni from '@dcloudio/vite-plugin-uni'
|
|
32
|
+
// const Uni = dcloudioUni.default || dcloudioUni
|
|
20
33
|
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
|
21
34
|
import { defineConfig } from 'vite'
|
|
22
35
|
|
|
23
|
-
// It is recommended to put it in front of Uni
|
|
24
36
|
export default defineConfig({
|
|
25
|
-
plugins: [
|
|
37
|
+
plugins: [
|
|
38
|
+
UniPages(), // 需要在 Uni() 之前调用
|
|
39
|
+
Uni(),
|
|
40
|
+
],
|
|
26
41
|
})
|
|
27
42
|
```
|
|
28
43
|
|
|
29
|
-
创建 `pages.config.(ts|mts|cts|js|cjs|mjs|json)`,然后用 TypeScript 编写你的 `pages.json`。你可以在创建的文件里使用 `#ifdef H5` 等条件编译语句。
|
|
44
|
+
创建 `pages.config.(ts|mts|cts|js|cjs|mjs|json)`,然后用 TypeScript 编写你的 `pages.json`。你可以在创建的文件里使用 `#ifdef H5` 等条件编译语句。[👉 pages.config.ts 示例](../../playground/pages.config.ts)
|
|
30
45
|
|
|
31
46
|
```ts
|
|
32
47
|
// pages.config.ts
|
|
@@ -47,16 +62,139 @@ export default defineUniPages({
|
|
|
47
62
|
})
|
|
48
63
|
```
|
|
49
64
|
|
|
50
|
-
|
|
65
|
+
### 页面级配置 definePage
|
|
51
66
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
在页面文件的 `<script setup>` 中使用 `definePage` 宏声明页面元数据:
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<script setup lang="ts">
|
|
71
|
+
definePage({
|
|
72
|
+
style: {
|
|
73
|
+
navigationBarTitleText: '首页',
|
|
74
|
+
},
|
|
75
|
+
type: 'home', // 标记为首页
|
|
76
|
+
})
|
|
77
|
+
</script>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> `definePage` 必须写在 `<script>` 块的顶层,不要包进 `if`、`try` 等语句块里。插件只在块的顶层查找这个宏:写在里面的调用既读不到配置,也不会在构建时从代码里移除,运行时会报 `definePage is not defined`。
|
|
81
|
+
|
|
82
|
+
要让编辑器识别 `definePage` 全局宏,需要在项目的声明文件或 `tsconfig.json > types` 引用本包的 `/client` 类型。
|
|
83
|
+
|
|
84
|
+
<details>
|
|
85
|
+
|
|
86
|
+
<summary> 声明文件简单示例 </summary>
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// env.d.ts
|
|
90
|
+
/// <reference types="vite/client" />
|
|
91
|
+
/// <reference types="@uni-helper/vite-plugin-uni-pages/client" />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
// tsconfig.json
|
|
96
|
+
{
|
|
97
|
+
"include": ["**/*.d.ts"]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
</details>
|
|
102
|
+
|
|
103
|
+
<details>
|
|
104
|
+
|
|
105
|
+
<summary> tsconfig.json 简单示例 </summary>
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
// tsconfig.json
|
|
109
|
+
{
|
|
110
|
+
"types": ["@uni-helper/vite-plugin-uni-pages/client"]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
</details>
|
|
115
|
+
|
|
116
|
+
自 0.5.0 起,`definePage` 支持函数式写法,接收 `{ platform, define }` 上下文(详见 [平台条件页面配置](#平台条件页面配置)):
|
|
117
|
+
|
|
118
|
+
```vue
|
|
119
|
+
<script setup lang="ts">
|
|
120
|
+
definePage(({ platform, define }) =>
|
|
121
|
+
define({
|
|
122
|
+
style: { navigationBarTitleText: '首页' },
|
|
123
|
+
})
|
|
124
|
+
.ifdef('mp-weixin', { style: { navigationBarBackgroundColor: '#07c160' } })
|
|
125
|
+
.ifndef('h5', { middlewares: ['auth'] }),
|
|
126
|
+
)
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
自 0.5.0 起,返回(或直接传入)`null` 表示当前平台不注册该页面:
|
|
131
|
+
|
|
132
|
+
```vue
|
|
133
|
+
<script setup lang="ts">
|
|
134
|
+
definePage(({ platform }) => {
|
|
135
|
+
// 仅小程序注册该页面
|
|
136
|
+
if (platform === 'h5')
|
|
137
|
+
return null
|
|
138
|
+
return { style: { navigationBarTitleText: '仅小程序' } }
|
|
139
|
+
})
|
|
140
|
+
</script>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
宏的参数里可以引用页面文件顶部导入的值(普通 `<script>` 块里的导入也算),插件在构建时会把这些导入一起执行;宏参数没用到的导入(比如组件的导入)不会被执行:
|
|
55
144
|
|
|
56
|
-
|
|
145
|
+
```vue
|
|
146
|
+
<script setup lang="ts">
|
|
147
|
+
import { TITLE } from './title.mjs'
|
|
148
|
+
|
|
149
|
+
definePage({
|
|
150
|
+
style: { navigationBarTitleText: TITLE },
|
|
151
|
+
})
|
|
152
|
+
</script>
|
|
57
153
|
```
|
|
58
154
|
|
|
59
|
-
|
|
155
|
+
> 导入路径要写全扩展名(`./title.mjs`、`./title.ts` 都可以)。TS 里习惯的省略扩展名写法(`./title`)在这里不行——宏代码在构建期用 Node 的 `require` 执行,它不做 TS 的扩展名补全。
|
|
156
|
+
|
|
157
|
+
### 平台条件页面配置
|
|
158
|
+
|
|
159
|
+
自 0.5.0 起,函数式 `definePage` 注入的 `define` 工厂提供条件 DSL,无需手动读取 `process.env.UNI_PLATFORM`:
|
|
160
|
+
|
|
161
|
+
- `define(base)`:所有平台共享的基础元数据
|
|
162
|
+
- `.ifdef(platforms, partial)`:仅在列出的平台生效
|
|
163
|
+
- `.ifndef(platforms, partial)`:在列出的平台之外的所有平台生效
|
|
164
|
+
- 匹配的分支按声明顺序深合并进 `base`:对象递归合并,数组与原始值直接替换
|
|
165
|
+
- `h5` 与 `web` 是同一平台的别名,分支中写任意一个即同时覆盖两者
|
|
166
|
+
- `platform` 为当前构建的平台标识(如 `'mp-weixin'`、`'h5'`)
|
|
167
|
+
|
|
168
|
+
```vue
|
|
169
|
+
<script setup lang="ts">
|
|
170
|
+
definePage(({ define }) =>
|
|
171
|
+
define({
|
|
172
|
+
style: { navigationBarTitleText: '标题', enablePullDownRefresh: false },
|
|
173
|
+
})
|
|
174
|
+
.ifdef('mp-weixin', { style: { navigationBarBackgroundColor: '#07c160' } })
|
|
175
|
+
.ifndef(['h5', 'web'], { middlewares: ['auth'] }),
|
|
176
|
+
)
|
|
177
|
+
</script>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
条件定义在扫描阶段即按当前平台解析为普通对象,最终写入 `pages.json` 的仍是平台无关的标准结构;多终端并发时各平台的差异由插件按条件编译块合并(见 [多终端并发开发](#多终端并发开发))。
|
|
181
|
+
|
|
182
|
+
你也可以导入 [虚拟模块](https://v5.vite.dev/guide/api-plugin.html#virtual-modules-convention) 来访问所有页面的元数据。
|
|
183
|
+
|
|
184
|
+
> 注意:虚拟模块的导出不保证稳定,可能会在小版本中出现变化。
|
|
185
|
+
|
|
186
|
+
<details>
|
|
187
|
+
|
|
188
|
+
<summary> 虚拟模块使用简单示例 </summary>
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
/// <reference types="@uni-helper/vite-plugin-uni-pages/client" />
|
|
192
|
+
import { pages } from 'virtual:uni-pages'
|
|
193
|
+
|
|
194
|
+
console.log(pages)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
</details>
|
|
60
198
|
|
|
61
199
|
## 插件配置
|
|
62
200
|
|
|
@@ -67,14 +205,15 @@ interface UserOptions {
|
|
|
67
205
|
* 为 true 时在项目根目录生成 uni-pages.d.ts
|
|
68
206
|
* 为 string 时作为自定义输出路径(相对于项目根目录)
|
|
69
207
|
* @default true
|
|
208
|
+
* @since 0.2.9
|
|
70
209
|
*/
|
|
71
210
|
dts?: boolean | string
|
|
72
211
|
|
|
73
212
|
/**
|
|
74
213
|
* 页面配置文件的加载源
|
|
75
214
|
* 基于 unconfig,支持多配置源合并
|
|
76
|
-
* 默认为 pages.config,支持 ts/mts/cts/js/cjs/mjs/json 等后缀
|
|
77
215
|
* @default 'pages.config'
|
|
216
|
+
* @since 0.2.7
|
|
78
217
|
*/
|
|
79
218
|
configSource?: ConfigSource
|
|
80
219
|
|
|
@@ -82,14 +221,16 @@ interface UserOptions {
|
|
|
82
221
|
* 默认应用入口页面(首页)
|
|
83
222
|
* 当没有页面通过 definePage({ type: 'home' }) 标记为首页时使用
|
|
84
223
|
* 支持多个路径样式以兼容不同的目录结构
|
|
85
|
-
* @default 'pages/index'
|
|
224
|
+
* @default ['pages/index', 'pages/index/index']
|
|
225
|
+
* @since 0.1.9
|
|
86
226
|
*/
|
|
87
|
-
homePage?: string
|
|
227
|
+
homePage?: string | string[]
|
|
88
228
|
|
|
89
229
|
/**
|
|
90
230
|
* 是否自动扫描目录并合并页面配置到 pages.json
|
|
91
231
|
* 关闭后仅加载用户配置文件,不扫描文件系统
|
|
92
232
|
* @default true
|
|
233
|
+
* @since 0.1.0
|
|
93
234
|
*/
|
|
94
235
|
mergePages?: boolean
|
|
95
236
|
|
|
@@ -98,59 +239,86 @@ interface UserOptions {
|
|
|
98
239
|
* 支持 glob 模式,如 'src/{pages,views}'
|
|
99
240
|
* 最终结果由 tinyglobby 解析为匹配的目录列表
|
|
100
241
|
* @default 'src/pages'
|
|
242
|
+
* @since 0.1.0
|
|
101
243
|
*/
|
|
102
244
|
dir?: string
|
|
103
245
|
|
|
104
246
|
/**
|
|
105
247
|
* 分包页面目录的根目录列表
|
|
106
|
-
*
|
|
107
248
|
* 用于 uni-app 的分包加载功能
|
|
108
|
-
*
|
|
109
249
|
* 支持字符串格式(目录路径)或对象格式(自定义 pages.json 中的 root)
|
|
110
|
-
*
|
|
111
250
|
* 更多上下文参考 <https://github.com/uni-helper/vite-plugin-uni-pages/issues/271>
|
|
112
251
|
* @default []
|
|
252
|
+
* @since 0.1.8
|
|
113
253
|
*/
|
|
114
|
-
subPackages?: (string | { dir: string
|
|
254
|
+
subPackages?: (string | { dir: string, root: string })[]
|
|
115
255
|
|
|
116
256
|
/**
|
|
117
257
|
* pages.json 所在目录
|
|
118
258
|
* 相对于项目根目录,也是计算页面相对路径的基准
|
|
119
259
|
* @default 'src'
|
|
260
|
+
* @since 0.0.1
|
|
120
261
|
*/
|
|
121
262
|
outDir?: string
|
|
122
263
|
|
|
123
264
|
/**
|
|
124
265
|
* 排除的文件/目录模式
|
|
125
266
|
* 基于 tinyglobby 的 ignore 选项
|
|
126
|
-
* @default ['node_modules', '.git', '
|
|
267
|
+
* @default ['node_modules', '.git', '**\/__*__/**']
|
|
268
|
+
* @since 0.0.4
|
|
127
269
|
*/
|
|
128
270
|
exclude?: string[]
|
|
129
271
|
|
|
130
272
|
/**
|
|
131
273
|
* 是否压缩生成的 pages.json
|
|
132
274
|
* @default false
|
|
275
|
+
* @since 0.1.6
|
|
133
276
|
*/
|
|
134
277
|
minify?: boolean
|
|
135
278
|
|
|
279
|
+
/**
|
|
280
|
+
* 是否在 pages.json 末尾插入换行
|
|
281
|
+
* @default false
|
|
282
|
+
* @since 0.5.0
|
|
283
|
+
*/
|
|
284
|
+
insertFinalNewline?: boolean
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 生成的 pages.json 的缩进
|
|
288
|
+
* 接受空格数量或字符串(如 `'\t'`)
|
|
289
|
+
* 当 `minify` 为 `true` 时被忽略
|
|
290
|
+
* @default 2
|
|
291
|
+
* @since 0.5.0
|
|
292
|
+
*/
|
|
293
|
+
indent?: number | string
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 生成的 pages.json 的换行符
|
|
297
|
+
* 自 0.5.0 起提供
|
|
298
|
+
* @default '\n'
|
|
299
|
+
*/
|
|
300
|
+
eol?: '\n' | '\r\n'
|
|
301
|
+
|
|
136
302
|
/**
|
|
137
303
|
* 启用调试日志
|
|
138
304
|
* 为 true 时启用所有分类;为字符串时仅启用特定分类
|
|
139
305
|
* 可选分类:hmr | options | pages | subPages | error | cache | declaration | definePage
|
|
140
306
|
* 也可通过环境变量 DEBUG=vite-plugin-uni-pages:* 控制
|
|
141
307
|
* @default false
|
|
308
|
+
* @since 0.1.8
|
|
142
309
|
*/
|
|
143
|
-
debug?: boolean |
|
|
144
|
-
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
310
|
+
debug?: boolean | DebugType
|
|
311
|
+
|
|
312
|
+
// 生命周期钩子,在每个阶段前后触发,仅接收该阶段的输入或输出数据
|
|
313
|
+
// 自 v0.0.3 起提供;0.5.0 起签名由 (ctx: PageContext) 改为按阶段传入数据,见「0.5.0 破坏性变更」
|
|
314
|
+
onBeforeLoadUserConfig?: () => void
|
|
315
|
+
onAfterLoadUserConfig?: (pagesGlobConfig: PagesConfig | undefined) => void
|
|
316
|
+
onBeforeScanPages?: () => void
|
|
317
|
+
onAfterScanPages?: (pages: Map<string, Page>, subPages: Map<string, Map<string, Page>>) => void
|
|
318
|
+
onBeforeMergePageMetaData?: (pages: Map<string, Page>, pagesGlobConfig: PagesConfig | undefined) => void
|
|
319
|
+
onAfterMergePageMetaData?: (pageMetaData: InternalPages, subPageMetaData: SubPackages) => void
|
|
320
|
+
onBeforeWriteFile?: (filePath: string) => void
|
|
321
|
+
onAfterWriteFile?: (filePath: string, content: string) => void
|
|
154
322
|
}
|
|
155
323
|
```
|
|
156
324
|
|
|
@@ -158,22 +326,24 @@ interface UserOptions {
|
|
|
158
326
|
|
|
159
327
|
插件内部按以下顺序执行生命周期,每个阶段对应一个生命周期钩子:
|
|
160
328
|
|
|
161
|
-
```
|
|
329
|
+
```text
|
|
162
330
|
加载用户配置 → 扫描页面文件 → 合并页面元数据 → 生成并写入 pages.json
|
|
163
331
|
```
|
|
164
332
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-
|
|
333
|
+
1. **加载用户配置**:通过 `unconfig` 加载 `pages.config.ts` 等配置文件,获取手动指定的页面元数据(`pages`、`subPackages`、`globalStyle` 等)
|
|
334
|
+
2. **扫描页面文件**(仅在 `mergePages: true` 时):根据 `dir` 和 `subPackages` 扫描文件系统,为每个页面文件创建 `Page` 实例
|
|
335
|
+
3. **合并页面元数据**:将扫描到的页面与用户配置中的页面元数据合并。优先级从低到高:
|
|
336
|
+
|
|
337
|
+
- `globalStyle`(`pages.config.ts` 中的全局样式)
|
|
338
|
+
- `definePage()`(`.vue` 文件中通过宏定义的页面配置)
|
|
339
|
+
- `pages.config.ts` 中 `pages` 数组里对应 path 的条目(最高优先级,会覆盖 `definePage` 的同名属性)
|
|
340
|
+
|
|
341
|
+
4. **生成并写入**:序列化为 `pages.json`,写入到 `outDir` 目录,并生成 TypeScript 类型声明
|
|
172
342
|
|
|
173
343
|
### 配置示例
|
|
174
344
|
|
|
175
345
|
```ts
|
|
176
|
-
// vite.config.
|
|
346
|
+
// vite.config.mts
|
|
177
347
|
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
|
178
348
|
import { defineConfig } from 'vite'
|
|
179
349
|
|
|
@@ -186,8 +356,8 @@ export default defineConfig({
|
|
|
186
356
|
exclude: ['node_modules', '.git', '**/__*__/**', '**/components/**'],
|
|
187
357
|
minify: true,
|
|
188
358
|
debug: true, // 或 debug: 'pages'
|
|
189
|
-
onAfterScanPages(
|
|
190
|
-
console.log(`扫描到 ${
|
|
359
|
+
onAfterScanPages(pages) {
|
|
360
|
+
console.log(`扫描到 ${pages.size} 个主包页面`)
|
|
191
361
|
},
|
|
192
362
|
}),
|
|
193
363
|
],
|
|
@@ -200,13 +370,46 @@ export default defineConfig({
|
|
|
200
370
|
|
|
201
371
|
## FAQ
|
|
202
372
|
|
|
203
|
-
### 这个插件写入配置晚于 uni-app
|
|
373
|
+
### 这个插件写入配置晚于 uni-app 读取配置,导致无法正常运行
|
|
374
|
+
|
|
375
|
+
`@dcloudio/vite-plugin-uni` 在 Vite 的 `config` 钩子里通过 `parsePagesJsonOnce` 读取 `pages.json`,而本插件在更晚的 `configResolved` 钩子里才写入。
|
|
376
|
+
|
|
377
|
+
`config` 早于 `configResolved`,所以即便本插件设置了 `enforce: 'pre'`,也只能在 `configResolved` 内部抢先,无法早于 `config` 钩子。`parsePagesJsonOnce` 结果被 `once` 缓存,首次读取后即固定,后续写入对 uni-app 无效。
|
|
378
|
+
|
|
379
|
+
核心矛盾是时序:必须在 uni-app 进程启动前把 `pages.json` 生成好。以下按推荐度排序给出方案。
|
|
380
|
+
|
|
381
|
+
#### 方案一(推荐):使用 [@uni-helper/unh](https://uni-helper.cn/unh/auto-generate)
|
|
382
|
+
|
|
383
|
+
`unh` 在调用 `uni dev/build` 前加载配置、扫描页面并写盘,再 spawn 子进程,天然解决时序问题。
|
|
384
|
+
|
|
385
|
+
```jsonc
|
|
386
|
+
// package.json
|
|
387
|
+
{
|
|
388
|
+
"scripts": {
|
|
389
|
+
"dev": "unh dev",
|
|
390
|
+
"build": "unh build"
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
// unh.config.ts
|
|
397
|
+
import { defineConfig } from '@uni-helper/unh'
|
|
398
|
+
|
|
399
|
+
export default defineConfig({
|
|
400
|
+
autoGenerate: {
|
|
401
|
+
pages: true, // 在 dev/build 前自动生成 pages.json
|
|
402
|
+
},
|
|
403
|
+
})
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### 方案二:自行编写脚本,在 uni 命令前生成
|
|
204
407
|
|
|
205
|
-
|
|
408
|
+
如果不想引入 `unh`,可以自行编写脚本在 `uni` 命令前生成 `pages.json`,再用 `&&` 串联或 npm `predev`/`prebuild` 钩子触发。脚本需要复刻本插件的完整流水线(加载 `pages.config.ts`、扫描页面文件、解析 `definePage` 宏、合并元数据),维护成本较高,故优先推荐方案一。
|
|
206
409
|
|
|
207
410
|
### 支持 JSX/TSX 吗?
|
|
208
411
|
|
|
209
|
-
不支持,只支持 vue/nvue/uvue
|
|
412
|
+
不支持,只支持 vue/nvue/uvue 文件。
|
|
210
413
|
|
|
211
414
|
### 文件名有限制吗?
|
|
212
415
|
|
|
@@ -217,7 +420,7 @@ export default defineConfig({
|
|
|
217
420
|
在 monorepo 项目中,如果页面分布在多个 package 中,可以使用 `subPackages` 配置的对象格式来自定义生成的 `root` 路径。
|
|
218
421
|
|
|
219
422
|
```ts
|
|
220
|
-
// vite.config.
|
|
423
|
+
// vite.config.mts
|
|
221
424
|
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
|
222
425
|
import { defineConfig } from 'vite'
|
|
223
426
|
|
|
@@ -246,6 +449,45 @@ export default defineConfig({
|
|
|
246
449
|
|
|
247
450
|
更多上下文参考 <https://github.com/uni-helper/vite-plugin-uni-pages/issues/271>。
|
|
248
451
|
|
|
452
|
+
## 0.5.0 破坏性变更
|
|
453
|
+
|
|
454
|
+
升级到 0.5.0 前请阅读以下变更:
|
|
455
|
+
|
|
456
|
+
- **仅支持 ESM**:移除了 `require` 条件导出,`main` 指向 `dist/index.mjs`。使用 CommonJS(`require()`)引入本插件的项目需要迁移到 ESM,例如将 Vite 配置文件改名为 `vite.config.mts`。
|
|
457
|
+
- **Node.js 版本要求**:`^22.22.2 || ^24.15.0 || >=26.0.0`(随 `@babel/*` v8 升级)。
|
|
458
|
+
- **Babel 8**:宏解析底层升级到 `@babel/parser` v8。import attributes 仅支持 `with { ... }` 语法,已废弃的 `assert { ... }` 语法不再支持;带有该语法的页面文件将无法解析宏,退化为仅路径元数据。
|
|
459
|
+
- **生命周期钩子签名变更**:钩子不再接收整个 `PageContext`,改为仅接收对应阶段的输入或输出数据,部分钩子名称同步调整。对照表:
|
|
460
|
+
|
|
461
|
+
| 0.4.x | 0.5.0 |
|
|
462
|
+
| --- | --- |
|
|
463
|
+
| `onBeforeLoadUserConfig(ctx)` | `onBeforeLoadUserConfig()` |
|
|
464
|
+
| `onAfterLoadUserConfig(ctx)` | `onAfterLoadUserConfig(pagesGlobConfig)` |
|
|
465
|
+
| `onBeforeScanPages(ctx)` | `onBeforeScanPages()` |
|
|
466
|
+
| `onAfterScanPages(ctx)` | `onAfterScanPages(pages, subPages)` |
|
|
467
|
+
| `onBeforeMergePageMetaData(ctx)` | `onBeforeMergePageMetaData(pages, pagesGlobConfig)` |
|
|
468
|
+
| `onAfterMergePageMetaData(ctx)` | `onAfterMergePageMetaData(pageMetaData, subPageMetaData)` |
|
|
469
|
+
| `onBeforeWriteFile(ctx)` | `onBeforeWriteFile(filePath)` |
|
|
470
|
+
| `onAfterWriteFile(ctx)` | `onAfterWriteFile(filePath, content)` |
|
|
471
|
+
|
|
472
|
+
- **依赖清理**:`json5`、`yaml`、`detect-indent`、`detect-newline` 不再是运行时依赖(配置文件仍支持 `ts/mts/cts/js/cjs/mjs/json`,无行为变化)。
|
|
473
|
+
|
|
474
|
+
- **类型入口**:包名的 `types` 入口直指构建产物,仅配置 `"types": ["@uni-helper/vite-plugin-uni-pages"]` 不再带入 `definePage` 全局与 `virtual:uni-pages` 模块声明,请改用 `/client` 子路径(见上方「页面级配置 definePage」)。
|
|
475
|
+
|
|
476
|
+
- **类型修正(静默变更)**:以下 `@uni-helper/uni-pages-types` 的类型变更不会产生 TS 报错(接口带索引签名),但行为有差异:
|
|
477
|
+
- `app-plus.softInputMode` / `softInputNavBar` 更名为官方拼写 `softinputMode` / `softinputNavBar`(App-Harmony 同步更名)。旧字段名不再有类型提示;`pages.json` 中已写的旧字段名请手动改为新拼写。
|
|
478
|
+
- `GlobalStyle` 移除 `disableScroll` / `disableSwipeBack`。官方文档注明这两个配置只在页面级 `style` 中有效、在 `globalStyle` 中设置无效,现仅在 `PageStyle` 上保留;写在 `globalStyle` 里的这两个字段会被忽略。
|
|
479
|
+
- **不可用的 `pages.json` 直接报错**:已存在的 `pages.json` 若不是普通文件(例如目录)或缺少读写权限,插件现在抛出错误并停止生成;0.4.x 会把它删除并重建为占位内容,静默丢失手写内容。请先修复权限(如 `chmod u+w`)或自行删除该文件。
|
|
480
|
+
|
|
481
|
+
> 从 0.3.x 直接升级到 0.5.x 的用户请注意:`<route>` 自定义块与 `routeBlockLang` 选项已在 0.4.0 移除(0.3.24 起运行时会打印废弃提示),请改用 `definePage` 宏,迁移说明见 <https://uni-helper.js.org/vite-plugin-uni-pages/definePage>。
|
|
482
|
+
|
|
483
|
+
0.5.0 同时带来以下新能力:
|
|
484
|
+
|
|
485
|
+
- `definePage(null)` 按平台跳过页面注册(见上方「页面级配置 definePage」)
|
|
486
|
+
- 函数式 `definePage` 与 `define().ifdef()/.ifndef()` 条件 DSL(见上方「平台条件页面配置」)
|
|
487
|
+
- 插件生成的过期子包自动清理(见下方「多终端并发开发」)
|
|
488
|
+
- tabBar 顶层属性按平台分别记录,多平台取值互不覆盖(见下方「多终端并发开发」)
|
|
489
|
+
- 新增 `indent`、`eol`、`insertFinalNewline` 格式化选项
|
|
490
|
+
|
|
249
491
|
## 多终端并发开发
|
|
250
492
|
|
|
251
493
|
同一项目里同时运行到多个平台时(例如,两个终端分别运行 `pnpm run dev:mp-weixin` 和 `pnpm run dev:mp-alipay`),多个进程会读写同一个 `pages.json`。
|
|
@@ -253,14 +495,110 @@ export default defineConfig({
|
|
|
253
495
|
本插件默认会:
|
|
254
496
|
|
|
255
497
|
- 通过文件锁串行化对 `pages.json` 的「读取已有内容 → 合并当前平台配置 → 写回」整个流程,避免并发写入互相覆盖;
|
|
256
|
-
-
|
|
257
|
-
- 对同一 `path`(或 tabBar 的 `pagePath
|
|
498
|
+
- 保留其他平台已经写入的 `#ifdef` 条件编译块,只更新当前平台对应的条目;
|
|
499
|
+
- 对同一 `path`(或 tabBar 的 `pagePath`)的条目按内容去重:内容相同的条目会合并为一条,并把两个平台合在一起写(如 `H5 || MP-WEIXIN`),不会因为先后跑过多个平台而产生重复路由;
|
|
500
|
+
- tabBar 的顶层属性(`color`、`selectedColor`、`custom`、`position`、`midButton` 等)同样按平台分别记录:各平台取值不同时,每个平台的取值保存在自己的 `#ifdef` 块里(同一属性名会出现多个按平台互斥的副本,uni-app 条件编译后每个平台只剩一份);取值相同时合并为一条不带条件的属性。只有声明了非空 `list` 的运行才会写属性,`list` 为空或未配置 tabBar 的运行不会覆盖其他平台已写入的属性。注意 `minify: true` 的输出无法携带注释,属性会变成最后写入的说了算,请对同一项目所有平台的构建保持一致的格式化设置;
|
|
501
|
+
- 手写的 `#ifndef` 块(插件自身只会写 `#ifdef`)表达不了「按平台列表」的规则,会原样保留、不参与合并,tabBar 属性上的 `#ifndef` 同样处理。页面与 tabBar 条目按「只包裹紧随其后的那一个条目」解释;tabBar 的外观属性支持一个条件块包住多个属性——块里每个属性会各自带上完整的 `#ifndef`/`#endif` 包裹重新输出(属性之间互相独立,各平台看到的显示/隐藏结果不变),`#ifdef` 多属性块同理按注释里列出的平台归属每个属性。扫描结果与某个 `#ifndef` 条目内容相同时,会并进该条目,避免当前平台出现重复路由。注意两个边界:当前平台恰好是被排除的平台时,内容相等的配置按 `#ifndef` 语义同样被隐藏;同一 tabBar 属性同时存在 `#ifndef` 副本与各平台不同取值的变体时,被多份副本覆盖的平台会出现重复键,按 JSON 规则后面的键覆盖前面的(当前平台自己的取值写在最后、优先生效)。
|
|
258
502
|
|
|
259
503
|
这样每个终端都能保留各自的平台配置。条件编译注释基于 `comment-json` 写入,最终 `pages.json` 同时包含所有平台的分支。
|
|
260
504
|
|
|
261
|
-
|
|
505
|
+
### 生成标记与过期子包清理
|
|
506
|
+
|
|
507
|
+
自 0.5.0 起,插件会在 `pages.json` 的 `pages` 与每个 `subPackages` 条目的头部写入生成标记注释(如 `// GENERATED BY UNI-PAGES, PLATFORM: H5 || MP-WEIXIN`),记录写入过该数组的平台全集。
|
|
508
|
+
|
|
509
|
+
平台全集**始终包含当前运行的平台**,即使本次运行没有向该数组贡献任何条目(例如所有页面都在当前平台通过 `definePage(null)` 跳过)。这样其他平台专属的条目会保持 `#ifdef` 包裹、不会不加包裹地输出:uni-app 条件编译下,没被包裹的条目所有平台都看得见,不加包裹会把它们漏进当前平台的构建产物。平台全集还会把上一次运行记在标记里的平台算进来,并且只增不减:某平台的条目被它自己的扫描重写后,它也不会从全集中消失,避免多终端交替写入时,残留的条目被误判成盖住全平台、不加包裹地输出。
|
|
510
|
+
|
|
511
|
+
基于该标记,插件会自动清理由它生成、但在当前平台扫描中已不存在的条目:
|
|
512
|
+
|
|
513
|
+
- **页面条目**:主包 `pages` 中带生成标记但本次扫描没找到的条目,会把当前平台从它的平台列表里去掉;其他平台的 `#ifdef` 条目保留。
|
|
514
|
+
- **子包条目**:带生成标记且本次扫描没找到的子包(所有页面都被 `definePage(null)` 跳过,或目录被删除),当前平台已经看不到它的任何页面,整个子包条目会从 `pages.json` 中删除。这里不会保留其他平台条目的空壳:当前平台的构建产物里没有该分包目录,app.json 里留空壳会指向一个不存在的 root。删除不会丢状态——每个平台每次运行都会从自己的扫描结果重写条目,其他平台下次写入时会把子包重新写回。
|
|
515
|
+
- **子包 `plugins`**:`subPackages` 配置里的 `plugins` 跟着配置走——配置里有就随子包写进 `pages.json`,配置里删掉后就从插件生成的子包(带生成标记)上自动清掉,不会一直留在产物里。手写子包(没有生成标记)的 `plugins` 不受影响。
|
|
516
|
+
- **用户手写内容**:没有生成标记的条目视为用户手写,插件永远不会修改或删除它们。如果你在 `pages.config.ts` 的 `subPackages` 里手动维护子包,或直接在 `pages.json` 里手写子包条目,它们不受清理影响。在 `pages.config.ts` 的子包里写 `pages` 时,`path` 按 `pages.json` 的惯例**相对 `root` 书写**(如 `root` 为 `pkg` 时写 `detail`):同一个页面文件扫描到时,两条会按页合并成一条,你写的属性(如 `style`)覆盖扫描结果。
|
|
517
|
+
|
|
518
|
+
> 注意:这个约定依赖生成标记的存在。不要删除插件写入的 `// GENERATED BY UNI-PAGES, PLATFORM: ...` 注释行,否则对应条目会被视为用户手写,过期后不再被清理。
|
|
519
|
+
|
|
520
|
+
### 平台全集只增不减
|
|
521
|
+
|
|
522
|
+
平台全集只增不减:某平台写入过一次,它就会一直留在标记里(即使该平台的开发终端之后再也不运行)。多算的平台唯一的影响,是让各平台不相同的条目一直包着 `#ifdef`——多余但无害,不会漏东西。
|
|
523
|
+
|
|
524
|
+
如果确实彻底停用了某个平台、想让 `pages.json` 回到干净状态:**不要手动编辑标记行**(改错会导致条目被误判为用户手写,或不加包裹地漏出去),直接删除生成的 `pages.json`,下次运行会从零重新生成。
|
|
525
|
+
|
|
526
|
+
另外,「用户手写内容永不修改或删除」的保护范围仅指上述清理逻辑覆盖的场景(带生成标记的子包条目)。`pages` 数组中不带生成标记、也不在本次扫描结果里的手写页面条目,会在合并时因平台列表为空而被丢弃——这是历史行为;需要长期保留的手写页面建议放进 `pages.config.ts` 的 `pages` 数组,插件每次都会原样并入。
|
|
527
|
+
|
|
528
|
+
## 开发
|
|
529
|
+
|
|
530
|
+
环境搭建、常用命令、测试与项目结构见仓库根目录的 [CONTRIBUTING.md](../../CONTRIBUTING.md)。
|
|
531
|
+
|
|
532
|
+
## 架构
|
|
533
|
+
|
|
534
|
+
插件围绕 `PageContext` 组织核心流程,流水线阶段顺序固定(加载用户配置 → 扫描页面 → 合并页面配置 → 写入 pages.json),各专项能力放在独立的模块里:
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
index.ts Vite 插件入口 — configResolved / transform / configureServer / resolveId / load
|
|
538
|
+
context.ts PageContext 编排核心 — 配置加载、扫描、合并、监听、虚拟模块与 HMR
|
|
539
|
+
pipeline.ts 纯流水线入口 — createPages / generateAll,root 和 platform 从外部传入,测试直接走这里
|
|
540
|
+
pages-json.ts pages.json 读-改-写 — 多平台 #ifdef 合并、首页排前、
|
|
541
|
+
生成标记与过期子包清理、序列化格式化、文件锁与原子写入
|
|
542
|
+
files.ts 文件工具 — 页面扫描、路径解析、文件锁(withFileLock)、pages.json 合法性检查
|
|
543
|
+
macro.ts definePage 宏解析 — SFC 解析、单个 script 块失败不影响别的块、
|
|
544
|
+
对象 / 函数 / 异步函数三种形态求值
|
|
545
|
+
condition.ts 按平台写配置 — define().ifdef()/.ifndef(),扫描时就算成当前平台的普通对象
|
|
546
|
+
page.ts 页面实体 — 文件读取、宏求值、变更检测、跳过状态
|
|
547
|
+
options.ts 选项解析 — 默认值合并、glob 目录解析、subPackages root 映射
|
|
548
|
+
declaration.ts uni-pages.d.ts 生成,为导航 API 提供路径类型检查
|
|
549
|
+
config.ts defineUniPages 辅助函数 + 类型重导出
|
|
550
|
+
constant.ts 常量 — 虚拟模块 ID、页面文件扩展名(vue / nvue / uvue)
|
|
551
|
+
logger.ts 分类 debug 日志
|
|
552
|
+
types.ts 公共类型定义(Options / UserOptions / ResolvedOptions)
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### 模块依赖关系
|
|
556
|
+
|
|
557
|
+
```shell
|
|
558
|
+
index.ts
|
|
559
|
+
└─ context.ts(编排核心)
|
|
560
|
+
├─ options.ts 选项解析
|
|
561
|
+
├─ files.ts 扫描与文件锁
|
|
562
|
+
├─ page.ts ── macro.ts ── condition.ts
|
|
563
|
+
├─ pages-json.ts read-modify-write(内含文件锁)
|
|
564
|
+
└─ declaration.ts
|
|
565
|
+
pipeline.ts ── context.ts 测试与外部调用的流水线入口
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### 插件生命周期
|
|
569
|
+
|
|
570
|
+
插件通过 Vite 的生命周期钩子驱动,顺序如下:
|
|
571
|
+
|
|
572
|
+
1. **插件工厂调用**(同步)— 预检 `pages.json` 是否存在且合法(此时 `config.root` 未知,回退到 `VITE_ROOT_DIR` / `process.cwd()` 解析路径)
|
|
573
|
+
2. **`configResolved`**(异步)— 创建 `PageContext`,检测是否与 `vite-plugin-uni-platform` 协同,执行首次 `updatePagesJSON()` 生成 pages.json;`build --watch` 模式下另建 chokidar 监听页面目录
|
|
574
|
+
3. **`configureServer`**(dev)— 复用 Vite 的 `server.watcher`,把配置文件源(`pages.config.ts` 等)加入监听;变更时重跑完整流水线,失效虚拟模块并通知浏览器 full-reload
|
|
575
|
+
4. **`transform`** — 从 vue / nvue / uvue 文件中移除 `definePage` 宏调用,避免运行时报错
|
|
576
|
+
5. **`resolveId` / `load`** — 提供 `virtual:uni-pages` 虚拟模块,暴露所有页面元数据
|
|
577
|
+
|
|
578
|
+
### 关键设计决策
|
|
579
|
+
|
|
580
|
+
- **统一入口**:`PageContext.updatePagesJSON()` 是唯一的完整流水线入口,阶段顺序封装其内,调用方(Vite 钩子、watcher 回调、测试)无需知道内部阶段。
|
|
581
|
+
- **read-modify-write + 生成标记**:平台状态保存在 `pages.json` 自身(`// GENERATED BY UNI-PAGES, PLATFORM: ...` 标记),无需外部缓存文件;无标记的条目视为用户手写,插件永不修改。过期生成物基于该标记自动清理。
|
|
582
|
+
- **文件锁 + 原子写入**:整个「读 → 合并 → 写」在同一个 `withFileLock` 内执行,写入走 tmp + rename,多终端并发(如同时跑 mp-weixin 和 mp-alipay)不会互相覆盖或读到写了一半的内容。
|
|
583
|
+
- **内容不变就不写**:写入前先和磁盘上的内容比一遍,没变化就跳过,避免触发下游不必要的重编译。
|
|
584
|
+
- **统一监听**:配置文件与页面文件共用同一个 chokidar(dev 下直接复用 Vite 的 watcher,不额外创建);配置变更不做增量,直接重跑完整流水线,配合「内容不变就不写」避免多余的磁盘写入。
|
|
585
|
+
- **增量页面更新**:页面文件变更时先经 `hasChanged()` 检测页面配置是否真的变化,无变化则跳过整轮流水线。
|
|
586
|
+
- **宏解析失败隔离**:每个 `<script>` 块独立解析,单块语法错误(如 Babel 8 已移除的 `assert` import attributes)只影响该块,不会阻断其它块的宏移除。
|
|
587
|
+
- **无导入时副作用**:所有文件系统操作都在插件生命周期内执行,而非模块导入时,模块可独立测试。
|
|
588
|
+
- **可从外部传入的入口**:`pipeline.ts` 的 `root` / `platform` 从外部传入,测试不绑定进程环境变量,平台相关快照结果确定。
|
|
262
589
|
|
|
263
590
|
## 感谢
|
|
264
591
|
|
|
265
592
|
- [hannoeru/vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages)
|
|
266
593
|
- [uni-ku/pages-json](https://github.com/uni-ku/pages-json)
|
|
594
|
+
|
|
595
|
+
## 关联项目
|
|
596
|
+
|
|
597
|
+
- [@uni-helper/vite-plugin-uni-pages](https://github.com/uni-helper/vite-plugin-uni-pages/tree/main/packages/core) - 使用 TypeScript 编写 uni-app 的 pages.json,支持约定式路由
|
|
598
|
+
- [@uni-helper/pages-json-schema](https://github.com/uni-helper/vite-plugin-uni-pages/tree/main/packages/schema) - 为 uni-app 的 pages.json 提供 schema
|
|
599
|
+
- [@uni-helper/uni-pages-types](https://github.com/uni-helper/vite-plugin-uni-pages/tree/main/packages/types) - 为 uni-app 的 pages.json 提供 TypeScript 类型
|
|
600
|
+
- [uni-helper/vite-plugin-uni-manifest](https://github.com/uni-helper/vite-plugin-uni-manifest) - 使用 TypeScript 来编写 uni-app 的 manifest.json
|
|
601
|
+
- [uni-helper/vite-plugin-uni-platform](https://github.com/uni-helper/vite-plugin-uni-platform) - 基于文件名 (*.<h5|mp-weixin|app>.*) 的按平台编译插件
|
|
602
|
+
- [uni-helper/vite-plugin-uni-platform-modifier](https://github.com/uni-helper/vite-plugin-uni-platform-modifier) - 为属性、指令提供平台修饰符并按需编译
|
|
603
|
+
- [uni-helper/vite-plugin-uni-layouts](https://github.com/uni-helper/vite-plugin-uni-layouts) - 为 Vite 下的 uni-app 提供类 nuxt 的 layouts 系统
|
|
604
|
+
- [uni-ku/root](https://github.com/uni-ku/root) - 解决 uni-app 无法使用根部组件问题
|
package/client.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/// <reference path="./global.d.ts" />
|
|
2
2
|
|
|
3
3
|
declare module 'virtual:uni-pages' {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export const pages: PageMetaDatum[]
|
|
7
|
-
export const subPackages: SubPackage[]
|
|
4
|
+
export const pages: import('.').InternalPages
|
|
5
|
+
export const subPackages: import('@uni-helper/uni-pages-types').SubPackages
|
|
8
6
|
}
|