uni-app 转微信小程序 · 避坑与实战全记录

uni-app 转微信小程序 · 避坑与实战全记录

uni-app 一端多套 最适合「轻交互、重展示、预算紧、周期短」的项目,不属于此范畴的自动避让,别掉进去了

这是一份从真实项目血泪史中整理出的 uni-app → 微信小程序 迁移指南。

覆盖分包、样式、组件、API、交互、性能等 全链路 问题,附完整代码片段与官方文档溯源。

建议收藏,随查随用。

📌 小程序包体积限制

根据微信小程序的限制:

● 主包大小:不超过 2MB。

● 单个分包大小:不超过 2MB。

● 所有分包总大小:不超过 20MB。

因此,合理地管理和划分模块的引用关系,对于控制包体积至关重要。

一、分包机制

分包之后,打包完成,分包与分包之间就不能互相调用内容。

分包可以调用主包的内容

1.1 官方限制速查表

维度 主包 普通分包 独立分包(independent)
大小上限 2 MB 2 MB 无上限
启动时下载 必下载 用时下载 只下载自己
能否 import 主包
能否 import 其他分包
能否被组件引用
适用场景 启动页/TabBar 业务模块 登录页/广告页

1.2 目录结构最佳实践

arduino 复制代码
├─ common/                 // 主包公共资源(必须被主包页面引用)
│  ├─ utils/
│  ├─ components/
│  └─ static/
├─ pages/                  // 主包页面(越少越好)
├─ subPackages/
│  ├─ moduleA/             // 普通分包
│  │  ├─ pages/
│  │  └─ components/       // 只能当前分包用
│  └─ moduleB/             // 独立分包
│     └─ pages/
└─ pages.json
 
示例说明

假设你的项目结构如下:

markdown 复制代码
markdown
复制编辑
- utils/
  - common.js
  - helper.js
- pages/
  - index.vue  // 主包页面
- subPackages/
  - feature/
    - page.vue  // 分包页面

● 如果 pages/index.vue 引用了 utils/common.js,则 common.js 会被打包进主包。

● 如果 subPackages/feature/page.vue 引用了 utils/helper.js,而主包中没有引用 helper.js,则 helper.js 不会被打包进主包。

● 如果你希望 helper.js 被主包和分包共同使用,确保在主包中也引用了 helper.js。

1.3 分包配置示例(pages.json)

json 复制代码
{
  "subPackages": [
    {
      "root": "subPackages/moduleA",
      "pages": [...]
    },
    {
      "root": "subPackages/moduleB",
      "independent": true,
      "pages": [...]
    }
  ]
}
 
分包的 independent

● 加了 "independent": true:这个分包是独立分包 → 启动时不加载主包代码,可突破主包 2 MB,但不能复用主包/其他分包的任何代码(包括 util、store、组件、npm 包)。

● 不加(默认):普通分包 → 启动时先下载主包,再下载该分包,可以复用主包代码,但主包总体积仍受 2 MB 限制。

详细对比
维度 独立分包 independent=true 普通分包(默认)
启动下载 只下载自己,不下载主包 先下载主包,再下载自己
主包大小限制 主包可<2 MB,独立包无上限 主包+所有普通包 ≤ 2 MB
代码复用 ❌ 不能复用主包/其他分包 ✅ 可以复用主包代码
包间引用 ❌ 不能 import 主包/其他分包 ✅ 可以 import 主包
适用场景 启动页/登录页/广告页等独立功能 普通业务模块,需要复用公共代码
使用建议

● 主包已接近 2 MB → 选 独立分包,把重依赖(图表、播放器)放进去。

● 需要复用公共工具/组件 → 选 普通分包,否则你得把公共代码再拷一份到独立包里,体积翻倍。

总结

"独立分包"就像独立 App,启动快但自给自足;普通分包像插件,启动慢但可共享主包代码。

1.4 跨包引用失败的 4 种典型错误

错误信息 原因 修复
module not found 主包引用分包文件 把文件移到 common/
can't require subPackages/... 分包互相引用 抽离公共文件到主包
超过 2 MB 主包体积爆炸 把大模块拆成独立分包
独立分包无法使用 uView 独立分包不能复用主包 npm 把 uView 再装一份到独立分包(牺牲体积)

1.5 强制把文件打进主包的黑科技

在 vue.config.js 或 vite.config.ts 里:

css 复制代码
// vite-plugin-uni 示例
plugins: [
  uni(),
  {
    name: 'force-include',
    config() {
      return {
        optimizeDeps: {
          include: [
            '@/common/utils/date.ts', // 即使没引用也打进主包
          ]
        }
      }
    }
  }
]
 

二、样式穿透 · 解决方案

在将 UniApp 项目转换为微信小程序时,使用 ::v-deep 进行样式穿透可能会遇到不生效的问题。这是由于微信小程序的组件样式隔离机制所致。以下是解决该问题的建议:

2.1 微信小程序样式隔离机制

● 默认 styleIsolation: 'isolated'(组件样式互不影响)

● 修改第三方组件样式 → 必须改成 shared

2.2 不同框架写法对照

框架 解除隔离写法 穿透语法
Vue2 export default { options:{styleIsolation:'shared'} } ::v-deep .a{}
Vue3 defineOptions({options:{styleIsolation:'shared'}}) :deep(.a){}
全局样式 在 App.vue 写样式 无需穿透

2.3 完整示例(Vue3 + uView)

xml 复制代码
<template>
  <u-upload class="my-upload" />
</template>
 
<script setup>
defineOptions({
  options: { styleIsolation: 'shared' }
})
</script>
 
<style scoped>
.my-upload :deep(.u-upload__button) {
  border-radius: 16rpx !important;
}
</style>
 

2.4 预处理器差异速查

推荐使用: ::v-deep

预处理器 支持语法
css >>>, /deep/, ::v-deep
less /deep/, ::v-deep
dart-sass 仅 ::v-deep 或 :deep()
node-sass 可以使用 ::v-deep

⚠️ 注意事项

● 样式隔离设置的位置:options 配置应放在组件的 export default 对象中,而不是 script setup 语法中。

● 样式作用范围:解除样式隔离后,父组件的样式可能会影响子组件,需注意可能引起的样式冲突。

● 平台差异:某些样式穿透方法在不同平台(如 H5、App、小程序)中的支持情况可能不同,需根据目标平台进行测试和调整

三、组件通信 · 100% 成功 checklist

场景 H5 写法 小程序兼容写法
传递对象 prop :data-source="list" ✅ 正常
传递函数 prop @on-change="fn" ✅ 正常
事件名大小写 @onChange ❌ 必须全小写 @on-change
动态插槽 ✅ 正常
默认插槽为空 slots.default?.() ✅ 防御式调用

3.1 emit 命名必知

kotlin 复制代码
// 子组件
this.$emit('select-change', value) // 小程序中模板必须写 kebab-case
 
xml 复制代码
<!-- 父组件 -->
<child @select-change="handle" />
 
示例1:

✅ 严格按照 相同的写法

kotlin 复制代码
 // 子组件发送
 this.$emit('on-select-changed', arr)
 
 // 父组件监听
  @on-select-changed="handleSelectedDoctors"

❌不相同写法,h5可用,小程序传递失败

php 复制代码
// 父组件传递参数
<doctor-list :data-source="mainExpertList"></doctor-list>
 
// 子组件,接收不到dataSource内容
const props = defineProps({
  dataSource: {
    type: Array,
    default: () => []
  }
})

四、 交互异常 · 真机调试玄学

4.1 textarea 高度错乱(官方 bug)

出现问题,页面内部处理不展示了,但是内容的字体定位会错乱。悬浮再页面上方

解决办法:【uni-app】 textarea组件的auto-hieght属性,显隐切换时高度异常,无法自适应内容撑开。_uniapp textarea auto-height-CSDN博客

同理,要组件展示出现,才赋值 this.showAutoHeight = true

ini 复制代码
<textarea class="text-area border-b"
        v-model="applyInfo.consult_case_history_other" :auto-height="showAutoHeight"
        placeholder="请填写患者治疗经过(500字以内)"/>

● 现象:v-if 切换后 auto-height 失效

● 根因:微信小程序必须先渲染再赋值

● 修复:

kotlin 复制代码
// 错误
this.show = true
this.text = '很长很长...'
 
// 正确
this.show = true
this.$nextTick(() => {
  this.text = '很长很长...'
})
    

4.2 input 点击两次才触发清除

使用fouce input框时候,会弹出输入的按键,点击第一次,是优先对input框失焦阻塞了自身事件,

点击第二次,才是真正处理事件

● 现象:键盘弹起后第一次点击无效

● 原理:第一次点击先失焦,第二次才真正触发

● 修复:优先考虑使用延迟处理

csharp 复制代码
async clearInput() {
  this.isFocus = false          // 关闭键盘
  await this.$nextTick()        // 等键盘收起
  this.value = ''               // 再清空
}

4.3 v-show 与 css样式 display:flex 冲突

● 现象:v-show="false" 仍占位

● 原因:display:flex 优先级高于 display:none

● 修复:

css 复制代码
/* 用 v-if 或加一层 wrapper */
.wrapper.hidden {
  display: none !important;
}

五、API 差异 · 平台判断大全

功能 H5 实现 小程序实现
Blob 转 URL URL.createObjectURL(blob) uni.arrayBufferToBase64
下载文件 a.href = url uni.downloadFile
页面栈 无限制 最多 10 层
返回上一页 history.back() uni.navigateBack()
刷新当前页 location.reload() const pages = getCurrentPages(); pages[pages.length-1].onLoad()

5.1 Blob 转 URL

请求图片展示

javascript 复制代码
// h5处理
const blob = new Blob([res.data], {
  type: res.headers['content-type']
})
 
return URL.createObjectURL(blob)
 
// 小程序处理
let base64 = uni.arrayBufferToBase64(res.data)
return `data:${res.headers['content-type']};base64,${base64}`

5.2 页面栈清理最佳实践

10层的栈,如果超出就会不容许再次跳转

php 复制代码
// 关键业务完成后主动清理
uni.redirectTo({ url: '/pages/index/index' }) // 替换当前页
 

切记要及时的清理栈

六、性能优化 · 体积与启动

6.1 主包瘦身 5 步法

  1. 图片放 CDN:用网络地址替代本地

  2. npm 包按需引入:如 lodash → lodash-es

  3. 动态 import:路由级/组件级懒加载

  4. 独立分包:把重依赖(图表、富文本)拆出去

  5. 构建分析:

arduino 复制代码
npm run build:mp-weixin --analyze

6.2 独立分包实战(uView 图表)

json 复制代码
// subPackages/chart/package.json
{
  "dependencies": {
    "uview-plus": "3.x" // 独立再装一份
  }
}
 

七、安全与适配

场景 代码示例
底部安全距离 padding-bottom: env(safe-area-inset-bottom)
自定义导航返回 「自定义返回监听」:components\interceptor
横屏适配 "pageOrientation": "auto" in pages.json

7.1 安全距离

css 安全距离

css 复制代码
.btn-content{
  padding-bottom: env(safe-area-inset-bottom);
  padding-bottom: constant(safe-area-inset-bottom); /* iOS <= 11.2 */
}

八、 配置

8.1不在以下 request 合法域名列表中

开始初期可以没有配置系列地址,需要去设置勾选 微信 --》右侧详情 --》本地设置

如遇到未收录的问题。 祝开发顺利,少踩坑,多提效!

相关推荐
奕辰杰3 小时前
关于npm前端项目编译时栈溢出 Maximum call stack size exceeded的处理方案
前端·npm·node.js
JiaLin_Denny4 小时前
如何在NPM上发布自己的React组件(包)
前端·react.js·npm·npm包·npm发布组件·npm发布包
路光.5 小时前
触发事件,按钮loading状态,封装hooks
前端·typescript·vue3hooks
我爱996!5 小时前
SpringMVC——响应
java·服务器·前端
咔咔一顿操作6 小时前
Vue 3 入门教程7 - 状态管理工具 Pinia
前端·javascript·vue.js·vue3
kk爱闹6 小时前
用el-table实现的可编辑的动态表格组件
前端·vue.js
漂流瓶jz7 小时前
JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具
前端·javascript·编译原理
换日线°7 小时前
css 不错的按钮动画
前端·css·微信小程序
风象南7 小时前
前端渲染三国杀:SSR、SPA、SSG
前端
90后的晨仔8 小时前
表单输入绑定详解:Vue 中的 v-model 实践指南
前端·vue.js