Files
Neo-ZQYY/docs/miniprogram-dev/h5-migration/h5-to-mp-bridge.md

1081 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# H5 原型 → 微信小程序迁移桥接规范(主规范 v3.0
> **版本**v3.0
> **适用对象**人工开发、AI 批量迁移、代码审查、验收回归
> **适用范围**:本项目 `docs/h5_ui/` 原型页面到微信原生小程序的迁移
> **目标**:实现页面与交互的高还原迁移,样式、层级、动效、状态切换、页面流转 1:1 对齐 H5 验收原型
---
## 📖 文档使用指南
### 本文档定位
这是**执行规程**不是知识笔记。使用本规范时AI 或开发者必须做到:
1. 能从单个 H5 页面产出对应的小程序页面文件
2. 能明确哪些能力可直接迁移,哪些必须降级,哪些禁止直迁
3. 能在不使用 DOM API 的前提下,把 H5 交互改造成小程序的数据驱动实现
4. 能输出可交付物,而不是停留在"分析建议"
### 文档结构
- **第一部分§1-4**:执行规程 - 输入输出、强制决策、能力分级
- **第二部分§5-9**:技术换算 - 尺寸、字号、颜色、布局、装饰
- **第三部分§10-12**:交互改造 - WXML 映射、数据驱动、API 替换
- **第四部分§13**:执行流程 - 10 步标准流程
- **附录引用§14**4 个附录字典的索引
### 配套文档
- **附录 A**Spacing 与尺寸字典
- **附录 B**字体与文本字典v3.1,含 line-height 规则)
- **附录 C**:颜色字典
- **附录 D**:布局类映射字典
- **附录索引**`docs/miniprogram-dev/New/h5_to_wechat_miniprogram_migration_bridge_appendix_index_v3.md`
---
## 第一部分:执行规程
## 1. 输入输出契约
### 1.1 输入物
单页迁移时,输入物必须至少包含:
- 对应 H5 页面 HTML 文件
- 该页面引用的 CSS页面内 `<style>`、独立 css 文件、Tailwind utility
- 该页面引用的 JS
- 该页面依赖的图片 / 图标 / SVG / JSON Mock
- 页面跳转关系和入口参数
### 1.2 输出物
每个页面迁移后,必须至少产出:
- `pages/<route>/<route>.wxml`
- `pages/<route>/<route>.wxss`
- `pages/<route>/<route>.js`
- `pages/<route>/<route>.json`
- 该页面使用到的 mock 数据文件或页面内 mock 常量
- 迁移说明(至少包含:状态变量、事件、待确认项、降级项)
### 1.3 不允许的输出
以下结果视为未完成:
- 只给"迁移思路",不产出页面文件
- 只把 Tailwind class 翻译成 CSS不改 WXML / 交互
- 继续依赖 `document.*``window.*``history.*``localStorage` 等浏览器 API
- 以"兼容性未知"为由保留 H5 写法不处理
- 把高风险 CSS 特性原样搬过去,且不写降级方案
---
## 2. 项目级强制决策
### 2.1 框架与运行时
1. **必须使用微信原生小程序页面体系**WXML + WXSS + JS + JSON
2. **禁止继续在小程序端运行 Tailwind CDN / 浏览器脚本**
3. **禁止使用 DOM 驱动交互**,页面必须改为数据驱动
4. **不引入 Taro / uni-app / WebView 兜底**,本规范面向原生小程序页面迁移
### 2.2 验收基准
1. H5 原型的主要验收基准宽度为 **412 CSS px**
2. 小程序以 412 宽设备的视觉效果为主验收目标
3. 其他常见宽度设备允许比例差异,但不得出现:文本溢出、遮挡、错层、关键按钮点击区域异常、吸顶/弹层错位
### 2.3 单位策略
采用 **`rpx` 为主、`px` 为辅** 的混合单位方案。
**优先 `rpx`**
- 页面宽度、横向布局、卡片尺寸
- 主容器 padding / margin、列表间距、栅格
- 吸顶区高度、块级间距
**优先 `px`**
- 1px 发丝线、阴影 blur/spread
- 小图标、绝对定位微调、小控件尺寸
- 状态栏 / 安全区补偿、细描边
**按效果决定**
- 字号、圆角、局部 padding、某些 transform 位移
**换算基准**
```
rpx = H5_CSS_px × (750 / 412)
≈ H5_CSS_px × 1.8204
```
**取整规则**
- 普通布局值:四舍五入到整数 rpx
- 高频 token沉淀成统一变量表见附录 A保持全项目一致
- 视觉敏感值:以真机截图比对微调,不强行套公式
### 2.4 页面滚动策略
1. 默认使用 **页面自然滚动**
2. 只有在必须做局部滚动区域时,才使用 `scroll-view`
3. 吸顶、滚动联动、section 感知优先围绕页面滚动实现;不要无必要把整页包进 `scroll-view`
### 2.5 样式组织策略
1. 全局 token、复用组件样式放 `app.wxss` 或公共样式文件
2. 页面私有样式放页面同名 `.wxss`
3. 样式优先使用 class动态值用内联 style 绑定
4. 不以 CSS 变量作为唯一运行前提;优先输出静态色值 / 静态 token
### 2.6 文本与行高策略 ⚠️
**关键发现**:微信小程序的 `<text>` 组件不能直接设置 `line-height`,必须通过外层 `<view>` 设置。
**全局设置(推荐):**
```wxss
page {
line-height: 1.5; /* Tailwind 默认行高 */
}
view {
line-height: inherit; /* view 继承 page 的 line-height */
}
/* text 会自动继承外层 view 的 line-height不需要额外设置 */
```
**局部覆盖:**
```wxss
.section-title {
font-size: 26rpx;
line-height: 36rpx; /* 在 view 的 class 上设置text 会继承 */
font-weight: 600;
}
```
```wxml
<view class="section-title">
<text>标题文本</text>
</view>
```
**错误做法:**
```wxss
/* ❌ 直接在 text 上设置 line-height 无效 */
text {
line-height: 36rpx; /* 不会生效 */
}
```
**执行规则**
1. 所有文本类样式必须同时设置 `font-size``line-height`
2. `line-height` 必须在外层 `<view>` 的 class 上设置,不能在 `<text>` 上设置
3. 标准字号的 `line-height` 换算参考附录 B.3
4. 若发现 Y 轴高度与 H5 端不一致,优先检查 `line-height` 是否正确设置
### 2.7 SVG / 图标策略
1. **禁止把 H5 的内联 `<svg>...</svg>` 直接当 WXML DOM 搬运**
2. 资源层允许 `svg` 文件进入小程序项目包,但页面显示是否稳定,必须以目标设备实测为准
3. 默认执行顺序:
- 简单静态图标:优先导出为独立资源文件
-`svg` 渲染不稳定:转为 `png`
- 高频单色图标:允许改为 iconfont / 组件化图标
4. 动态换色的 SVG 不要临时拼接字符串;应改为多份资源、图标组件或样式控制的等价实现
---
## 3. 能力分级A/B/C 类)
### 3.1 A 类:可直接使用
以下能力在当前项目中视为稳定迁移能力:
- `app.wxss` / 页面 `wxss` / `@import`
- `rpx`
- `.class``#id`、元素选择器、`::before``::after`
- `flex``position: relative/absolute/fixed`
- `box-shadow``border-radius``linear-gradient`
- `opacity`、常规 `transform`
- `transition``animation`
- 动态内联样式绑定
- `onPageScroll`
- `wx.createAnimation()`
- `wx.createSelectorQuery()`
- `wx.createIntersectionObserver()`
- `navigator` / `wx.navigateTo` / `wx.redirectTo` / `wx.switchTab` / `wx.navigateBack`
- `view` / `navigator``hover-class`
### 3.2 B 类:有条件使用,必须带回退或验证
以下能力允许使用,但不能把它们当成项目硬依赖:
- `position: sticky`
- `grid`
- `filter: blur()`
- `backdrop-filter`
- `clip-path`
- `vh`
- `line-clamp`
- 复杂组合选择器
- 资源型 `svg`
- CSS 变量
**执行规则**
1. 使用前必须确认该页面已有回退方案
2. 若页面对视觉或交互依赖度高,则必须做真机验证
3. 若 B 类能力失效,不得阻塞交付,必须切换为本规范定义的降级实现
### 3.3 C 类:禁止直迁
以下能力不得原样从 H5 搬到小程序:
- `document.getElementById/querySelector/querySelectorAll`
- `classList.add/remove/toggle`
- 直接改 `element.style.xxx`
- `innerHTML` / `textContent` 方式渲染视图
- `window.scrollY` / `window.scrollTo`
- `history.back`
- `localStorage` / `sessionStorage`
- `alert` / `confirm`
- CSS `env(safe-area-inset-*)`
- `:hover` / `group-hover:*`
- 依赖浏览器焦点模型的 `:focus` / `:active` 交互
- 依赖 `:first-child/:last-child/:nth-child` 才能成立的视觉规则
---
## 4. Tailwind CSS 在本项目中的行为
### 4.1 Tailwind CSS 渲染机制
本项目 H5 原型使用 `<script src="https://cdn.tailwindcss.com"></script>`Tailwind CSS v3 CDN Play 模式)。
Tailwind v3 默认主题中,大量 spacing、font-size、border-radius token 主要使用 `rem` 体系。在浏览器默认根字号 `1rem = 16px` 且页面未主动改写 `html { font-size }` 的前提下,这些 rem 体系的 utility 会计算成固定 CSS px 值。
**关键事实**Tailwind 默认 rem 体系的 utility class 在任何设备上都会计算为固定的 CSS px 值,与设备布局宽度无关。
### 4.2 微信 rpx 的精确定义
来源:[微信官方 WXSS 文档](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html)
> rpxresponsive pixel可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。
换算公式:
```
1rpx = 屏幕宽度px / 750
1px = 750 / 屏幕宽度px × rpx
```
官方示例:
| 设备 | 屏幕宽度 | 1rpx = ?px | 1px = ?rpx |
|------|----------|-----------|-----------|
| iPhone 5 | 320px | 0.42px | 2.34rpx |
| iPhone 6 | 375px | 0.5px | 2rpx |
| iPhone 6 Plus | 414px | 0.552px | 1.81rpx |
### 4.3 本项目的换算链路
验收基准设备:等效宽度 412px
完整链路:`Tailwind class → CSS 计算值(px) → rpx`
```
rpx = CSS_px × (750 / 412)
= CSS_px × 1.82039...
```
---
**(待续:第二部分将包含详细的技术换算表)**
---
## 📚 快速导航
- **执行规程**§1-4当前部分
- **技术换算**§5-9见下文
- **交互改造**§10-12见下文
- **执行流程**§13见下文
- **附录索引**§14见下文
---
## 版本历史
- **v3.0**2026-03-14合并 v1.0 技术换算基础和 v2.0 执行规范,补充 line-height 关键发现
- **v2.0**:执行规范,强化输入输出契约和能力分级
- **v1.0**:技术换算基础,建立完整的映射表
# H5 原型 → 微信小程序迁移桥接规范(主规范 v3.0 - 第二部分)
> 接续第一部分本部分包含§5-9 技术换算基础
---
## 第二部分:技术换算基础
## 5. Tailwind Spacing → rpx 完整换算表
Tailwind v3 的 spacing scale 基于 `0.25rem = 4px` 步进。以下是本项目原型中实际使用的所有 spacing 值的精确换算。这些 rpx 值已在已迁移页面中使用,作为项目统一 token后续页面应保持一致。
### 5.1 Tailwind spacing scale默认
| Tailwind 值 | rem | CSS px | rpx项目 token | 常见用途 |
|-------------|-----|--------|---------------------|---------|
| `0.5` | 0.125rem | 2px | 4rpx | gap-0.5, p-0.5, mt-0.5 |
| `px` | 1px | 1px | 2rpx | py-px, w-px |
| `1` | 0.25rem | 4px | 8rpx | gap-1, p-1, mt-1, mb-1 |
| `1.5` | 0.375rem | 6px | 12rpx | gap-1.5, p-1.5, mt-1.5 |
| `2` | 0.5rem | 8px | 14rpx | gap-2, p-2, mt-2, mb-2 |
| `2.5` | 0.625rem | 10px | 18rpx | gap-2.5, p-2.5, mb-2.5 |
| `3` | 0.75rem | 12px | 22rpx | gap-3, p-3, mb-3, pl-3 |
| `3.5` | 0.875rem | 14px | 26rpx | p-3.5, h-3.5, w-3.5 |
| `4` | 1rem | 16px | 30rpx | gap-4, p-4, px-4, mb-4 |
| `5` | 1.25rem | 20px | 36rpx | p-5, px-5, mb-5, pb-5 |
| `6` | 1.5rem | 24px | 44rpx | gap-6, p-6, px-6, mb-6 |
| `7` | 1.75rem | 28px | 52rpx | h-7, w-7 |
| `8` | 2rem | 32px | 58rpx | px-8, mb-8, pb-8, h-8 |
| `10` | 2.5rem | 40px | 72rpx | h-10, w-10, pb-10 |
| `12` | 3rem | 48px | 88rpx | h-12, w-12, pb-12 |
| `16` | 4rem | 64px | 116rpx | h-16, w-16 |
| `20` | 5rem | 80px | 146rpx | h-20, w-20, top-20 |
| `24` | 6rem | 96px | 174rpx | h-24, w-24 |
| `32` | 8rem | 128px | 232rpx | h-32, w-32 |
| `40` | 10rem | 160px | 292rpx | bottom-40, top-40 |
**详细完整表格**:参见附录 A
---
## 6. Tailwind 字号 → WXSS font-size + line-height 完整换算表
### 6.1 核心规则
Tailwind v3 的字号类(`text-xs``text-sm` 等)同时设置 `font-size``line-height`
例如 `text-sm` 生成的 CSS 是:
```css
font-size: 0.875rem; /* 14px */
line-height: 1.25rem; /* 20px */
```
**强制规则**:每个 Tailwind 字号类转 WXSS 时,必须同时写 `font-size``line-height`
### 6.2 微信小程序 text 组件的 line-height 限制 ⚠️
**关键发现**:微信小程序的 `<text>` 组件不能直接设置 `line-height`,必须通过外层 `<view>` 设置。
**正确做法**:参见 §2.6
**原因**:微信小程序的 `<text>` 是特殊的内联组件,直接设置 `line-height` 不生效,必须在外层 `<view>` 上设置。
**验证方法**:在开发者工具的 Computed 面板中text 元素不会显示 `line-height` 属性,但外层 view 的 `height` 值会包含行高效果。
### 6.3 标准字号类换算表
| Tailwind class | font-size (px) | line-height (px) | WXSS font-size | WXSS line-height |
|---------------|---------------|-----------------|----------------|-----------------|
| `text-xs` | 12px | 16px | 22rpx | 29rpx |
| `text-sm` | 14px | 20px | 26rpx | 36rpx |
| `text-base` | 16px | 24px | 30rpx | 44rpx |
| `text-lg` | 18px | 28px | 33rpx | 51rpx |
| `text-xl` | 20px | 28px | 36rpx | 51rpx |
| `text-2xl` | 24px | 32px | 44rpx | 58rpx |
| `text-3xl` | 30px | 36px | 54rpx | 66rpx |
**换算公式**
```
font-size(rpx) = font-size(px) × 1.8204
line-height(rpx) = line-height(px) × 1.8204
```
**详细完整表格**:参见附录 B
### 6.4 font-weight 映射
| Tailwind class | CSS font-weight | WXSS |
|---------------|----------------|------|
| `font-normal` | 400 | `font-weight: 400` |
| `font-medium` | 500 | `font-weight: 500` |
| `font-semibold` | 600 | `font-weight: 600` |
| `font-bold` | 700 | `font-weight: 700` |
**注意**`font-medium` 是 500 不是 600。这是常见错误。
### 6.5 其他文本属性
| Tailwind class | WXSS |
|---------------|------|
| `text-center` | `text-align: center` |
| `text-right` | `text-align: right` |
| `text-left` | `text-align: left` |
| `truncate` | `overflow: hidden; text-overflow: ellipsis; white-space: nowrap` |
| `line-clamp-2` | `display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden` |
| `whitespace-nowrap` | `white-space: nowrap` |
| `underline` | `text-decoration: underline` |
| `line-through` | `text-decoration: line-through` |
---
## 7. Tailwind 颜色 → WXSS 颜色完整映射
### 7.1 项目自定义颜色
每个 HTML 页面的 `<script>` 中定义了相同的自定义颜色扩展:
| Tailwind 名称 | 色值 | WXSS 变量 |
|--------------|------|----------|
| `primary` | `#0052d9` | `var(--color-primary)` |
| `primary-light` | `#ecf2fe` | `var(--color-primary-light)` |
| `success` | `#00a870` | `var(--color-success)` |
| `warning` | `#ed7b2f` | `var(--color-warning)` |
| `error` | `#e34d59` | `var(--color-error)` |
| `gray-1` | `#f3f3f3` | `var(--color-gray-1)` |
| `gray-7` | `#8b8b8b` | `var(--color-gray-7)` |
| `gray-9` | `#5e5e5e` | `var(--color-gray-9)` |
| `gray-13` | `#242424` | `var(--color-gray-13)` |
**详细完整表格**:参见附录 C
### 7.2 透明度变体
原型大量使用 Tailwind 的透明度修饰符(`/N`),如 `bg-primary/10``text-white/60`
WXSS 映射方式:使用 `rgba()` 或直接写带透明度的色值。
| 透明度后缀 | alpha 值 | 示例 |
|-----------|---------|------|
| `/10` | 0.10 | `bg-primary/10``rgba(0, 82, 217, 0.10)` |
| `/20` | 0.20 | `bg-white/20``rgba(255, 255, 255, 0.20)` |
| `/50` | 0.50 | `bg-black/50``rgba(0, 0, 0, 0.50)` |
| `/80` | 0.80 | `bg-white/80``rgba(255, 255, 255, 0.80)` |
---
## 8. Tailwind 布局类 → WXSS 完整映射
### 8.1 Flexbox
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `flex` | `display: flex` | `display: flex` |
| `flex-col` | `flex-direction: column` | `flex-direction: column` |
| `flex-1` | `flex: 1 1 0%` | `flex: 1` |
| `flex-shrink-0` | `flex-shrink: 0` | `flex-shrink: 0` |
| `flex-wrap` | `flex-wrap: wrap` | `flex-wrap: wrap` |
| `items-center` | `align-items: center` | `align-items: center` |
| `items-start` | `align-items: flex-start` | `align-items: flex-start` |
| `justify-between` | `justify-content: space-between` | `justify-content: space-between` |
| `justify-center` | `justify-content: center` | `justify-content: center` |
### 8.2 Grid
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `grid` | `display: grid` | `display: grid` |
| `grid-cols-2` | `grid-template-columns: repeat(2, minmax(0, 1fr))` | `grid-template-columns: repeat(2, minmax(0, 1fr))` |
| `grid-cols-3` | `grid-template-columns: repeat(3, minmax(0, 1fr))` | `grid-template-columns: repeat(3, minmax(0, 1fr))` |
| `grid-cols-4` | `grid-template-columns: repeat(4, minmax(0, 1fr))` | `grid-template-columns: repeat(4, minmax(0, 1fr))` |
### 8.3 Gap间距
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `gap-1` | `gap: 4px` | `gap: 8rpx` |
| `gap-2` | `gap: 8px` | `gap: 14rpx` |
| `gap-3` | `gap: 12px` | `gap: 22rpx` |
| `gap-4` | `gap: 16px` | `gap: 30rpx` |
| `gap-x-3` | `column-gap: 12px` | `column-gap: 22rpx` |
| `gap-y-2` | `row-gap: 8px` | `row-gap: 14rpx` |
### 8.4 定位
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `relative` | `position: relative` | `position: relative` |
| `absolute` | `position: absolute` | `position: absolute` |
| `fixed` | `position: fixed` | `position: fixed` |
| `sticky` | `position: sticky` | 见 §3.2 兼容性说明 |
| `inset-0` | `inset: 0` | `top: 0; right: 0; bottom: 0; left: 0` |
| `top-0` | `top: 0` | `top: 0` |
| `z-10` | `z-index: 10` | `z-index: 10` |
| `z-50` | `z-index: 50` | `z-index: 50` |
### 8.5 尺寸
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `w-full` | `width: 100%` | `width: 100%` |
| `h-full` | `height: 100%` | `height: 100%` |
| `w-1/2` | `width: 50%` | `width: 50%` |
| `min-h-screen` | `min-height: 100vh` | `min-height: 100vh` |
**详细完整表格**:参见附录 D
---
## 9. Tailwind 装饰类 → WXSS 完整映射
### 9.1 圆角
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `rounded` | `border-radius: 4px` | `border-radius: 8rpx` |
| `rounded-lg` | `border-radius: 8px` | `border-radius: 14rpx` |
| `rounded-xl` | `border-radius: 12px` | `border-radius: 22rpx` |
| `rounded-2xl` | `border-radius: 16px` | `border-radius: 30rpx` |
| `rounded-full` | `border-radius: 9999px` | `border-radius: 50%``border-radius: 999rpx` |
### 9.2 阴影
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `shadow-sm` | `box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05)` | `box-shadow: 0 2rpx 4rpx 0 rgba(0,0,0,0.05)` |
| `shadow-lg` | `box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)` | `box-shadow: 0 18rpx 28rpx -6rpx rgba(0,0,0,0.1), 0 8rpx 12rpx -8rpx rgba(0,0,0,0.1)` |
### 9.3 边框
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `border` | `border: 1px solid` | `border: 1px solid` |
| `border-2` | `border: 2px solid` | `border: 2px solid` |
| `border-b` | `border-bottom: 1px solid` | `border-bottom: 1px solid` |
| `border-t` | `border-top: 1px solid` | `border-top: 1px solid` |
### 9.4 渐变
| Tailwind class | CSS | WXSS |
|---------------|-----|------|
| `bg-gradient-to-r` | `background-image: linear-gradient(to right, ...)` | `background: linear-gradient(to right, ...)` |
| `bg-gradient-to-br` | `background-image: linear-gradient(to bottom right, ...)` | `background: linear-gradient(to bottom right, ...)` |
| `from-X` | 渐变起始色 | 渐变第一个色值 |
| `to-X` | 渐变结束色 | 渐变最后一个色值 |
示例:`bg-gradient-to-br from-blue-400 to-blue-500`
→ WXSS: `background: linear-gradient(to bottom right, #60a5fa, #3b82f6)`
### 9.5 透明度与模糊
| Tailwind class | CSS | WXSS | 小程序兼容性 |
|---------------|-----|------|-------------|
| `opacity-30` | `opacity: 0.3` | `opacity: 0.3` | ✓ |
| `opacity-60` | `opacity: 0.6` | `opacity: 0.6` | ✓ |
| `backdrop-blur-md` | `backdrop-filter: blur(12px)` | 见 §3.2 | ⚠️ 部分支持 |
| `blur-xl` | `filter: blur(24px)` | `filter: blur(24px)` | ✓ |
---
**(待续:第三部分将包含交互改造)**
---
## 📚 快速导航
- **执行规程**§1-4见第一部分
- **技术换算**§5-9当前部分
- **交互改造**§10-12见第三部分
- **执行流程**§13见第三部分
- **附录索引**§14见第三部分
# H5 原型 → 微信小程序迁移桥接规范(主规范 v3.0 - 第三部分)
> 接续第二部分本部分包含§10-12 交互改造、§13 执行流程、§14 附录索引
---
## 第三部分:交互改造
## 10. HTML 标签 → WXML 组件映射
### 10.1 基础标签映射
| H5 标签 | WXML 组件 | 说明 |
|--------|----------|------|
| `<div>` | `<view>` | 通用容器 |
| `<span>` | `<text>` | 行内文本。注意:`<text>` 内只能嵌套 `<text>`,不能嵌套 `<view>` |
| `<p>` | `<view>``<text>` | 段落 |
| `<img>` | `<image>` | 图片。必须指定 `mode` 属性 |
| `<a>` | `<navigator>``<view bindtap>` | 链接/跳转 |
| `<button>` | `<button>``<view bindtap>` | 按钮 |
| `<input>` | `<input>` | 输入框 |
| `<textarea>` | `<textarea>` | 多行输入 |
| `<svg>` | `<image src="xxx.svg">` | 小程序不支持内联 SVG |
| `<ul>` / `<ol>` / `<li>` | `<view>` | 列表容器 |
### 10.2 image 组件的 mode 属性
| H5 CSS | image mode |
|--------|-----------|
| `object-fit: cover` | `mode="aspectFill"` |
| `object-fit: contain` | `mode="aspectFit"` |
| 无特殊设置 | `mode="widthFix"`(宽度撑满,高度自适应) |
### 10.3 事件属性映射
| H5 事件 | WXML 事件 | 说明 |
|--------|----------|------|
| `onclick` | `bindtap` | 点击 |
| `onchange` | `bindchange` | 值变化 |
| `oninput` | `bindinput` | 输入 |
| `onscroll` | `bindscroll`scroll-view`onPageScroll`(页面) | 滚动 |
| `ontouchstart` | `bindtouchstart` | 触摸开始 |
| `ontouchend` | `bindtouchend` | 触摸结束 |
| `oncontextmenu` | `bindlongpress` | 长按 |
**注意**
- `bind` 前缀事件会冒泡
- `catch` 前缀事件会阻止冒泡(等同于 `e.stopPropagation()`
- `catchtouchmove`:阻止触摸移动冒泡(常用于遮罩层阻止背景滚动)
---
## 11. JS DOM 操作 → 小程序数据驱动完整映射
### 11.1 总体原则
**H5 模式**`用户操作 → JS 查询 DOM → 修改 DOM 属性/样式/内容 → 视觉变化`
**小程序模式**`用户操作 → 事件处理函数 → setData 更新数据 → WXML 自动重渲染 → 视觉变化`
**禁止在小程序中使用的 H5 API**
- `document.getElementById()`
- `document.querySelector()` / `querySelectorAll()`
- `element.classList.add/remove/toggle/contains`
- `element.style.xxx = yyy`
- `element.innerHTML` / `textContent`
- `document.createElement()` / `appendChild()`
### 11.2 模式 A显示/隐藏切换
**H5 写法**
```javascript
document.getElementById('modal').classList.remove('hidden')
document.getElementById('modal').classList.add('flex')
```
**小程序写法**
```javascript
// JS
Page({
data: { showModal: false },
openModal() { this.setData({ showModal: true }) },
closeModal() { this.setData({ showModal: false }) }
})
```
```xml
<!-- WXML -->
<view wx:if="{{showModal}}" class="modal-overlay" catchtouchmove bindtap="closeModal">
<view class="modal-content" catchtap>
<!-- 弹窗内容 -->
</view>
</view>
```
### 11.3 模式 BTab/状态切换
**H5 写法**
```javascript
function switchTab(tabName) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'))
document.getElementById('tab-' + tabName).classList.add('active')
}
```
**小程序写法**
```javascript
Page({
data: { activeTab: 'basic' },
onTabChange(e) {
this.setData({ activeTab: e.currentTarget.dataset.tab })
}
})
```
```xml
<view class="tab {{activeTab === 'basic' ? 'tab--active' : ''}}"
data-tab="basic" bindtap="onTabChange">基础课</view>
<view class="tab {{activeTab === 'vip' ? 'tab--active' : ''}}"
data-tab="vip" bindtap="onTabChange">VIP课</view>
<view wx:if="{{activeTab === 'basic'}}">基础课内容</view>
<view wx:if="{{activeTab === 'vip'}}">VIP课内容</view>
```
### 11.4 模式 C展开/收起
**H5 写法**
```javascript
function toggleExpand(id) {
const el = document.getElementById(id)
el.classList.toggle('hidden')
}
```
**小程序写法**
```javascript
Page({
data: { expandedSections: {} },
toggleSection(e) {
const key = e.currentTarget.dataset.key
this.setData({ [`expandedSections.${key}`]: !this.data.expandedSections[key] })
}
})
```
```xml
<view bindtap="toggleSection" data-key="detail">
<text>{{expandedSections.detail ? '收起' : '展开更多'}}</text>
</view>
<view wx:if="{{expandedSections.detail}}">
<!-- 展开内容 -->
</view>
```
### 11.5 模式 D内容动态更新
**H5 写法**
```javascript
document.getElementById('total').textContent = '¥12,345'
document.getElementById('list').innerHTML = items.map(i => `<div>${i.name}</div>`).join('')
```
**小程序写法**
```javascript
Page({
data: { total: '¥12,345', items: [] },
loadData() {
this.setData({
total: '¥12,345',
items: [{ name: '台桌' }, { name: '酒水' }]
})
}
})
```
```xml
<text>{{total}}</text>
<view wx:for="{{items}}" wx:key="name">
<text>{{item.name}}</text>
</view>
```
### 11.6 模式 E滚动联动
**H5 写法**
```javascript
window.addEventListener('scroll', () => {
const currentY = window.scrollY
if (currentY > 100) {
filterBar.classList.add('filter-bar-hidden')
}
})
```
**小程序写法**
```javascript
Page({
data: { filterBarVisible: true },
_lastScrollTop: 0,
_throttleTimer: null,
onPageScroll(e) {
// 节流:避免每帧 setData
if (this._throttleTimer) return
this._throttleTimer = setTimeout(() => {
this._throttleTimer = null
}, 100)
const scrollTop = e.scrollTop
const shouldHide = scrollTop > 100
// 只在状态真正变化时 setData
if (shouldHide !== !this.data.filterBarVisible) {
this.setData({ filterBarVisible: !shouldHide })
}
}
})
```
**关键规则**
1. 滚动事件处理函数中必须做节流100ms 以上)
2. 只在状态真正变化时调用 `setData`,避免无意义的重渲染
3. 不要在滚动事件中更新大量数据或触发复杂计算
### 11.7 模式 F页面导航
**H5 写法**
```javascript
window.location.href = 'task-detail.html?id=123'
history.back()
```
**小程序写法**
```javascript
// 普通页面跳转
wx.navigateTo({ url: '/pages/task-detail/task-detail?id=123' })
// 返回上一页
wx.navigateBack()
// TabBar 页面跳转
wx.switchTab({ url: '/pages/task-list/task-list' })
// 重定向(替换当前页)
wx.redirectTo({ url: '/pages/login/login' })
```
**关键规则**TabBar 页面(在 `app.json``tabBar.list` 中定义的)必须用 `wx.switchTab`,用 `navigateTo` 会静默失败。
---
## 12. 浏览器 API → 微信小程序 API 完整映射
### 12.1 导航
| H5 API | 小程序 API | 说明 |
|--------|-----------|------|
| `window.location.href = url` | `wx.navigateTo({ url })` | 普通页面跳转 |
| `window.location.replace(url)` | `wx.redirectTo({ url })` | 替换当前页 |
| `history.back()` | `wx.navigateBack()` | 返回上一页 |
| — | `wx.switchTab({ url })` | TabBar 页面跳转(必须用这个) |
### 12.2 滚动
| H5 API | 小程序 API | 说明 |
|--------|-----------|------|
| `window.scrollY` | `onPageScroll(e)``e.scrollTop` | 页面滚动位置 |
| `window.scrollTo(0, y)` | `wx.pageScrollTo({ scrollTop: y })` | 滚动到指定位置 |
| `element.scrollIntoView()` | `scroll-view``scroll-into-view="{{id}}"` | 滚动到指定元素 |
| `element.getBoundingClientRect()` | `wx.createSelectorQuery().select('.cls').boundingClientRect()` | 获取元素位置(异步) |
### 12.3 存储
| H5 API | 小程序 API | 说明 |
|--------|-----------|------|
| `localStorage.setItem(k, v)` | `wx.setStorageSync(k, v)` | 同步写入 |
| `localStorage.getItem(k)` | `wx.getStorageSync(k)` | 同步读取 |
| `localStorage.removeItem(k)` | `wx.removeStorageSync(k)` | 同步删除 |
| `localStorage.clear()` | `wx.clearStorageSync()` | 清空全部 |
### 12.4 剪贴板
| H5 API | 小程序 API |
|--------|-----------|
| `navigator.clipboard.writeText(text)` | `wx.setClipboardData({ data: text })` |
| `navigator.clipboard.readText()` | `wx.getClipboardData()` |
### 12.5 对话框
| H5 API | 小程序 API |
|--------|-----------|
| `alert(msg)` | `wx.showModal({ title: '', content: msg, showCancel: false })` |
| `confirm(msg)` | `wx.showModal({ title: '提示', content: msg })` |
| — | `wx.showToast({ title: msg, icon: 'success' })` |
### 12.6 系统信息
| 需求 | 小程序 API |
|------|-----------|
| 状态栏高度 | `wx.getSystemInfoSync().statusBarHeight` |
| 屏幕宽度 | `wx.getSystemInfoSync().windowWidth` |
| 安全区域 | `wx.getSystemInfoSync().safeArea` |
| 底部安全区高度 | `screenHeight - safeArea.bottom` |
---
## 第四部分:执行流程
## 13. 页面转换的标准执行流程10 步)
每迁移 1 个页面,都必须按以下顺序执行。
### 步骤 1页面资产盘点
**输入**HTML / CSS / JS / 图片资源
**动作**
- 抽取页面标题、路由名、入口参数、返回路径
- 列出页面块顶部、卡片区、列表区、底部操作区、弹层、浮层、Toast
- 列出页面状态:默认态、空态、加载态、错误态、选中态、展开态、禁用态
- 列出页面动效:淡入、位移、缩放、吸顶、滚动联动、长按、评分拖动
**输出**:页面迁移卡
### 步骤 2结构改写为 WXML 语义树
**动作**
- `div``view`
- 文本优先用 `text`
- 图片用 `image`
- 链接/跳转用 `navigator``bindtap`
- 表单元素替换为原生小程序组件
- 列表一律改为 `wx:for`
- 条件显示一律改为 `wx:if` / `hidden` / 条件 class
**禁止**
- 保留 H5 DOM 层级只是把标签名替换一下
-`text` 内嵌套 `view`
- 用字符串拼接 HTML 片段
### 步骤 3Tailwind 与自定义 CSS 拆解
**动作**
- 先按视觉功能拆成:布局、尺寸、文本、颜色、装饰、状态、动画
- 再判断每个值使用 `rpx` 还是 `px`
- 统一抽出公共 token不允许每页各自发散命名
**要求**
- 不保留"一长串 utility class 直接照搬"的中间态
- 对频繁出现的组合样式抽成语义类,如 `.card``.section-title``.tab--active`
### 步骤 4交互从 DOM 驱动改为数据驱动
**动作**
- 把所有 DOM 查询改成状态变量
- 把显隐、激活、展开、选中、校验、加载都改成 `data` 驱动
- 把滚动联动改成 `onPageScroll` + 节流 + 仅在状态变化时 `setData`
**输出**:状态表
状态表至少包含:
- 状态变量名
- 默认值
- 可选值
- 受哪个事件修改
- 影响哪些视图
### 步骤 5页面级动效实现
**执行规则**
- 纯显隐 / 透明度 / 位移 / 简单位移动画:优先 `transition` / `animation`
- 需要顺序编排、组合变换、受事件精确驱动:用 `wx.createAnimation()`
- 高频交互不要每帧 `setData`
### 步骤 6导航与参数改造
**动作**
- 普通页面:`wx.navigateTo`
- 替换当前页:`wx.redirectTo`
- tabBar 页面:`wx.switchTab`
- 返回:`wx.navigateBack`
- 页面参数:统一在 `onLoad(query)` 中读取
### 步骤 7安全区与导航栏补偿
**动作**
- 禁止使用 `env(safe-area-inset-*)`
- 通过系统信息计算状态栏与底部安全区
- 自定义导航栏页面必须统一实现,不得一页一套
### 步骤 8Mock 数据接入
**动作**
- 页面先用 mock 跑通
- 模板静态文案与后端数据字段分开
- 所有列表、徽标、状态标签都要有 mock
### 步骤 9真机回归与像素微调
**动作**
- 先看 412 宽主验收设备
- 再看至少一台 375 宽设备
- 出现不对齐时优先微调局部 `px/rpx`,不要推翻整体单位策略
### 步骤 10记录迁移说明
每页必须输出迁移说明,至少包含:
- 页面依赖资源
- 迁移状态变量
- 高风险点
- 已做降级项
---
## 14. 附录文档索引
### 14.1 附录清单
位于 `docs/miniprogram-dev/New/`
1. **附录索引**`h5_to_wechat_miniprogram_migration_bridge_appendix_index_v3.md`v3.1
2. **附录 A**`appendix_a_spacing_and_sizing_dictionary_v3.md` - Spacing 与尺寸字典
3. **附录 B**`appendix_b_typography_and_text_dictionary_v3.md`v3.1- 字体与文本字典
4. **附录 C**`appendix_c_color_dictionary_v3.md` - 颜色字典
5. **附录 D**`appendix_d_layout_class_mapping_dictionary_v3.md` - 布局类映射字典
### 14.2 使用方式
**AI 使用顺序**
1. 加载本主规范v3.0
2. 加载附录索引
3. 按需加载具体附录A/B/C/D
4. 执行迁移
**开发者使用顺序**
1. 先读本主规范了解整体流程和规则
2. 迁移时查阅附录进行 token 映射
3. 遇到问题时回到主规范查找解决方案
### 14.3 附录更新记录
- **v3.1**2026-03-14附录 B 补充 text 组件的 line-height 设置规则
- **v3.0**:初始版本,建立附录索引和四个附录字典
---
## 📚 完整导航
- **第一部分:执行规程**§1-4
- 输入输出契约
- 项目级强制决策
- 能力分级A/B/C 类)
- Tailwind CSS 行为说明
- **第二部分:技术换算**§5-9
- Spacing 换算表
- 字号与行高换算
- 颜色映射
- 布局类映射
- 装饰类映射
- **第三部分:交互改造**§10-12
- HTML→WXML 映射
- DOM 操作→数据驱动
- 浏览器 API→小程序 API
- **第四部分:执行流程**§13-14
- 10 步标准流程
- 附录文档索引
---
## 版本历史
- **v3.0**2026-03-14合并 v1.0 技术换算基础和 v2.0 执行规范,补充 line-height 关键发现
- **v2.0**:执行规范,强化输入输出契约和能力分级
- **v1.0**:技术换算基础,建立完整的映射表
---
## 📖 使用建议
### 对于 AI
1. **首次加载**:加载本主规范全文 + 附录索引
2. **执行迁移**:按 §13 的 10 步流程执行
3. **查表映射**:按需加载附录 A/B/C/D
4. **验证输出**:确保符合 §1.2 的输出物要求
### 对于开发者
1. **快速上手**:先读 §1-4 了解规则和决策
2. **技术细节**:需要时查阅 §5-12 的换算表
3. **执行迁移**:按 §13 的流程逐步完成
4. **问题排查**:回到对应章节查找解决方案
---
**文档完整,可以开始迁移!** 🚀