` |
---
## 二、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
```
小程序转换:
```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
```
**小程序**:
```xml
点击
```
```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
```
**小程序转换**:
```xml
```
```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
审核中
审核中
```
- 频繁切换 → 用 `hidden`(避免重复创建销毁)
- 初始条件不太可能变 → 用 `wx:if`(减少初始渲染量)
### 4.3 实际对比:reviewing 页面的条件渲染
**H5 原型**:只有一种状态(审核中),用静态 HTML。
**小程序转换**:支持 pending/rejected 两种状态,用 `wx:if` 动态切换:
```xml
{{status === 'pending' ? '申请审核中' : '申请未通过'}}
```
> **坑**:`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()
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 组件 | 注意事项 |
|-------------|-------------|---------|
| `