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

9.5 KiB
Raw Blame History

Next → 小程序 转化参考(详细步骤)

SKILL.md 中「Next 项目 → 小程序 最优转化方式」对应,用于按步骤执行转化。


1. 架构与目录约定

目录/产物 作用
newpp/ Kbone 源码:多入口 React构建输出到 newpp/dist/mp/
miniprogram/ 小程序壳:app.jsapp.jsoncustom-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 数组
  • redirectnotFoundaccessDenied 必须填 router 里存在的页面名(如 index),不要填不存在的 home
  • 底部菜单appExtraConfig不配置 tabBar。Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),转化时不要改为小程序原生 tabBar保留「统一菜单组件」+ wx.reLaunch 跳转。
  • global:建议 rem: truepageStyle: true
  • pages:为每个页面配置 extra.navigationBarTitleText

示例(按实际路由调整):

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:每个页面一个入口,例如 indexchaptersreadmymatch,指向 newpp 内对应入口 JSX。
  • isOptimizeconst 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 Webfetch(相对路径);小程序:wx.request必须带完整 baseUrl,本项目线上域名为 https://soul.quwanzhi.com(见开发文档 8、部署
adapters/storage.js WeblocalStorage;小程序:wx.getStorageSync/setStorageSync
adapters/router.js Webwindow.location 或 Next Router小程序wx.navigateTo/wx.reLaunch,路径用 toMpPath(path) 转成小程序页面路径

业务代码通过适配层访问请求、存储、路由,不直接使用 windowdocumentlocalStorageURLSearchParams 等。

请求基地址(转化时必须处理):小程序端不能使用相对路径,需用完整 URL。本项目线上 API 域名为 https://soul.quwanzhi.com,见 开发文档/8、部署/当前项目部署到线上.md。转化时:

  • miniprogram/app.jsglobalData 中设置 baseUrl: 'https://soul.quwanzhi.com'(或从环境/配置读取)。
  • adapters/request.js 中,小程序分支:url 若不以 http 开头则拼上 getApp().globalData.baseUrl,再调用 wx.request
  • 不要写死为 your-domain.com 或其它占位域名。

4. 功能控制一致

与 Next 端保持一致:

  • 功能开关来自同一 API例如 /api/db/configfeatures.matchEnabled
  • 底部导航「找伙伴」等入口根据该配置条件渲染,不在小程序端写死。
  • 支付方式、营销二维码等同样由 API 配置驱动,两端逻辑一致。

4.5 底部菜单:必须用自定义组件,不要用原生 tabBar

  • 原因Web 端底部菜单多为自定义组件(如 BottomNav),主页面统一引入,有「某页不显示菜单」「根据 API 显隐某项」等逻辑;原生 tabBar 样式与可操作性差,且无法等价实现上述逻辑。
  • 要求:转化时不要把自定义底部菜单改成小程序原生 tabBar
  • 做法
    • appExtraConfig 中不配置 tabBar
    • 各主页面(首页、目录、我的、找伙伴等)继续引入同一底部菜单组件,由该组件控制:是否在本页显示、各菜单项显隐(如 matchEnabled 控制「找伙伴」)、样式、点击跳转(小程序内用 wx.reLaunch)。
    • 与 Web 一致:阅读页、文档页、关于页等可不渲染该组件或由布局隐藏。
  • 检查:若发现被改成了原生 tabBar应回退为「主页面引入统一菜单组件」的方式。

5. UI 来源策略

  • 复制适配:从 Next 的 app/**/page.tsxcomponents/** 复制到 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.jsonLaunch同步获取系统信息并计算占位高度,写入 globalData
      • statusBarHeightwx.getSystemInfoSync().statusBarHeight || 44
      • navBarHeight:用 wx.getMenuButtonBoundingClientRect() 计算「状态栏 + 胶囊区域」总高,公式:menuButton.bottom + menuButton.top - systemInfo.statusBarHeight;无菜单按钮时回退 statusBarHeight + 44
      • capsulePaddingRightsystemInfo.windowWidth - menuButton.left + 10,供标题右侧留白使用
    • 每个页面的顶部占位条高度设为 navBarHeightpx占位条内部若需区分状态栏与导航内容可用 padding-top: {{ statusBarHeight || 44 }}px
    • 页面 onLoad/onShow 时从 getApp().globalDatanavBarHeightstatusBarHeight 写入页面 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-rightcapsulePaddingRight,保证标题与返回按钮不被遮挡且右侧留白一致。

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每页单独 routerother未配置原生 tabBar,底部菜单沿用 Web 的自定义组件(主页面统一引入,显隐与跳转逻辑一致)。
  • webpack.mp.config.js每页有 entryisOptimize 按 NODE_ENV中小型项目 splitChunks 为 false。
  • 适配层env、request、storage、router 已实现并在业务中统一使用。
  • 功能开关与 Next 一致,来自同一 API。
  • 样式:无 GridFlex 已加 boxSizing/lineHeight不支持特性已替代。
  • 安全区app.js 的 onLaunch 中已计算并写入 navBarHeightstatusBarHeightcapsulePaddingRight;每页顶部占位用 navBarHeight;底部导航有 padding-bottom: env(safe-area-inset-bottom);带标题的头部有 .safe-header-rightcapsulePaddingRight 留白,标题/按钮未被胶囊遮挡。
  • 合并后 app.js 未被错误覆盖,页面路径与 app.json 一致。