微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
476
apps/miniprogram - 副本/doc/h5-to-miniprogram-pitfalls.md
Normal file
476
apps/miniprogram - 副本/doc/h5-to-miniprogram-pitfalls.md
Normal file
@@ -0,0 +1,476 @@
|
||||
# H5 → 微信小程序转换避坑指南
|
||||
|
||||
> 基于本项目 `docs/h5_ui/` 原型与 `apps/miniprogram/miniprogram/pages/` 已转换页面的实际对比,结合微信小程序官方文档整理。
|
||||
> 适用于后续页面开发(如 board-coach、task-list、customer-detail 等)的快速参考。
|
||||
|
||||
---
|
||||
|
||||
## 一、WXML vs HTML — 标签与结构
|
||||
|
||||
### 1.1 标签映射表
|
||||
|
||||
| H5 (HTML) | 小程序 (WXML) | 说明 |
|
||||
|---------------------|----------------------|------|
|
||||
| `<div>` | `<view>` | 最基础的容器 |
|
||||
| `<span>` / `<p>` | `<text>` | 文本必须用 `<text>` 包裹才可选中/换行 |
|
||||
| `<a href="...">` | `<navigator url="">` | 或用 `wx.navigateTo()` 编程式跳转 |
|
||||
| `<img src="...">` | `<image src="" mode="">` | 必须指定 `mode`,默认 320×240 |
|
||||
| `<input>` | `<input>` 或 `<t-input>` | 原生 input 事件名不同;推荐 TDesign |
|
||||
| `<textarea>` | `<textarea>` 或 `<t-textarea>` | 同上 |
|
||||
| `<button>` | `<button>` 或 `<t-button>` | 小程序 button 有 `open-type` 能力 |
|
||||
| `<ul>/<li>` | `<view wx:for="{{list}}">` | 没有列表语义标签 |
|
||||
| `<select>` | `<picker>` 或 `<t-picker>` | 完全不同的交互模式 |
|
||||
| `<label for="id">` | `<label for="id">` | 支持,但 for 只能绑定 checkbox/radio/switch |
|
||||
| `<svg>` 内联 | `<image src="xx.svg">` | 不支持内联 SVG,只能作为图片引用 |
|
||||
| `<iframe>` | `<web-view>` | 需配置业务域名白名单 |
|
||||
|
||||
### 1.2 实际对比:login 页面
|
||||
|
||||
**H5 原型** — 内联 SVG 图标:
|
||||
```html
|
||||
<svg class="w-14 h-14 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="10" .../>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**小程序转换** — 改为 image 引用:
|
||||
```xml
|
||||
<image class="logo-icon" src="/assets/icons/logo-billiard.svg" mode="aspectFit" />
|
||||
```
|
||||
|
||||
> **坑**:小程序不支持内联 SVG。所有 SVG 图标需提取为独立 `.svg` 文件放到 `assets/icons/`,通过 `<image>` 引用。
|
||||
|
||||
### 1.3 不存在的标签/属性
|
||||
|
||||
| H5 特性 | 小程序替代方案 |
|
||||
|---------|---------------|
|
||||
| `<h1>`~`<h6>` | `<text>` + 样式类 |
|
||||
| `<table>` | `<view>` 手动布局 |
|
||||
| `<form>` + `<input name>` | 小程序 `<form>` 或直接 `setData` 收集 |
|
||||
| `onclick="fn()"` | `bindtap="fn"` 或 `bind:tap="fn"` |
|
||||
| `class="a b c"` 动态 | `class="base {{condition ? 'a' : 'b'}}"` |
|
||||
| `innerHTML` | `<rich-text nodes="{{html}}">` |
|
||||
|
||||
---
|
||||
|
||||
## 二、WXSS vs CSS — 样式差异
|
||||
|
||||
### 2.1 支持的选择器(有限)
|
||||
|
||||
| 选择器 | 支持 | 说明 |
|
||||
|--------|------|------|
|
||||
| `.class` | ✅ | |
|
||||
| `#id` | ✅ | |
|
||||
| `element` (如 `view`) | ✅ | |
|
||||
| `element, element` | ✅ | 群组选择器 |
|
||||
| `::before` / `::after` | ✅ | |
|
||||
| `*` 通配符 | ❌ | Tailwind 的 `*` reset 全部失效 |
|
||||
| `>` 子选择器 | ⚠️ | 部分版本支持,不推荐依赖 |
|
||||
| `+` / `~` 兄弟选择器 | ⚠️ | 同上 |
|
||||
| `:nth-child()` | ⚠️ | 部分支持 |
|
||||
| `@media` | ✅ | 支持,但用 rpx 更好 |
|
||||
|
||||
### 2.2 rpx 单位 — 最大差异
|
||||
|
||||
H5 用 `px`/`rem`/`vw`,小程序用 `rpx`(responsive pixel):
|
||||
- 屏幕宽度固定 = 750rpx
|
||||
- iPhone6 上 1rpx = 0.5px,即 1px = 2rpx
|
||||
- 设计稿以 750px 宽为基准时,数值直接写 rpx
|
||||
|
||||
**实际对比:login 页面**
|
||||
|
||||
H5 原型(Tailwind):
|
||||
```html
|
||||
<div class="w-24 h-24 rounded-3xl"> <!-- 96px × 96px -->
|
||||
```
|
||||
|
||||
小程序转换:
|
||||
```css
|
||||
.logo-box {
|
||||
width: 192rpx; /* 96px × 2 = 192rpx */
|
||||
height: 192rpx;
|
||||
border-radius: 48rpx;
|
||||
}
|
||||
```
|
||||
|
||||
> **换算规则**:H5 的 px 值 × 2 = rpx 值(基于 750 宽设计稿)。
|
||||
|
||||
### 2.2.1 H5 → 小程序全局缩放比例(87.5%)
|
||||
|
||||
H5 原型基于 375px 视口设计(iPhone SE/6/7/8),直接 ×2 转 rpx 后在大屏手机(iPhone 15 Pro Max,430pt 宽)上元素偏大。经实测对比,对所有 rpx 值统一乘以 0.875 缩放系数后视觉效果最佳。
|
||||
|
||||
**规则**:
|
||||
- 尺寸、间距、圆角、阴影偏移:`H5 px × 2 × 0.875` = 最终 rpx,取偶数
|
||||
- 字号:同上规则,如 `text-2xl`(24px) → 48rpx × 0.875 = 42rpx
|
||||
- t-icon size:同上规则,如 `w-5`(20px) → 40rpx × 0.875 = 35rpx
|
||||
- `max-width` 等约束宽度:同上规则
|
||||
- 背景纹理间距(如十字纹 `bg-pattern`):不缩放,保持原值
|
||||
|
||||
**换算速查**(常用 Tailwind 值):
|
||||
|
||||
| Tailwind | H5 px | 原始 rpx | ×0.875 | 取整 rpx |
|
||||
|----------|-------|----------|--------|----------|
|
||||
| `text-xs` (12px) | 12 | 24 | 21 | 22 |
|
||||
| `text-sm` (14px) | 14 | 28 | 24.5 | 24 |
|
||||
| `text-base` (16px) | 16 | 32 | 28 | 28 |
|
||||
| `text-lg` (18px) | 18 | 36 | 31.5 | 32 |
|
||||
| `text-xl` (20px) | 20 | 40 | 35 | 36 |
|
||||
| `text-2xl` (24px) | 24 | 48 | 42 | 42 |
|
||||
| `gap-2` / `p-2` (8px) | 8 | 16 | 14 | 14 |
|
||||
| `gap-3` / `p-3` (12px) | 12 | 24 | 21 | 22 |
|
||||
| `gap-4` / `p-4` (16px) | 16 | 32 | 28 | 28 |
|
||||
| `gap-5` / `p-5` (20px) | 20 | 40 | 35 | 36 |
|
||||
| `gap-6` / `p-6` (24px) | 24 | 48 | 42 | 42 |
|
||||
| `gap-8` / `p-8` (32px) | 32 | 64 | 56 | 56 |
|
||||
| `w-10` / `h-10` (40px) | 40 | 80 | 70 | 70 |
|
||||
| `w-14` / `h-14` (56px) | 56 | 112 | 98 | 98 |
|
||||
| `w-24` / `h-24` (96px) | 96 | 192 | 168 | 168 |
|
||||
| `w-28` / `h-28` (112px) | 112 | 224 | 196 | 196 |
|
||||
| `rounded-xl` (12px) | 12 | 24 | 21 | 22 |
|
||||
| `rounded-2xl` (16px) | 16 | 32 | 28 | 28 |
|
||||
| `rounded-3xl` (24px) | 24 | 48 | 42 | 42 |
|
||||
|
||||
> **来源**:no-permission 页面实测确定。先后尝试了非统一缩放、80%、87.5% 三种方案,87.5% 在 iPhone 15 Pro Max 上与 H5 原型视觉一致度最高。后续所有页面转换统一使用此系数。
|
||||
|
||||
### 2.3 Tailwind CSS → 手写 WXSS
|
||||
|
||||
小程序不支持 Tailwind CSS(无构建链集成),所有 Tailwind 工具类必须手写为 WXSS。
|
||||
|
||||
| Tailwind 类 | WXSS 等价写法 |
|
||||
|-------------|--------------|
|
||||
| `flex flex-col items-center` | `display: flex; flex-direction: column; align-items: center;` |
|
||||
| `gap-4` | `gap: 32rpx;`(4 × 8px × 2rpx) |
|
||||
| `p-5` | `padding: 40rpx;` |
|
||||
| `rounded-2xl` | `border-radius: 32rpx;` |
|
||||
| `text-sm` | `font-size: 28rpx;` |
|
||||
| `text-gray-7` | `color: #8b8b8b;` |
|
||||
| `bg-white/60` | `background: rgba(255,255,255,0.6);` |
|
||||
| `backdrop-blur-sm` | ❌ 不支持 `backdrop-filter` |
|
||||
| `shadow-lg` | `box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.06);` |
|
||||
| `min-h-screen` | `min-height: 100vh;` |
|
||||
|
||||
### 2.4 不支持/有差异的 CSS 特性
|
||||
|
||||
| CSS 特性 | 小程序支持情况 | 替代方案 |
|
||||
|----------|---------------|---------|
|
||||
| `backdrop-filter: blur()` | ❌ 不支持 | 用半透明背景色模拟 |
|
||||
| `position: fixed` | ⚠️ 部分场景异常 | 用 `position: sticky` 或组件自带吸顶 |
|
||||
| `@import url()` 远程 | ❌ | 只支持本地 `@import "path.wxss"` |
|
||||
| `@font-face` 远程字体 | ⚠️ | 需 `wx.loadFontFace()` 动态加载 |
|
||||
| CSS 变量 `var()` | ✅ 支持 | TDesign 大量使用 |
|
||||
| `linear-gradient` | ✅ 支持 | 正常使用 |
|
||||
| `animation` / `@keyframes` | ✅ 支持 | 正常使用 |
|
||||
| `transition` | ✅ 支持 | 正常使用 |
|
||||
| `env(safe-area-inset-*)` | ✅ 支持 | 刘海屏适配必用 |
|
||||
|
||||
### 2.5 样式作用域
|
||||
|
||||
- `app.wxss` = 全局样式
|
||||
- 页面 `.wxss` = 仅当前页面生效(自动隔离)
|
||||
- 组件 `.wxss` = 默认隔离(`styleIsolation: 'isolated'`)
|
||||
|
||||
> **坑**:H5 的全局 CSS reset(如 `* { box-sizing: border-box; }`)在小程序中无效。需要在每个需要的元素上手动设置 `box-sizing`。
|
||||
|
||||
---
|
||||
|
||||
## 三、事件系统 — 最容易踩坑
|
||||
|
||||
### 3.1 事件绑定对比
|
||||
|
||||
| H5 | 小程序 | 说明 |
|
||||
|----|--------|------|
|
||||
| `onclick="fn()"` | `bindtap="fn"` | 不能传参! |
|
||||
| `onclick="fn(1)"` | `data-id="1" bindtap="fn"` | 通过 dataset 传参 |
|
||||
| `addEventListener` | 不支持 | 只能在 WXML 中声明式绑定 |
|
||||
| `event.target.value` | `e.detail.value` | 取值路径不同 |
|
||||
| `event.preventDefault()` | `catchtap` | 用 catch 前缀阻止冒泡 |
|
||||
| `event.stopPropagation()` | `catchtap` | 同上 |
|
||||
|
||||
### 3.2 传参方式
|
||||
|
||||
**H5**:
|
||||
```html
|
||||
<button onclick="handleClick(item.id, item.name)">点击</button>
|
||||
```
|
||||
|
||||
**小程序**:
|
||||
```xml
|
||||
<view data-id="{{item.id}}" data-name="{{item.name}}" bindtap="handleClick">点击</view>
|
||||
```
|
||||
```typescript
|
||||
handleClick(e: WechatMiniprogram.TouchEvent) {
|
||||
const { id, name } = e.currentTarget.dataset
|
||||
}
|
||||
```
|
||||
|
||||
> **坑**:`data-` 属性名会自动转换 — 连字符转驼峰(`data-user-id` → `dataset.userId`),大写转小写(`data-userId` → `dataset.userid`)。
|
||||
|
||||
### 3.3 实际对比:login 页面的协议勾选
|
||||
|
||||
**H5 原型**:
|
||||
```html
|
||||
<input type="checkbox" id="agreeCheckbox" onchange="updateButtonState()">
|
||||
<script>
|
||||
checkbox.addEventListener('change', updateButtonState);
|
||||
</script>
|
||||
```
|
||||
|
||||
**小程序转换**:
|
||||
```xml
|
||||
<view class="agreement" bindtap="onAgreeChange">
|
||||
<view class="checkbox {{agreed ? 'checkbox--checked' : ''}}">
|
||||
<t-icon wx:if="{{agreed}}" name="check" size="20rpx" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
```
|
||||
```typescript
|
||||
onAgreeChange() {
|
||||
this.setData({ agreed: !this.data.agreed })
|
||||
}
|
||||
```
|
||||
|
||||
> **坑**:小程序没有原生 checkbox 的 `checked` 双向绑定,需要手动用 `setData` + 条件样式类模拟。
|
||||
|
||||
---
|
||||
|
||||
## 四、数据绑定与渲染
|
||||
|
||||
### 4.1 模板语法对比
|
||||
|
||||
| 功能 | H5 (原生/框架) | 小程序 WXML |
|
||||
|------|----------------|-------------|
|
||||
| 插值 | `${variable}` / `{{variable}}` | `{{variable}}` |
|
||||
| 条件渲染 | `if/else` + DOM 操作 | `wx:if` / `wx:elif` / `wx:else` |
|
||||
| 列表渲染 | `forEach` + `innerHTML` | `wx:for="{{list}}" wx:key="id"` |
|
||||
| 显示/隐藏 | `style.display = 'none'` | `hidden="{{!show}}"` 或 `wx:if` |
|
||||
| 动态 class | `classList.toggle()` | `class="base {{active ? 'on' : ''}}"` |
|
||||
| 动态 style | `element.style.color = 'red'` | `style="color: {{color}};"` |
|
||||
|
||||
### 4.2 wx:if vs hidden
|
||||
|
||||
```xml
|
||||
<!-- wx:if:条件为 false 时不渲染 DOM,切换时销毁/重建 -->
|
||||
<view wx:if="{{status === 'pending'}}">审核中</view>
|
||||
|
||||
<!-- hidden:始终渲染,只切换 display -->
|
||||
<view hidden="{{status !== 'pending'}}">审核中</view>
|
||||
```
|
||||
|
||||
- 频繁切换 → 用 `hidden`(避免重复创建销毁)
|
||||
- 初始条件不太可能变 → 用 `wx:if`(减少初始渲染量)
|
||||
|
||||
### 4.3 实际对比:reviewing 页面的条件渲染
|
||||
|
||||
**H5 原型**:只有一种状态(审核中),用静态 HTML。
|
||||
|
||||
**小程序转换**:支持 pending/rejected 两种状态,用 `wx:if` 动态切换:
|
||||
```xml
|
||||
<view class="top-gradient top-gradient--{{status}}"></view>
|
||||
<t-icon wx:if="{{status === 'pending'}}" name="time" size="112rpx" color="#fff" />
|
||||
<t-icon wx:else name="close-circle" size="112rpx" color="#fff" />
|
||||
<text class="main-title">{{status === 'pending' ? '申请审核中' : '申请未通过'}}</text>
|
||||
```
|
||||
|
||||
> **坑**:`wx:if` 中的表达式必须在 `{{}}` 内,且不支持复杂 JS 表达式(如函数调用)。需要复杂逻辑时用 WXS 或在 JS 中预处理好数据。
|
||||
|
||||
---
|
||||
|
||||
## 五、路由与导航
|
||||
|
||||
### 5.1 对比
|
||||
|
||||
| H5 | 小程序 | 说明 |
|
||||
|----|--------|------|
|
||||
| `window.location.href = 'xx.html'` | `wx.navigateTo({ url: '/pages/xx/xx' })` | 保留当前页,跳新页 |
|
||||
| `window.location.replace()` | `wx.redirectTo()` | 关闭当前页,跳新页 |
|
||||
| `history.back()` | `wx.navigateBack()` | 返回上一页 |
|
||||
| 无直接等价 | `wx.reLaunch()` | 关闭所有页面,打开新页 |
|
||||
| 无直接等价 | `wx.switchTab()` | 跳转 tabBar 页面 |
|
||||
|
||||
### 5.2 实际对比:reviewing 页面的"更换账号"
|
||||
|
||||
**H5 原型**:
|
||||
```javascript
|
||||
function switchAccount() {
|
||||
localStorage.clear();
|
||||
window.location.href = 'login.html';
|
||||
}
|
||||
```
|
||||
|
||||
**小程序转换**:
|
||||
```typescript
|
||||
onSwitchAccount() {
|
||||
const app = getApp<IAppOption>()
|
||||
app.globalData.token = undefined
|
||||
wx.removeStorageSync("token")
|
||||
wx.removeStorageSync("refreshToken")
|
||||
wx.reLaunch({ url: "/pages/login/login" }) // 清空页面栈
|
||||
}
|
||||
```
|
||||
|
||||
> **坑**:
|
||||
> - 页面栈最多 10 层,`navigateTo` 超过会静默失败
|
||||
> - 跳转 tabBar 页面必须用 `switchTab`,用 `navigateTo` 会报错
|
||||
> - 路径必须以 `/` 开头,且不带 `.wxml` 后缀
|
||||
> - `reLaunch` 会销毁所有页面,适合登录/登出等场景
|
||||
|
||||
---
|
||||
|
||||
## 六、存储与网络
|
||||
|
||||
### 6.1 本地存储
|
||||
|
||||
| H5 | 小程序 | 说明 |
|
||||
|----|--------|------|
|
||||
| `localStorage.setItem(k, v)` | `wx.setStorageSync(k, v)` | 同步写入 |
|
||||
| `localStorage.getItem(k)` | `wx.getStorageSync(k)` | 同步读取 |
|
||||
| `localStorage.removeItem(k)` | `wx.removeStorageSync(k)` | 同步删除 |
|
||||
| `localStorage.clear()` | `wx.clearStorageSync()` | 清空全部 |
|
||||
| 上限 ~5MB | 上限 10MB | 小程序更大 |
|
||||
|
||||
> **坑**:小程序没有 Cookie,登录态必须自行通过 Storage + header token 管理。
|
||||
|
||||
### 6.2 网络请求
|
||||
|
||||
| H5 | 小程序 | 说明 |
|
||||
|----|--------|------|
|
||||
| `fetch()` / `XMLHttpRequest` | `wx.request()` | 需配置域名白名单 |
|
||||
| 无限制 | 并发上限 10 个 | 超出排队 |
|
||||
| 任意域名 | 必须 HTTPS + 白名单 | 开发时可关闭校验 |
|
||||
|
||||
---
|
||||
|
||||
## 七、TDesign 组件替代 H5 原生元素
|
||||
|
||||
本项目使用 TDesign 小程序组件库,以下是常见替代关系:
|
||||
|
||||
| H5 原型元素 | TDesign 组件 | 注意事项 |
|
||||
|-------------|-------------|---------|
|
||||
| `<button>` | `<t-button>` | 用 CSS 变量定制样式,如 `--td-button-large-height` |
|
||||
| `<input>` | `<t-input>` | 事件是 `bind:change` 而非 `bindinput` |
|
||||
| `<textarea>` | `<t-textarea>` | 同上 |
|
||||
| `<select>` | `<t-picker>` | 完全不同的交互 |
|
||||
| `<checkbox>` | `<t-checkbox>` | 或手动实现(如 login 页) |
|
||||
| `<radio>` | `<t-radio>` / `<t-radio-group>` | `bind:change` 取值 |
|
||||
| SVG 图标 | `<t-icon name="xxx">` | TDesign 内置图标库 |
|
||||
| 加载动画 | `<t-loading>` | 替代 CSS spinner |
|
||||
| 弹窗 | `<t-dialog>` / `<t-toast>` | 替代 `alert()` / `confirm()` |
|
||||
| 下拉刷新 | 页面 `onPullDownRefresh` | 在 page.json 中 `enablePullDownRefresh: true` |
|
||||
|
||||
### TDesign 样式覆盖 4 种方式
|
||||
|
||||
1. **CSS 变量**(推荐):`--td-button-large-height: 96rpx`
|
||||
2. **外部样式类**:`t-class="my-class"` + `.my-class { ... !important }`
|
||||
3. **解除隔离**:TDesign 已开启 `addGlobalClass`,页面样式可直接覆盖
|
||||
4. **style 属性**:`style="background: #f5f5f5; border-radius: 16rpx;"`
|
||||
|
||||
**实际示例**(login 页面按钮定制):
|
||||
```css
|
||||
.login-btn {
|
||||
--td-button-large-height: 96rpx !important;
|
||||
--td-button-large-font-size: 32rpx !important;
|
||||
--td-button-border-radius: 24rpx !important;
|
||||
}
|
||||
.login-btn--active {
|
||||
background: linear-gradient(135deg, #0052d9, #3b82f6) !important;
|
||||
box-shadow: 0 12rpx 32rpx rgba(0, 82, 217, 0.3);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、高频踩坑清单
|
||||
|
||||
### 8.1 结构层
|
||||
|
||||
| # | 坑 | 说明 | 解决方案 |
|
||||
|---|-----|------|---------|
|
||||
| 1 | 内联 SVG 不支持 | WXML 不能写 `<svg>` 标签 | 提取为 `.svg` 文件,用 `<image>` 或 `<t-icon>` |
|
||||
| 2 | 没有 DOM API | `document.getElementById` 等全部不可用 | 用 `this.setData()` 驱动视图更新 |
|
||||
| 3 | `<text>` 内只能嵌套 `<text>` | 不能在 `<text>` 内放 `<view>` | 需要块级布局时外层用 `<view>` |
|
||||
| 4 | `checked="false"` 是 true | 字符串 `"false"` 是 truthy | 必须写 `checked="{{false}}"` |
|
||||
| 5 | `wx:key` 必须提供 | 列表渲染不加 key 会警告且性能差 | `wx:key="id"` 或 `wx:key="*this"` |
|
||||
| 6 | `<block>` 不渲染 DOM | 只是逻辑包裹,不产生真实节点 | 需要样式时改用 `<view>` |
|
||||
|
||||
### 8.2 样式层
|
||||
|
||||
| # | 坑 | 说明 | 解决方案 |
|
||||
|---|-----|------|---------|
|
||||
| 7 | `*` 选择器无效 | 全局 reset 失效 | 逐个元素设置 `box-sizing` |
|
||||
| 8 | `backdrop-filter` 不支持 | 毛玻璃效果无法实现 | 用半透明背景色 `rgba()` 近似 |
|
||||
| 9 | `image` 默认 320×240 | 不设宽高会变形 | 始终指定 `width`/`height` + `mode` |
|
||||
| 10 | rpx 小数精度 | 1rpx 在某些设备上不显示 | 边框最小用 2rpx |
|
||||
| 11 | 组件样式隔离 | 页面样式穿不进自定义组件 | 用外部样式类或 `styleIsolation: 'shared'` |
|
||||
| 12 | `!important` 滥用 | TDesign 组件内部样式优先级高 | 优先用 CSS 变量覆盖 |
|
||||
|
||||
### 8.3 逻辑层
|
||||
|
||||
| # | 坑 | 说明 | 解决方案 |
|
||||
|---|-----|------|---------|
|
||||
| 13 | `setData` 性能 | 大数据量传输卡顿 | 只传变化字段:`'list[2].name': 'new'` |
|
||||
| 14 | 没有 Cookie | 登录态不能靠 Cookie | Storage + header token |
|
||||
| 15 | `eval()` 不可用 | 动态代码执行被禁止 | 预编译逻辑 |
|
||||
| 16 | 页面栈 10 层限制 | `navigateTo` 超过 10 层静默失败 | 合理使用 `redirectTo` / `reLaunch` |
|
||||
| 17 | `alert()` 不存在 | 没有浏览器弹窗 API | `wx.showToast()` / `wx.showModal()` |
|
||||
| 18 | `window` / `document` 不存在 | 所有 Web API 不可用 | 用 `wx.*` API 替代 |
|
||||
|
||||
### 8.4 TDesign 相关
|
||||
|
||||
| # | 坑 | 说明 | 解决方案 |
|
||||
|---|-----|------|---------|
|
||||
| 19 | `style: v2` 冲突 | app.json 中的 `"style": "v2"` 导致样式错乱 | 删除该配置 |
|
||||
| 20 | npm 构建遗忘 | 安装新包后忘记构建 npm | 每次 `npm install` 后在开发者工具中"构建 npm" |
|
||||
| 21 | 事件名差异 | TDesign 用 `bind:change`,原生用 `bindinput` | 查阅组件文档确认事件名 |
|
||||
| 22 | 外部样式类命名 | `t-class` / `t-class-input` 等各组件不同 | 查阅组件文档的 External Classes |
|
||||
|
||||
---
|
||||
|
||||
## 九、转换 Checklist(新页面开发用)
|
||||
|
||||
开发新页面时,按此清单逐项检查:
|
||||
|
||||
- [ ] HTML 标签全部替换为 WXML 组件(`div→view`、`span→text`、`img→image`)
|
||||
- [ ] 内联 SVG 提取为文件,改用 `<image>` 或 `<t-icon>`
|
||||
- [ ] Tailwind 类全部手写为 WXSS(px × 2 × 0.875 = rpx,见 §2.2.1 缩放规则)
|
||||
- [ ] `backdrop-filter` 等不支持的 CSS 改为替代方案
|
||||
- [ ] 事件绑定改为 `bindtap` / `bind:change`,传参用 `data-*`
|
||||
- [ ] `alert/confirm` 改为 `wx.showToast` / `wx.showModal`
|
||||
- [ ] `localStorage` 改为 `wx.setStorageSync`
|
||||
- [ ] 路由跳转改为 `wx.navigateTo` / `wx.reLaunch` 等
|
||||
- [ ] 表单收集改为 `setData` + 事件回调
|
||||
- [ ] 图片设置 `mode` 属性(`aspectFit` / `aspectFill` / `widthFix`)
|
||||
- [ ] 列表渲染加 `wx:key`
|
||||
- [ ] 布尔属性用 `{{}}` 包裹(`checked="{{true}}"`)
|
||||
- [ ] TDesign 组件在页面 `.json` 中注册 `usingComponents`
|
||||
- [ ] 安全区适配:`padding-top: env(safe-area-inset-top)`
|
||||
- [ ] 页面配置:`enablePullDownRefresh`、`navigationBarTitleText` 等
|
||||
|
||||
---
|
||||
|
||||
## 十、board-customer 迁移经验补充
|
||||
|
||||
> 来源:board-customer 页面 8 维度卡片迁移实战(2026-03-07)
|
||||
|
||||
### 10.1 复杂维度用独立布局
|
||||
|
||||
最专一维度的助教明细表不适合用通用的 `card-mid-row` 或 `card-grid`,直接用独立的 `loyal-table` 布局(左侧竖线 `border-left: 4rpx solid #eee` + 表头 + 数据行)。同时在助教行的 `wx:if` 中排除 `dimType !== 'loyal'`,避免信息重复。
|
||||
|
||||
**关键**:当某个维度的卡片结构与其他维度差异过大时,不要硬套通用模板,直接写独立布局更清晰。
|
||||
|
||||
### 10.2 heart-icon 组件:TS observer 替代 WXS
|
||||
|
||||
小程序 WXS 不支持 emoji surrogate pair(如 `\uD83D\uDC96`),渲染为乱码。解决方案:
|
||||
- 用 TS `observers` 监听 `score` 属性变化,计算对应 emoji 字符串
|
||||
- WXML 中用 `{{heartEmoji}}` 数据绑定渲染
|
||||
- 样式:`font-size: 22rpx; line-height: 1; position: relative; top: -4rpx` 和文字对齐
|
||||
|
||||
### 10.3 助教字体颜色三态 + badge 渐变
|
||||
|
||||
助教名字颜色规则(通过 CSS class 控制):
|
||||
- 跟 badge → `.assistant--assignee`:`color: #e34d59; font-weight: 700`(红色加粗)
|
||||
- 弃 badge → `.assistant--abandoned`:`color: #a6a6a6`(灰色)
|
||||
- 无 badge → `.assistant--normal`:`color: #242424`(黑色)
|
||||
|
||||
Badge 样式(白字 + 渐变背景 + 阴影):
|
||||
- 跟:`background: linear-gradient(135deg, #e34d59, #f26a76); box-shadow: 0 2rpx 6rpx rgba(227,77,89,0.28)`
|
||||
- 弃:`background: linear-gradient(135deg, #d4d4d4, #b6b6b6); box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.14)`
|
||||
Reference in New Issue
Block a user