Files
soul-yongping/.cursor/skills/kbone-miniprogram/reference.md

164 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Next → 小程序 转化参考(详细步骤)
`SKILL.md` 中「Next 项目 → 小程序 最优转化方式」对应,用于按步骤执行转化。
---
## 1. 架构与目录约定
| 目录/产物 | 作用 |
|-----------|------|
| `newpp/` | Kbone 源码:多入口 React构建输出到 `newpp/dist/mp/` |
| `miniprogram/` | 小程序壳:`app.js``app.json``custom-tab-bar`、空白页面占位等 |
| Next 根目录 `app/``components/` | Web 端页面与组件;可被 newpp 复制或通过 alias 引用 |
构建后:将 `newpp/dist/mp/` 下的页面与 `common/` 合并进 `miniprogram/`,保留壳的 `app.js` 全局逻辑。
---
## 2. newpp 配置
### 2.1 miniprogram.config.js
- `router`:每个页面单独 key对应 Next 路由,**不要使用 `other` 数组**。
- **`redirect`**`notFound``accessDenied` 必须填 **router 里存在的页面名**(如 `index`),不要填不存在的 `home`
- **底部菜单**`appExtraConfig` 中**不配置** `tabBar`。Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),转化时**不要**改为小程序原生 tabBar保留「统一菜单组件」+ `wx.reLaunch` 跳转。
- `global`:建议 `rem: true``pageStyle: true`
- `pages`:为每个页面配置 `extra.navigationBarTitleText`
示例(按实际路由调整):
```javascript
router: {
index: ['/', '/(index)?', '/index.html'],
chapters: ['/chapters', '/chapters.html'],
read: ['/read/:id', '/read.html'],
my: ['/my', '/my.html'],
match: ['/match', '/match.html'],
},
appExtraConfig: {
sitemapLocation: 'sitemap.json',
// 不配置 tabBar
},
```
### 2.2 webpack.mp.config.js
- `entry`:每个页面一个入口,例如 `index``chapters``read``my``match`,指向 newpp 内对应入口 JSX。
- `isOptimize``const isOptimize = process.env.NODE_ENV === 'production'`,生产构建时再压缩。
- `optimization.splitChunks`:中小型项目建议 `splitChunks: false`,避免 chunk 缺失;大型项目再考虑固定命名 cacheGroups。
---
## 3. 适配层(必须)
在 newpp 内(或共享到根目录再被引用)实现:
| 文件 | 职责 |
|------|------|
| `adapters/env.js` | `isMiniProgram()``typeof wx !== 'undefined' && wx.getSystemInfoSync` |
| `adapters/request.js` | Web`fetch`(相对路径);小程序:`wx.request`**必须带完整 baseUrl**,本项目线上域名为 `https://soul.quwanzhi.com`(见开发文档 8、部署 |
| `adapters/storage.js` | Web`localStorage`;小程序:`wx.getStorageSync`/`setStorageSync` |
| `adapters/router.js` | Web`window.location` 或 Next Router小程序`wx.navigateTo`/`wx.reLaunch`,路径用 `toMpPath(path)` 转成小程序页面路径 |
业务代码**只**通过适配层访问请求、存储、路由,不直接使用 `window``document``localStorage``URLSearchParams` 等。
**请求基地址(转化时必须处理)**:小程序端不能使用相对路径,需用完整 URL。本项目线上 API 域名为 **`https://soul.quwanzhi.com`**,见 `开发文档/8、部署/当前项目部署到线上.md`。转化时:
- 在**壳**的 `miniprogram/app.js``globalData` 中设置 `baseUrl: 'https://soul.quwanzhi.com'`(或从环境/配置读取)。
-**adapters/request.js** 中,小程序分支:`url` 若不以 `http` 开头则拼上 `getApp().globalData.baseUrl`,再调用 `wx.request`
- 不要写死为 `your-domain.com` 或其它占位域名。
---
## 4. 功能控制一致
与 Next 端保持一致:
- 功能开关来自同一 API例如 `/api/db/config``features.matchEnabled`
- 底部导航「找伙伴」等入口根据该配置条件渲染,不在小程序端写死。
- 支付方式、营销二维码等同样由 API 配置驱动,两端逻辑一致。
---
## 4.5 底部菜单:必须用自定义组件,不要用原生 tabBar
- **原因**Web 端底部菜单多为**自定义组件**(如 `BottomNav`),主页面统一引入,有「某页不显示菜单」「根据 API 显隐某项」等逻辑;原生 tabBar 样式与可操作性差,且无法等价实现上述逻辑。
- **要求**:转化时**不要**把自定义底部菜单改成小程序原生 `tabBar`
- **做法**
- `appExtraConfig` 中不配置 `tabBar`
- 各主页面(首页、目录、我的、找伙伴等)**继续引入同一底部菜单组件**,由该组件控制:是否在本页显示、各菜单项显隐(如 `matchEnabled` 控制「找伙伴」)、样式、点击跳转(小程序内用 `wx.reLaunch`)。
- 与 Web 一致:阅读页、文档页、关于页等可不渲染该组件或由布局隐藏。
- **检查**:若发现被改成了原生 tabBar应回退为「主页面引入统一菜单组件」的方式。
---
## 5. UI 来源策略
- **复制适配**:从 Next 的 `app/**/page.tsx``components/**` 复制到 newpp改为 JSX用适配层替代 `Link`/`useRouter`/`usePathname`/`fetch`
- **Alias 引用**:在 newpp 的 webpack `resolve.alias` 中指向项目根目录的共享组件目录,保证该目录内仅用 React + 适配层,无 Next 专属 API。
---
## 6. 样式兼容(与 Next 视觉一致)
- **Grid → Flex**:所有 Grid 布局改为 Flex并加 `boxSizing: 'border-box'`、必要时 `lineHeight`
- **backdrop-filter / sticky**:不支持则用纯色、渐变或占位,保证布局不错位。
- **单位**:小程序侧可用 rpx与 Web 的 rem/px 按设计稿统一换算。
详见 `troubleshooting.md` 中的「样式问题」小节。
---
## 7. 安全区与标题/胶囊避让(必须)
### 7.1 电池栏/顶部安全区
- **问题**:使用自定义导航栏(`navigationStyle: 'custom'`)时,状态栏(电池、时间)会与页面顶部重叠,若不做占位,标题会被挡。
- **做法**
- 在**壳**的 `app.js``onLaunch` 中**同步**获取系统信息并计算占位高度,写入 `globalData`
- `statusBarHeight``wx.getSystemInfoSync().statusBarHeight || 44`
- `navBarHeight`:用 `wx.getMenuButtonBoundingClientRect()` 计算「状态栏 + 胶囊区域」总高,公式:`menuButton.bottom + menuButton.top - systemInfo.statusBarHeight`;无菜单按钮时回退 `statusBarHeight + 44`
- `capsulePaddingRight``systemInfo.windowWidth - menuButton.left + 10`,供标题右侧留白使用
- 每个页面的**顶部占位条**高度设为 `navBarHeight`px占位条内部若需区分状态栏与导航内容可用 `padding-top: {{ statusBarHeight || 44 }}px`
- 页面 `onLoad`/`onShow` 时从 `getApp().globalData``navBarHeight``statusBarHeight` 写入页面 `data`,供 WXML 内联样式使用。
### 7.2 底部安全区
- **问题**:有底部刘海的设备上,固定底栏(如 TabBar、底部导航会贴边内容被遮挡。
- **做法**:底部导航容器加 `padding-bottom: env(safe-area-inset-bottom)`WXSS 或内联均可),无需额外 JS。
### 7.3 标题右侧不被胶囊遮挡
- **问题**:小程序右上角有胶囊按钮(…、首页),若标题或返回按钮延伸到右侧,会被遮挡或误触。
- **做法**
- 所有带标题/返回/右侧按钮的**头部容器**必须预留右侧空白:
- **方式 A**:全局样式 `.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }`,头部容器加该类。
- **方式 B**:内联 `style="padding-right: {{ capsulePaddingRight }}px"``capsulePaddingRight` 来自 `getApp().globalData.capsulePaddingRight`(在 onLaunch 中已计算)。
- 若出现「标题或按钮被挡」,优先检查该头部是否加了上述留白,并确认 `app.js` 中已正确写入 `capsulePaddingRight`
### 7.4 统一占位模板(可选)
- 无复杂导航的页面(如设置、关于、推广、购买记录等)建议使用**同一套**顶部安全区:占位条高度 `navBarHeight`,其内 `padding-top: statusBarHeight`,导航容器 `display: flex; flex-direction: column; justify-content: flex-end; box-sizing: border-box;`,并加 `.safe-header-right``capsulePaddingRight`,保证标题与返回按钮不被遮挡且右侧留白一致。
---
## 8. 构建与合并流程
1. 在 newpp 执行:`NODE_ENV=production npm run build:mp`
2.`newpp/dist/mp/` 下生成的页面目录及 `common/` 复制/合并到 `miniprogram/`
3. 保留 miniprogram 壳的 `app.js`(全局 data、onLaunch、onShow 等),仅覆盖或新增 Kbone 生成的页面与 common。
4. 用微信开发者工具打开 `miniprogram/` 根目录,编译并测试。
---
## 9. 检查清单
- [ ] miniprogram.config.js每页单独 router`other`**未配置原生 tabBar**,底部菜单沿用 Web 的自定义组件(主页面统一引入,显隐与跳转逻辑一致)。
- [ ] webpack.mp.config.js每页有 entryisOptimize 按 NODE_ENV中小型项目 splitChunks 为 false。
- [ ] 适配层env、request、storage、router 已实现并在业务中统一使用。
- [ ] 功能开关与 Next 一致,来自同一 API。
- [ ] 样式:无 GridFlex 已加 boxSizing/lineHeight不支持特性已替代。
- [ ] **安全区**app.js 的 onLaunch 中已计算并写入 `navBarHeight``statusBarHeight``capsulePaddingRight`;每页顶部占位用 `navBarHeight`;底部导航有 `padding-bottom: env(safe-area-inset-bottom)`;带标题的头部有 `.safe-header-right``capsulePaddingRight` 留白,标题/按钮未被胶囊遮挡。
- [ ] 合并后 app.js 未被错误覆盖,页面路径与 app.json 一致。