删除多个完成报告文件,优化项目结构以提升可维护性。
This commit is contained in:
@@ -22,11 +22,18 @@
|
||||
- 开发流程和最佳实践
|
||||
- 快速检查清单
|
||||
|
||||
### reference.md
|
||||
Next → 小程序转化参考(详细步骤),包含:
|
||||
- 架构与 newpp 配置、适配层、功能控制一致
|
||||
- 底部菜单必须用自定义组件(不要用原生 tabBar)
|
||||
- UI 来源策略、样式兼容、**安全区与标题/胶囊避让**
|
||||
- 构建与合并流程、检查清单
|
||||
|
||||
### troubleshooting.md
|
||||
详细的故障排查指南,包含:
|
||||
- 编译问题(chunk 文件、Babel、依赖)
|
||||
- 从 Next 迁移要点、编译问题(chunk 文件、Babel、依赖)
|
||||
- 运行时问题(URLSearchParams、localStorage、window/document)
|
||||
- 样式问题(Grid、盒模型、Flexbox)
|
||||
- 样式问题(Grid、盒模型、**标题/头部被胶囊或电池栏遮挡**、Flexbox)
|
||||
- 路由问题(导航、switchTab、动态路由)
|
||||
- 网络问题(API 请求、跨域)
|
||||
- 性能问题(加载慢、卡顿)
|
||||
@@ -46,9 +53,10 @@ Agent 应该在以下情况下自动应用此技能:
|
||||
- 需要配置 `webpack.mp.config.js`
|
||||
|
||||
2. **代码转换**
|
||||
- 将 React Web 应用转换为小程序
|
||||
- 将 React Web 应用转换为小程序(含 Next 项目转化)
|
||||
- 需要创建跨平台适配层
|
||||
- 处理 Web 和小程序的 API 差异
|
||||
- 底部菜单保留自定义组件(不要用原生 tabBar)、安全区与标题避让
|
||||
|
||||
3. **问题排查**
|
||||
- 编译错误(chunk 文件、Webpack 配置)
|
||||
@@ -129,10 +137,14 @@ function isMiniProgram() {
|
||||
|
||||
| 问题 | 文档位置 |
|
||||
|------|---------|
|
||||
| redirect 跳转异常(用了 home) | SKILL.md > 必记坑点 1 / troubleshooting.md > 配置问题 1 |
|
||||
| Babel 报错(?. / ??) | SKILL.md > 必记坑点 2 / troubleshooting.md > 编译问题 2 |
|
||||
| chunk 文件缺失 | SKILL.md > 问题 2 |
|
||||
| URLSearchParams 错误 | SKILL.md > 问题 3 / troubleshooting.md > 运行时问题 1 |
|
||||
| CSS Grid 不生效 | SKILL.md > 问题 1 / troubleshooting.md > 样式问题 1 |
|
||||
| 底部导航动态显示 | SKILL.md > 问题 4 |
|
||||
| 底部菜单/导航(不要用原生 tabBar) | SKILL.md > 问题 4 / reference.md > 4.5 |
|
||||
| 安全区/标题被胶囊或电池栏遮挡 | reference.md > 第 7 节 / troubleshooting.md > 样式问题 4 |
|
||||
| 阅读页上下章/数据源 | SKILL.md > 必记坑点 3、4 |
|
||||
| 页面跳转失败 | troubleshooting.md > 路由问题 1 |
|
||||
| API 请求失败 | troubleshooting.md > 网络问题 1 |
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React
|
||||
|
||||
2. **适配层(必须)**
|
||||
- `adapters/env.js`:`isMiniProgram()`(`typeof wx !== 'undefined' && wx.getSystemInfoSync`)。
|
||||
- `adapters/request.js`:Web 用 `fetch`,小程序用 `wx.request`,统一返回 Promise。
|
||||
- `adapters/request.js`:Web 用 `fetch`(相对路径同源);小程序用 `wx.request`,**必须带完整 baseUrl**。本项目线上 API 域名为 **`https://soul.quwanzhi.com`**(见开发文档 `开发文档/8、部署/当前项目部署到线上.md`)。转化时:小程序侧 `request(url)` 实际请求 `baseUrl + url`,`baseUrl` 来自 `getApp().globalData.baseUrl`,壳的 `app.js` 的 `onLaunch` 前应设置 `globalData.baseUrl = 'https://soul.quwanzhi.com'`(或从配置读),**不要写死为 your-domain.com**。
|
||||
- `adapters/storage.js`:Web 用 `localStorage`,小程序用 `wx.getStorageSync`/`setStorageSync`。
|
||||
- `adapters/router.js`:Web 用 `window.location` 或 Next `router`,小程序用 `wx.navigateTo`/`wx.reLaunch`;路径转小程序页面路径 `toMpPath(path)`。
|
||||
- **功能控制一致**:与 Next 端相同,用同一套 API(如 `/api/db/config`)和条件渲染(如 `features.matchEnabled` 控制「找伙伴」入口),不写死开关。
|
||||
@@ -48,21 +48,26 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React
|
||||
|
||||
4. **miniprogram.config.js 要点**
|
||||
- `router`:每个页面单独 key,例如 `index`、`chapters`、`read`、`my`、`match`,对应 `entry` 与小程序页面路径。
|
||||
- 动态底部导航:不配置原生 `tabBar`,在 `appExtraConfig` 中不写 `tabBar`;底部栏用 React 组件 + `wx.reLaunch` 跳转。
|
||||
- **底部菜单(重要)**:Web 端多为**自定义底部菜单组件**,有显隐逻辑(如某页不显示、根据 API 动态显隐某项),原生 tabBar 丑且可操作性差。转化时**不要**改为小程序原生 tabBar,应保留「主页面引入统一菜单组件」的方式:在 `appExtraConfig` 中**不配置** `tabBar`,各主页面(首页、目录、我的、找伙伴等)继续引入同一底部菜单组件,由组件控制显隐、样式与跳转(小程序内用 `wx.reLaunch`),与 Web 行为一致。
|
||||
|
||||
5. **样式兼容(与 Next 视觉一致)**
|
||||
- Grid → Flex(含 `boxSizing: 'border-box'`、必要时 `lineHeight`)。
|
||||
- `backdrop-filter` / `position: sticky` 等不支持则用占位或纯色/渐变替代,保证布局不错乱。
|
||||
- 单位:小程序侧可用 rpx,与 Web 的 rem/px 按设计稿做一次换算或共用同一套换算规则。
|
||||
|
||||
6. **构建与合并**
|
||||
6. **安全区与标题避让(必须)**
|
||||
- **顶部/电池栏**:使用 **`navBarHeight`**(状态栏 + 胶囊区域总高),不用固定 `statusBarHeight + 44`。在壳的 `app.js` 的 `onLaunch` 里用 `wx.getSystemInfoSync()` + `wx.getMenuButtonBoundingClientRect()` 计算并写入 `globalData.navBarHeight`、`globalData.statusBarHeight`;无菜单按钮时回退 `statusBarHeight + 44`。每页顶部占位条高度设为 `navBarHeight`,内容区 `padding-top` 用 `statusBarHeight`。
|
||||
- **底部安全区**:底部导航/固定底栏必须加 `padding-bottom: env(safe-area-inset-bottom)`,避免在有底部刘海的设备上被遮挡。
|
||||
- **标题右侧防遮挡**:小程序右上角胶囊会覆盖标题/按钮。所有带标题或右侧按钮的头部容器必须**右侧留白**:用 `globalData.capsulePaddingRight` 内联 `padding-right`,或在全局样式中定义 `.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }`,避免标题、返回按钮与胶囊重叠或被遮挡。若出现标题被挡,优先检查是否加了该留白。
|
||||
|
||||
7. **构建与合并**
|
||||
- 在 newpp 执行 `NODE_ENV=production npm run build:mp`,将 `dist/mp/common/` 及 mp-plugin 生成的页面目录合并到 `miniprogram/`。
|
||||
- 合并时保留 miniprogram 壳的 `app.js` 全局数据与生命周期,只覆盖/新增 Kbone 生成的页面与 common 资源。
|
||||
|
||||
### 与 Kbone 规则的关系
|
||||
|
||||
- **router**:遵循本技能「核心配置规范」——每页单独配置、不用 `other`。
|
||||
- **底部导航**:遵循「问题 4:底部导航动态显示」——不配置原生 tabBar,用自定义组件 + `wx.reLaunch`。
|
||||
- **底部菜单**:遵循「问题 4:底部导航动态显示」——**不要用原生 tabBar**;保留 Web 的自定义底部菜单组件,主页面统一引入该组件,显隐与样式逻辑与 Web 一致,跳转用 `wx.reLaunch`。
|
||||
- **API/兼容**:遵循「跨平台适配层」与「问题 3:URLSearchParams」——全部走适配层,避免 Web 独有 API。
|
||||
- **样式**:遵循「问题 1:样式错位」与 troubleshooting 中的 Grid/Flex、box-sizing、lineHeight 等。
|
||||
|
||||
@@ -78,7 +83,7 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
origin: 'https://your-domain.com',
|
||||
origin: 'https://soul.quwanzhi.com', // 本项目线上域名,见开发文档 8、部署
|
||||
entry: '/',
|
||||
|
||||
// ✅ 正确:每个页面单独配置
|
||||
@@ -95,6 +100,12 @@ module.exports = {
|
||||
// other: ['/chapters', '/read/:id', ...],
|
||||
// },
|
||||
|
||||
// ✅ redirect 必须用 router 里存在的页面名(如 index),不能写不存在的 'home'
|
||||
redirect: {
|
||||
notFound: 'index',
|
||||
accessDenied: 'index',
|
||||
},
|
||||
|
||||
// 全局配置
|
||||
global: {
|
||||
rem: true, // 支持 rem 单位
|
||||
@@ -182,6 +193,15 @@ module.exports = {
|
||||
|
||||
---
|
||||
|
||||
## 必记坑点(项目实践)
|
||||
|
||||
1. **redirect 必须用 router 里存在的页面名**:`notFound` / `accessDenied` 填 router 的 key(如 `index`),不要填不存在的 `home`。
|
||||
2. **Babel 6 不支持 ?. 和 ??**:官方 React 模板是 Babel 6 + stage-3,用 `(x && x.y)` 替代 `x?.y`,用 `x != null ? x : default` 替代 `x ?? default`,否则 build:mp 报 `Unexpected token`。
|
||||
3. **阅读页内容与上下章用同一数据源**:用 `useChapterContent` + `useChapters` 的 getNextSection/getPrevSection,不要混用静态 bookData 与 API。
|
||||
4. **useChapters 需暴露 getNextSection / getPrevSection**:有阅读页且 API 驱动时,供上一章/下一章使用。
|
||||
|
||||
---
|
||||
|
||||
## 常见问题解决
|
||||
|
||||
### 问题 1:样式错位
|
||||
@@ -270,27 +290,30 @@ const queryString = buildQueryString({ key: 'value', page: 1 })
|
||||
|
||||
---
|
||||
|
||||
### 问题 4:底部导航动态显示
|
||||
### 问题 4:底部导航动态显示(必须用自定义组件,不要用原生 tabBar)
|
||||
|
||||
**场景**:需要根据 API 配置动态显示/隐藏某些导航项
|
||||
**背景**:Web 端通常用**自定义底部菜单组件**,主页面统一引入,有显隐逻辑(如某路由不显示菜单、根据 API 动态显示/隐藏「找伙伴」等项);原生 tabBar 样式丑、可操作性差,且无法灵活控制显隐。
|
||||
|
||||
**方案**:使用完全自定义的导航组件,不配置原生 tabBar
|
||||
**要求**:转化时**不要**把 Web 的自定义底部菜单改成小程序原生 `tabBar`,应保留「统一菜单组件」方式。
|
||||
|
||||
**方案**:
|
||||
- 在 `appExtraConfig` 中**不配置** `tabBar`。
|
||||
- 各主页面(首页、目录、我的、找伙伴等)继续引入**同一底部菜单组件**(如 BottomNav),由该组件负责:显隐(与 Web 一致,如文档/阅读页不显示)、菜单项动态显隐(如根据 `matchEnabled` 显示/隐藏「找伙伴」)、样式与跳转。
|
||||
- 跳转使用 `wx.reLaunch`(因未配置原生 tabBar,不能用 `wx.switchTab`)。
|
||||
|
||||
```javascript
|
||||
// miniprogram.config.js
|
||||
appExtraConfig: {
|
||||
sitemapLocation: 'sitemap.json',
|
||||
// ✅ 不配置 tabBar,使用完全自定义的导航组件
|
||||
// 原因:需要根据 API 配置动态显示/隐藏功能
|
||||
// ✅ 不配置 tabBar,保留 Web 的自定义底部菜单组件
|
||||
// 原因:显隐逻辑、样式与可操作性需与 Web 一致
|
||||
},
|
||||
```
|
||||
|
||||
```javascript
|
||||
// router adapter
|
||||
// router adapter(底部菜单点击时)
|
||||
export function switchTab(path) {
|
||||
if (isMiniProgram()) {
|
||||
// ✅ 使用 wx.reLaunch 代替 wx.switchTab
|
||||
// 原因:没有配置原生 tabBar
|
||||
wx.reLaunch({ url: toMpPath(path) })
|
||||
} else {
|
||||
window.location.href = path === '/' ? 'index.html' : path.replace(/^\//, '') + '.html'
|
||||
@@ -321,12 +344,14 @@ export function navigateTo(path) {
|
||||
}
|
||||
}
|
||||
|
||||
// adapters/request.js
|
||||
// adapters/request.js(小程序侧必须用完整 URL,baseUrl 见 globalData)
|
||||
export function request(url, options) {
|
||||
if (isMiniProgram()) {
|
||||
const baseUrl = (typeof getApp === 'function' && getApp().globalData?.baseUrl) || 'https://soul.quwanzhi.com'
|
||||
const fullUrl = url.startsWith('http') ? url : baseUrl + url
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url,
|
||||
url: fullUrl,
|
||||
method: options.method || 'GET',
|
||||
data: options.body,
|
||||
success: res => resolve(res.data),
|
||||
@@ -464,13 +489,13 @@ appExtraConfig: {
|
||||
}
|
||||
```
|
||||
|
||||
**动态导航**:使用自定义组件
|
||||
**动态导航 / Web 自定义底部菜单**:必须用自定义组件,不要用原生 tabBar
|
||||
```javascript
|
||||
appExtraConfig: {
|
||||
// 不配置 tabBar
|
||||
}
|
||||
// 使用 React 组件实现导航
|
||||
// 使用 wx.reLaunch 进行跳转
|
||||
// Web 端多为统一底部菜单组件,主页面引入;转化时保留该方式:
|
||||
// 各主页面引入同一 BottomNav 等组件,显隐/样式与 Web 一致,跳转用 wx.reLaunch
|
||||
```
|
||||
|
||||
---
|
||||
@@ -480,6 +505,7 @@ appExtraConfig: {
|
||||
### 配置检查
|
||||
|
||||
- [ ] router 每个页面单独配置
|
||||
- [ ] **redirect.notFound / accessDenied 使用 router 中存在的页面名(如 index)**
|
||||
- [ ] pages 配置了所有页面标题
|
||||
- [ ] global 启用了 rem 和 pageStyle
|
||||
- [ ] webpack mode 根据环境判断
|
||||
@@ -489,6 +515,7 @@ appExtraConfig: {
|
||||
|
||||
- [ ] 所有 CSS Grid 替换为 Flexbox
|
||||
- [ ] 添加了 boxSizing: 'border-box'
|
||||
- [ ] **没有使用可选链 ?. 和空值合并 ??(Babel 6 不支持)**
|
||||
- [ ] 没有使用 URLSearchParams
|
||||
- [ ] 没有使用其他 Web 独有 API
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
### 2.1 miniprogram.config.js
|
||||
|
||||
- `router`:每个页面单独 key,对应 Next 路由,**不要使用 `other` 数组**。
|
||||
- 动态底部导航:`appExtraConfig` 中**不配置** `tabBar`,用自定义 React 组件 + `wx.reLaunch`。
|
||||
- **`redirect`**:`notFound`、`accessDenied` 必须填 **router 里存在的页面名**(如 `index`),不要填不存在的 `home`。
|
||||
- **底部菜单**:`appExtraConfig` 中**不配置** `tabBar`。Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),转化时**不要**改为小程序原生 tabBar,保留「统一菜单组件」+ `wx.reLaunch` 跳转。
|
||||
- `global`:建议 `rem: true`、`pageStyle: true`。
|
||||
- `pages`:为每个页面配置 `extra.navigationBarTitleText`。
|
||||
|
||||
@@ -56,12 +57,18 @@ appExtraConfig: {
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `adapters/env.js` | `isMiniProgram()`:`typeof wx !== 'undefined' && wx.getSystemInfoSync` |
|
||||
| `adapters/request.js` | Web:`fetch`;小程序:`wx.request`,统一返回 Promise |
|
||||
| `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. 功能控制一致
|
||||
@@ -74,6 +81,18 @@ appExtraConfig: {
|
||||
|
||||
---
|
||||
|
||||
## 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`。
|
||||
@@ -91,7 +110,40 @@ appExtraConfig: {
|
||||
|
||||
---
|
||||
|
||||
## 7. 构建与合并流程
|
||||
## 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/`。
|
||||
@@ -100,11 +152,12 @@ appExtraConfig: {
|
||||
|
||||
---
|
||||
|
||||
## 8. 检查清单
|
||||
## 9. 检查清单
|
||||
|
||||
- [ ] miniprogram.config.js:每页单独 router,无 `other`;未配置原生 tabBar。
|
||||
- [ ] miniprogram.config.js:每页单独 router,无 `other`;**未配置原生 tabBar**,底部菜单沿用 Web 的自定义组件(主页面统一引入,显隐与跳转逻辑一致)。
|
||||
- [ ] webpack.mp.config.js:每页有 entry;isOptimize 按 NODE_ENV;中小型项目 splitChunks 为 false。
|
||||
- [ ] 适配层:env、request、storage、router 已实现并在业务中统一使用。
|
||||
- [ ] 功能开关与 Next 一致,来自同一 API。
|
||||
- [ ] 样式:无 Grid,Flex 已加 boxSizing/lineHeight;不支持特性已替代。
|
||||
- [ ] **安全区**:app.js 的 onLaunch 中已计算并写入 `navBarHeight`、`statusBarHeight`、`capsulePaddingRight`;每页顶部占位用 `navBarHeight`;底部导航有 `padding-bottom: env(safe-area-inset-bottom)`;带标题的头部有 `.safe-header-right` 或 `capsulePaddingRight` 留白,标题/按钮未被胶囊遮挡。
|
||||
- [ ] 合并后 app.js 未被错误覆盖,页面路径与 app.json 一致。
|
||||
|
||||
@@ -8,15 +8,66 @@
|
||||
- 以 **newpp** 为 Kbone 构建源,**miniprogram** 为壳,构建后合并产物。
|
||||
- 必须做 **适配层**:`env`、`request`、`storage`、`router`;功能开关与 Next 一致,用同一 API(如 `/api/db/config` 的 `features.matchEnabled`)。
|
||||
- **UI**:在 newpp 内复制并适配 Next 的页面/组件,或通过 webpack alias 引用根目录共享组件(无 Next 专属 API)。
|
||||
- **配置**:`miniprogram.config.js` 每页单独 router、不配原生 tabBar;`webpack.mp.config.js` 中 `isOptimize = process.env.NODE_ENV === 'production'`,中小型项目建议 `splitChunks: false`。
|
||||
- **配置**:`miniprogram.config.js` 每页单独 router、**不配原生 tabBar**;`webpack.mp.config.js` 中 `isOptimize = process.env.NODE_ENV === 'production'`,中小型项目建议 `splitChunks: false`。
|
||||
- **底部菜单**:Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),**不要**转为小程序原生 tabBar;保留「主页面引入统一菜单组件」,显隐与跳转与 Web 一致,跳转用 `wx.reLaunch`。
|
||||
- **样式**:Grid 改 Flex,补 `boxSizing`/`lineHeight`;不支持特性用兼容写法或替代。
|
||||
|
||||
遇到具体报错时,在下方「编译问题」「运行时问题」「样式问题」中按类型排查。
|
||||
遇到具体报错时,在下方「配置问题」「编译问题」「运行时问题」「样式问题」中按类型排查。
|
||||
|
||||
---
|
||||
|
||||
## 配置问题
|
||||
|
||||
### 1. redirect 用了不存在的页面名
|
||||
|
||||
**现象**:配置了 `redirect.notFound: 'home'`、`redirect.accessDenied: 'home'`,但 router 里没有名为 `home` 的页面。
|
||||
|
||||
**后果**:404 或无权时,Kbone 可能无法正确跳转到首页。
|
||||
|
||||
**解决**:redirect 必须使用 **router 里存在的 key**。首页通常是 `index`,不要写 `home`。
|
||||
|
||||
```javascript
|
||||
// ❌ 错误
|
||||
redirect: { notFound: 'home', accessDenied: 'home' }
|
||||
|
||||
// ✅ 正确
|
||||
redirect: { notFound: 'index', accessDenied: 'index' }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 编译问题
|
||||
|
||||
### 0. Node 17+ OpenSSL 错误(ERR_OSSL_EVP_UNSUPPORTED)
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
Error: error:0308010C:digital envelope routines::unsupported
|
||||
code: 'ERR_OSSL_EVP_UNSUPPORTED'
|
||||
```
|
||||
|
||||
**原因**:Node.js 17+ 使用 OpenSSL 3.0,旧版 webpack(如 4.x)依赖的 MD4 等算法被移出默认提供,导致构建时报错。
|
||||
|
||||
**解决方案**:
|
||||
|
||||
**方案 1:在 newpp 的 package.json 脚本中加 NODE_OPTIONS**(推荐)
|
||||
```json
|
||||
"scripts": {
|
||||
"web": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=development webpack-dev-server ...",
|
||||
"mp": "rimraf dist/mp/common && cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=development webpack ...",
|
||||
"build:mp": "rimraf dist/mp/common && cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack ..."
|
||||
}
|
||||
```
|
||||
|
||||
**方案 2:当前终端临时设置后再运行**
|
||||
- Windows PowerShell:`$env:NODE_OPTIONS="--openssl-legacy-provider"; cd newpp; npm run web`
|
||||
- Windows CMD:`set NODE_OPTIONS=--openssl-legacy-provider && cd newpp && npm run web`
|
||||
- Linux/macOS:`NODE_OPTIONS=--openssl-legacy-provider npm run web`
|
||||
|
||||
**方案 3**:使用 Node.js 16.x(LTS)运行 newpp 构建,避免 OpenSSL 3.0。
|
||||
|
||||
---
|
||||
|
||||
### 1. chunk 文件缺失错误
|
||||
|
||||
**错误信息**:
|
||||
@@ -74,15 +125,28 @@ optimization: {
|
||||
```
|
||||
SyntaxError: Unexpected token ...
|
||||
```
|
||||
或具体到某列:`Unexpected token (45:64)`(多为可选链位置)
|
||||
|
||||
**原因**:
|
||||
- Babel 配置不正确
|
||||
- 使用了小程序不支持的 ES6+ 语法
|
||||
- 使用了 Babel 6(stage-3)不支持的语法:**可选链 `?.`、空值合并 `??`**(属 ES2020)
|
||||
- 其他小程序不支持的 ES6+ 语法
|
||||
|
||||
**解决方案**:
|
||||
|
||||
**若为可选链 / 空值合并**(最常见):
|
||||
```javascript
|
||||
// ❌ Babel 6 会报错
|
||||
res.content?.length
|
||||
x ?? 'default'
|
||||
|
||||
// ✅ 兼容写法
|
||||
(res.content && res.content.length) || 0
|
||||
x != null ? x : 'default'
|
||||
```
|
||||
|
||||
**Babel 配置**(.babelrc 或 babel.config.js):
|
||||
```javascript
|
||||
// .babelrc 或 babel.config.js
|
||||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
@@ -377,7 +441,26 @@ const styles = {
|
||||
|
||||
---
|
||||
|
||||
### 4. Flexbox 间距问题
|
||||
### 4. 标题/头部被胶囊或电池栏遮挡
|
||||
|
||||
**问题描述**:自定义导航栏时,标题、返回按钮被右上角胶囊或顶部状态栏遮挡。
|
||||
|
||||
**原因**:未预留顶部安全区高度和右侧胶囊留白。
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. **顶部安全区**:在 `app.js` 的 `onLaunch` 中计算并写入 `globalData`:
|
||||
- `statusBarHeight = wx.getSystemInfoSync().statusBarHeight || 44`
|
||||
- `navBarHeight`:用 `wx.getMenuButtonBoundingClientRect()` 计算 `menuButton.bottom + menuButton.top - statusBarHeight`,无菜单时用 `statusBarHeight + 44`
|
||||
- `capsulePaddingRight = systemInfo.windowWidth - menuButton.left + 10`
|
||||
2. **页面占位**:顶部占位条高度设为 `{{ navBarHeight }}px`,内容区 `padding-top: {{ statusBarHeight }}px`;页面 `onLoad`/`onShow` 从 `getApp().globalData` 取上述值写入 `data`。
|
||||
3. **标题右侧留白**:头部容器加类 `.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }`,或内联 `padding-right: {{ capsulePaddingRight }}px`,避免标题与胶囊重叠。
|
||||
|
||||
详见本目录 `reference.md` 第 7 节「安全区与标题/胶囊避让」。
|
||||
|
||||
---
|
||||
|
||||
### 5. Flexbox 间距问题
|
||||
|
||||
**问题描述**:Flexbox 子元素间距不均匀
|
||||
|
||||
@@ -528,7 +611,7 @@ request:fail url not in domain list
|
||||
```javascript
|
||||
// miniprogram.config.js
|
||||
module.exports = {
|
||||
origin: 'https://your-domain.com', // 使用已配置的域名
|
||||
origin: 'https://soul.quwanzhi.com', // 本项目线上域名,见开发文档 8、部署
|
||||
}
|
||||
```
|
||||
|
||||
@@ -563,7 +646,7 @@ devServer: {
|
||||
```javascript
|
||||
// Express 示例
|
||||
app.use(cors({
|
||||
origin: ['http://localhost:8080', 'https://your-domain.com'],
|
||||
origin: ['http://localhost:8080', 'https://soul.quwanzhi.com'],
|
||||
credentials: true,
|
||||
}))
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user