Files
soul-yongping/.cursor/skills/kbone-miniprogram/troubleshooting.md
2026-02-03 19:17:05 +08:00

18 KiB
Raw Blame History

Kbone 常见问题排查指南

从 Next 项目迁移到小程序

先读:本技能 SKILL.md 中的「Next 项目 → 小程序 最优转化方式」章节。

要点

  • newpp 为 Kbone 构建源,miniprogram 为壳,构建后合并产物。
  • 必须做 适配层envrequeststoragerouter;功能开关与 Next 一致,用同一 API/api/db/configfeatures.matchEnabled)。
  • UI:在 newpp 内复制并适配 Next 的页面/组件,或通过 webpack alias 引用根目录共享组件(无 Next 专属 API
  • 配置miniprogram.config.js 每页单独 router、不配原生 tabBarwebpack.mp.config.jsisOptimize = process.env.NODE_ENV === 'production',中小型项目建议 splitChunks: false
  • 底部菜单Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),不要转为小程序原生 tabBar保留「主页面引入统一菜单组件」显隐与跳转与 Web 一致,跳转用 wx.reLaunch
  • 样式Grid 改 FlexboxSizing/lineHeight;不支持特性用兼容写法或替代。

遇到具体报错时,在下方「配置问题」「编译问题」「运行时问题」「样式问题」中按类型排查。


配置问题

1. redirect 用了不存在的页面名

现象:配置了 redirect.notFound: 'home'redirect.accessDenied: 'home',但 router 里没有名为 home 的页面。

后果404 或无权时Kbone 可能无法正确跳转到首页。

解决redirect 必须使用 router 里存在的 key。首页通常是 index,不要写 home

// ❌ 错误
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(推荐)

"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 CMDset NODE_OPTIONS=--openssl-legacy-provider && cd newpp && npm run web
  • Linux/macOSNODE_OPTIONS=--openssl-legacy-provider npm run web

方案 3:使用 Node.js 16.xLTS运行 newpp 构建,避免 OpenSSL 3.0。


1. chunk 文件缺失错误

错误信息

Error: ENOENT: no such file or directory, open 'E:/path/to/default~chapters~index~my.js'

原因

  • Webpack 代码分割splitChunks生成了动态命名的 chunk 文件
  • 小程序环境下,动态 chunk 路径可能不稳定

解决方案

方案 1完全禁用代码分割(推荐中小型项目)

// webpack.mp.config.js
optimization: {
  runtimeChunk: false,
  splitChunks: false, // 完全禁用
}

方案 2固定 chunk 命名(大型项目)

optimization: {
  runtimeChunk: false,
  splitChunks: {
    chunks: 'all',
    name: true, // 使用固定命名
    cacheGroups: {
      vendors: {
        name: 'vendors',
        test: /[\\/]node_modules[\\/]/,
        priority: 10,
      },
      default: {
        name: 'common',
        minChunks: 2,
        priority: 5,
      },
    },
  },
}

选择建议

  • 项目 <20 页面:方案 1稳定性优先
  • 项目 >50 页面:方案 2体积优化优先

2. Babel 编译错误

错误信息

SyntaxError: Unexpected token ...

或具体到某列:Unexpected token (45:64)(多为可选链位置)

原因

  • Babel 配置不正确
  • 使用了 Babel 6stage-3不支持的语法可选链 ?.、空值合并 ??(属 ES2020
  • 其他小程序不支持的 ES6+ 语法

解决方案

若为可选链 / 空值合并(最常见):

// ❌ Babel 6 会报错
res.content?.length
x ?? 'default'

// ✅ 兼容写法
(res.content && res.content.length) || 0
x != null ? x : 'default'

Babel 配置.babelrc 或 babel.config.js

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "react",
    "stage-3"
  ],
  "plugins": [
    "transform-runtime"
  ]
}

3. 依赖包报错

错误信息

Module not found: Can't resolve 'xxx'

排查步骤

  1. 检查依赖是否安装
npm list [package-name]
  1. 重新安装依赖
rm -rf node_modules package-lock.json
npm install
  1. 检查 webpack resolve 配置
resolve: {
  extensions: ['*', '.js', '.jsx', '.json'],
  alias: {
    '@': path.resolve(__dirname, 'src'),
  },
}

运行时问题

0. TypeError: eval is not a function

错误信息

TypeError: eval is not a function
  at Object../src/index.jsx (index.js?...)

原因npm run mp 使用 development 模式时webpack 默认 devtool: 'eval',会注入使用 eval() 的代码;小程序环境不支持 eval,因此报错。

解决方案:在 webpack.mp.config.js 中显式关闭 devtool

module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  devtool: false,  // 小程序环境不支持 eval必须关闭
  entry: { ... },
  // ...
}

重新执行 npm run mpnpm run build:mp 后,再在微信开发者工具中预览即可。


1. URLSearchParams 未定义

错误信息

ReferenceError: URLSearchParams is not defined

原因:小程序环境不支持 URLSearchParams

解决方案

// 自定义实现
function buildQueryString(params) {
  const parts = []
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined && value !== null) {
      parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
    }
  }
  return parts.join('&')
}

// 使用示例
const queryString = buildQueryString({ 
  page: 1, 
  limit: 10,
  keyword: '搜索' 
})
// 结果: "page=1&limit=10&keyword=%E6%90%9C%E7%B4%A2"

2. localStorage 未定义

错误信息

ReferenceError: localStorage is not defined

原因:小程序环境使用 wx.storage 而不是 localStorage

解决方案

// adapters/storage.js
function isMiniProgram() {
  return typeof wx !== 'undefined' && wx.getSystemInfoSync
}

export const storage = {
  get(key) {
    if (isMiniProgram()) {
      return wx.getStorageSync(key)
    } else {
      const value = localStorage.getItem(key)
      try {
        return JSON.parse(value)
      } catch {
        return value
      }
    }
  },
  
  set(key, value) {
    if (isMiniProgram()) {
      wx.setStorageSync(key, value)
    } else {
      localStorage.setItem(key, JSON.stringify(value))
    }
  },
  
  remove(key) {
    if (isMiniProgram()) {
      wx.removeStorageSync(key)
    } else {
      localStorage.removeItem(key)
    }
  },
  
  clear() {
    if (isMiniProgram()) {
      wx.clearStorageSync()
    } else {
      localStorage.clear()
    }
  },
}

3. window 对象未定义

错误信息

ReferenceError: window is not defined

原因:小程序环境没有 window 对象

解决方案

// 环境判断
function isMiniProgram() {
  return typeof wx !== 'undefined' && wx.getSystemInfoSync
}

// 使用示例
if (!isMiniProgram()) {
  window.addEventListener('resize', handleResize)
}

// 或使用可选链
window?.addEventListener?.('resize', handleResize)

4. document 对象未定义

错误信息

ReferenceError: document is not defined

原因:小程序环境没有 document 对象

解决方案

// ❌ 不要直接使用 document
const title = document.title

// ✅ 使用 React 特性
import { useEffect } from 'react'

function MyComponent() {
  useEffect(() => {
    if (!isMiniProgram()) {
      document.title = 'My Page'
    } else {
      // 小程序使用 wx.setNavigationBarTitle
      wx.setNavigationBarTitle({
        title: 'My Page'
      })
    }
  }, [])
}

样式问题

1. CSS Grid 不生效

问题描述:使用 CSS Grid 布局在小程序中显示错乱

原因:小程序对 CSS Grid 支持不完整

解决方案

// ❌ 避免使用 Grid
const badStyles = {
  container: {
    display: 'grid',
    gridTemplateColumns: 'repeat(4, 1fr)',
    gap: '10px',
  }
}

// ✅ 使用 Flexbox
const goodStyles = {
  container: {
    display: 'flex',
    flexDirection: 'row',
    flexWrap: 'wrap',
    boxSizing: 'border-box',
  },
  item: {
    flex: '0 0 25%', // 相当于 4 列
    boxSizing: 'border-box',
    padding: '5px',
  }
}

2. 盒模型计算错误

问题描述元素尺寸计算不正确padding/border 撑大元素

原因:未设置 box-sizing: border-box

解决方案

// ✅ 所有容器都添加 boxSizing
const styles = {
  container: {
    width: '100%',
    padding: '10px',
    boxSizing: 'border-box', // 重要!
  },
  item: {
    width: '50%',
    border: '1px solid #ccc',
    boxSizing: 'border-box', // 重要!
  }
}

3. 文字垂直居中问题

问题描述:文字没有垂直居中

原因:未设置 line-height

解决方案

const styles = {
  button: {
    height: '44px',
    lineHeight: '44px', // 与 height 相同
    textAlign: 'center',
  },
  text: {
    lineHeight: '1.5', // 或使用相对值
  }
}

4. 标题/头部被胶囊或电池栏遮挡

问题描述:自定义导航栏时,标题、返回按钮被右上角胶囊或顶部状态栏遮挡。

原因:未预留顶部安全区高度和右侧胶囊留白。

解决方案

  1. 顶部安全区:在 app.jsonLaunch 中计算并写入 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/onShowgetApp().globalData 取上述值写入 data
  3. 标题右侧留白:头部容器加类 .safe-header-right { padding-right: 200rpx; box-sizing: border-box; },或内联 padding-right: {{ capsulePaddingRight }}px,避免标题与胶囊重叠。

详见本目录 reference.md 第 7 节「安全区与标题/胶囊避让」。


5. Flexbox 间距问题

问题描述Flexbox 子元素间距不均匀

解决方案

// 方案 1使用 gap注意兼容性
const styles = {
  container: {
    display: 'flex',
    gap: '10px', // 可能不支持
  }
}

// 方案 2使用 margin推荐
const styles = {
  container: {
    display: 'flex',
    margin: '-5px', // 负边距抵消子元素边距
  },
  item: {
    margin: '5px',
  }
}

// 方案 3使用伪元素
// CSS: .item:not(:last-child) { margin-right: 10px; }

路由问题

1. 页面跳转无效

问题描述:点击导航无反应

排查步骤

  1. 检查 router 配置
// miniprogram.config.js
router: {
  index: ['/', '/index.html'],
  my: ['/my', '/my.html'], // 确保配置了目标页面
}
  1. 检查路由适配器
// adapters/router.js
export function navigateTo(path) {
  if (isMiniProgram()) {
    wx.navigateTo({ 
      url: toMpPath(path),
      fail: (err) => console.error('导航失败:', err)
    })
  } else {
    window.location.href = path
  }
}
  1. 检查页面是否存在
# 确保入口文件存在
ls src/my.jsx

2. switchTab 报错

错误信息

navigateTo:fail can not navigateTo a tabbar page

原因:使用 wx.navigateTo 跳转 tabBar 页面

解决方案

// ❌ 错误
wx.navigateTo({ url: '/pages/index/index' })

// ✅ 正确
wx.switchTab({ url: '/pages/index/index' })

// 或使用 wx.reLaunch不依赖 tabBar 配置)
wx.reLaunch({ url: '/pages/index/index' })

3. 动态路由参数丢失

问题描述/read/:id 中的 id 获取不到

解决方案

// 小程序环境
if (isMiniProgram()) {
  // 从页面 options 获取
  const pages = getCurrentPages()
  const currentPage = pages[pages.length - 1]
  const { id } = currentPage.options
}

// Web 环境
else {
  // 从 URL 解析
  const pathParts = window.location.pathname.split('/')
  const id = pathParts[pathParts.length - 1]
  
  // 或使用 React Router
  import { useParams } from 'react-router-dom'
  const { id } = useParams()
}

网络问题

1. API 请求失败

错误信息

request:fail url not in domain list

原因:小程序要求配置服务器域名白名单

解决方案

  1. 小程序管理后台配置

    • 登录小程序管理后台
    • 开发 > 开发设置 > 服务器域名
    • 添加你的 API 域名
  2. 开发阶段临时方案

    • 微信开发者工具 > 右上角详情
    • 勾选"不校验合法域名"
  3. 使用代理

// miniprogram.config.js
module.exports = {
  origin: 'https://soul.quwanzhi.com', // 本项目线上域名,见开发文档 8、部署
}

2. 跨域问题

错误信息

Access to fetch at 'xxx' has been blocked by CORS policy

原因Web 端开发时遇到跨域限制

解决方案

方案 1配置 webpack devServer 代理

// webpack.dev.config.js
devServer: {
  proxy: {
    '/api': {
      target: 'https://your-api.com',
      changeOrigin: true,
      pathRewrite: { '^/api': '' },
    },
  },
}

方案 2后端配置 CORS

// Express 示例
app.use(cors({
  origin: ['http://localhost:8080', 'https://soul.quwanzhi.com'],
  credentials: true,
}))

性能问题

1. 首屏加载慢

排查步骤

  1. 检查包体积
# 查看构建产物大小
ls -lh dist/mp/common/
  1. 启用代码压缩
// webpack.mp.config.js
const isOptimize = process.env.NODE_ENV === 'production'

optimization: {
  minimizer: isOptimize ? [
    new TerserPlugin(),
    new OptimizeCSSAssetsPlugin(),
  ] : [],
}
  1. 使用代码分割(大型项目)
optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: 10,
      },
    },
  },
}

2. 页面卡顿

常见原因

  1. 渲染列表过长
// ✅ 使用虚拟列表
import VirtualList from 'react-virtual-list'

<VirtualList
  items={items}
  itemHeight={50}
  renderItem={(item) => <Item {...item} />}
/>
  1. 频繁重渲染
// ✅ 使用 React.memo
const MyComponent = React.memo(({ data }) => {
  return <div>{data}</div>
})

// ✅ 使用 useMemo
const expensiveValue = useMemo(() => {
  return computeExpensiveValue(data)
}, [data])

调试技巧

1. 查看小程序日志

微信开发者工具

  • 控制台 > Console查看 console.log
  • 控制台 > Network查看网络请求
  • 控制台 > Storage查看本地存储

2. 条件断点

// 只在小程序环境下打印
if (isMiniProgram()) {
  console.log('小程序环境:', data)
}

// 只在 Web 环境下打印
if (!isMiniProgram()) {
  console.log('Web 环境:', data)
}

3. 真机调试

  1. 开启调试模式

    • 微信开发者工具 > 预览
    • 扫码在真机上打开
    • 点击右上角 > 打开调试
  2. 查看 vConsole

    • 小程序右下角会出现绿色悬浮按钮
    • 点击查看日志、网络、存储等信息

检查清单

遇到问题时,按此清单逐项排查:

编译检查

  • 所有依赖已安装(npm install
  • entry 配置正确
  • output 配置未修改必需字段
  • splitChunks 配置合理

兼容性检查

  • 没有使用 URLSearchParams
  • 没有使用 localStorage改用适配器
  • 没有直接使用 window/document
  • CSS 没有使用 Grid改用 Flexbox

配置检查

  • router 每个页面单独配置
  • pages 配置了所有页面
  • 适配器正确处理平台差异

运行检查

  • Web 端能正常运行
  • 小程序能正常编译
  • 微信开发者工具无错误
  • 真机预览正常

获取帮助

官方资源

社区资源

调试工具

  • 微信开发者工具:下载地址
  • React DevTools浏览器扩展
  • vConsole真机调试工具小程序内置