HTML 转小程序页面
核心原则
- 1:1 视觉复刻:最终小程序页面必须与原HTML设计稿在布局、配色、间距、字体上完全一致。
- 小程序语法正确:生成的 WXML/WXSS/JS/JSON 必须符合微信小程序原生语法规范。
- 导航栏意识:顶部导航栏高度需适配,底部 tabBar 不写在页面中(由 app.json 统一配置)。
- Skyline 兼容:项目使用 Skyline 渲染器(glass-easel),注意默认 flex 布局和 border-box。
- 先检查后转换:动手写代码前,必须先检查目标项目的
app.json现有配置,避免与已有结构冲突。
转换流程
0. 转换前检查(必须先做)
在分析设计稿之前,务必先读取目标小程序项目的 app.json,确认以下关键配置:
0.1 检查 tabBar 配置
// 目标项目的 app.json
{
"tabBar": { ... } // <-- 是否存在?
}
| 场景 | 处理方式 |
|---|---|
app.json 已有 tabBar |
页面内绝对不要写底部导航代码;内容区域用 padding-bottom 预留 tabBar 高度 |
app.json 没有 tabBar,但设计稿有底部导航 |
需要在 app.json 中补充 tabBar 配置;为每个 tab 准备 iconPath / selectedIconPath 图标;页面内不写底部导航 |
app.json 没有 tabBar,设计稿也没有底部导航 |
页面内不写,无需处理 |
禁忌:绝不允许在每个页面内重复写自定义底部导航。如果设计稿有底部导航而
app.json没有tabBar,必须先补充app.json配置,而不是把导航写到页面里。
0.2 检查 renderer 和 rendererOptions
{
"renderer": "skyline",
"rendererOptions": {
"skyline": {
"defaultDisplayBlock": true, // <-- 注意这个!
"defaultContentBox": true // <-- 注意这个!
}
}
}
- 如果
defaultDisplayBlock: true→ 元素默认是block,不是flex,需要时显式写display: flex - 如果
defaultContentBox: true→ 元素默认是content-box,不是border-box,需要时显式写box-sizing: border-box - 不要 blindly 相信"Skyline 默认 flex + border-box"的说法,必须以
app.json的实际配置为准
0.3 检查 navigationStyle
{
"window": {
"navigationStyle": "custom" // <-- 全局自定义导航栏?
}
}
- 如果全局
navigationStyle: custom,所有页面都不写系统导航栏,自行实现顶部标题栏 - 如果是
default,按单个页面的需求决定
1. 分析输入
- 识别 HTML 结构层次(容器、文本、图片、表单、列表等)
- 提取所有 CSS 样式(行内、内联、外链),记录颜色、字号、间距、圆角、阴影
- 列出图片资源及尺寸,标注是背景图还是
<img>内容图 - 识别交互元素(按钮点击、表单提交、轮播、弹窗、下拉刷新等)
- 识别图标类型:记录设计稿中使用的图标字体(如 Material Symbols、FontAwesome 等),这些无法直接在小程序中使用
2. 规划页面结构
- 判断导航栏模式:原设计稿顶部是否有自定义标题栏?如果有,页面 JSON 配
navigationStyle: custom;否则用系统默认导航栏。 - 确认 tabBar(基于 0.1 的检查结果):
- 若该页面属于底部 tab 页 → 页面内不写底部导航代码;内容区底部预留
padding-bottom: calc(120rpx + env(safe-area-inset-bottom)) - 若
app.json没有tabBar但设计稿有 → 先补充app.json的tabBar配置,再写页面
- 若该页面属于底部 tab 页 → 页面内不写底部导航代码;内容区底部预留
- 选择滚动方案:
- 优先使用
page自身滚动(推荐) - 避免使用
scroll-view作为页面主滚动容器,尤其是嵌套在 flex 容器内时,Skyline 下高度计算极易失效 - 只有局部滚动区域(如横向滚动卡片、固定高度的列表)才使用
scroll-view
- 优先使用
- 识别可复用组件:如卡片、列表项、弹窗等,评估是否需要拆分为自定义组件。
3. 图标处理策略
设计稿中的图标通常来自图标字体(如 Material Symbols),小程序无法直接加载外部字体。按优先级处理:
| 方案 | 适用场景 | 实现方式 |
|---|---|---|
| CSS 绘制 | 简单几何图标(箭头、放大镜、方块等) | 用 ::before/::after + border/background 绘制 |
| 本地图片 | 复杂图标、品牌 Logo | 导出为 PNG/SVG,放入 images/ 目录 |
| iconfont | 大量图标需统一管理 | 在 iconfont.cn 创建项目,下载字体文件引入 |
| 系统图标 | 通用操作(如设置、分享) | 使用微信内置的 icon 组件(类型有限) |
注意:
- 绝不要把
<span class="material-symbols-outlined">...</span>这类字体图标直接搬到 WXML 中,小程序无法渲染外部字体 - 图标颜色必须与设计稿一致,不能只画形状不管配色
4. HTML → WXML 转换
- 按标签映射表替换(详见
references/html-to-wxml-mapping.md) - 保留
class和id作为 WXSS 选择器依据 - 将事件属性转换为小程序事件(
onclick→bindtap等) - 用
wx:if/wx:for替代模板条件/循环语法
5. CSS → WXSS 转换
- 将
px按 750px 设计稿基准转为rpx(1px = 1rpx) - 移除小程序不支持的属性(如
cursor、*选择器,详见references/css-to-wxss-mapping.md) - 根据
app.json的实际rendererOptions决定是否需要显式声明display: flex和box-sizing: border-box - 页面根节点设置
page { background-color: #xxx; }
6. JS 逻辑转换
- 将 DOM 操作(
document.querySelector、innerHTML等)转换为小程序数据驱动(setData) - 将原生事件处理转换为 Page 生命周期和事件函数
- 将 AJAX/Fetch 请求转换为
wx.request - 常见交互模式参考
references/interaction-patterns.md
7. 图片资源处理
- 页面内使用的图片建议放入页面同级目录(如
images/)或上传到 CDN <img src="...">转为<image src="..." mode="aspectFill|widthFix" />- 背景图优先用网络地址;如需本地背景图,用
<image>模拟或确认 base64 方案
8. 质量检查
生成完成后逐项确认:
- 已读取
app.json,确认tabBar/rendererOptions/navigationStyle配置 - WXML 中无 HTML 残留标签(如
<div>、<span>、<br>) - WXSS 无
*通配符选择器,无小程序不支持的伪类(如:hover) - 页面滚动正常:使用
page自身滚动,内容超出屏幕可上下滑动 - 若使用自定义导航栏,导航栏高度计算正确,内容不被遮挡
- 若页面是 tabBar 页:
app.json已配置tabBar,页面内无底部导航代码,内容底部已预留padding-bottom - 若设计稿有底部导航但
app.json无tabBar:已补充app.json配置,不是把导航写到页面里 - 所有图标已处理(CSS 绘制 / 本地图片 / iconfont),无字体图标残留
- 所有交互元素有对应
bindtap/bindinput/bindsubmit事件 - 图片路径正确,
mode属性设置合理 - 页面
.json配置完整(navigationBarTitleText、navigationStyle、renderer: skyline等)
关键注意事项
顶部导航栏
| 模式 | 页面 JSON 配置 | WXML 处理 |
|---|---|---|
| 系统默认导航栏 | "navigationStyle": "default" |
不写导航栏代码,页面内容从导航栏下方自动开始 |
| 完全自定义导航栏 | "navigationStyle": "custom" |
自行实现标题栏,需计算状态栏高度+胶囊按钮高度 |
自定义导航栏 JS 计算示例:
Page({
data: {
statusBarHeight: 0,
navBarHeight: 44
},
onLoad() {
const sys = wx.getSystemInfoSync();
const menu = wx.getMenuButtonBoundingClientRect();
this.setData({
statusBarHeight: sys.statusBarHeight,
navBarHeight: (menu.top - sys.statusBarHeight) * 2 + menu.height
});
}
});
底部 TabBar(反复强调)
tabBar 只能在 app.json 中配置,页面内绝对不要写底部固定导航条代码。
正确的分工:
app.json:配置tabBar.list,定义 tab 的页面路径、文字、图标- 页面
.wxss:内容区底部预留高度padding-bottom: calc(120rpx + env(safe-area-inset-bottom)) - 页面
.js:无 tab 切换逻辑(由系统 tabBar 处理)
滚动方案选择
| 方案 | 适用场景 | 注意事项 |
|---|---|---|
page 自身滚动 |
页面主内容滚动(首选) | 无额外配置,最稳定 |
scroll-view |
局部固定高度区域滚动 | 必须设置明确 height,避免嵌套在 flex 容器内作为全页滚动 |
onPullDownRefresh |
下拉刷新 | 页面 JSON 配 enablePullDownRefresh: true |
Skyline 下 scroll-view 的坑:
scroll-view嵌套在display: flex的父容器中时,flex: 1无法正确计算可滚动高度- 即使设置了
height: 100%,如果父容器高度不确定,也会导致无法滚动 - 结论:页面级滚动不要用
scroll-view,让page自己滚
Skyline 渲染器特性
- 是否默认
display: flex取决于app.json的rendererOptions.skyline.defaultDisplayBlock - 是否默认
box-sizing: border-box取决于rendererOptions.skyline.defaultContentBox - 不支持
*通配符选择器 - 废弃组件/API 不可用
- 自定义组件框架:
componentFramework: "glass-easel"
参考文档
按需加载对应参考文件:
| 文件 | 何时查阅 |
|---|---|
references/html-to-wxml-mapping.md |
进行标签映射时 |
references/css-to-wxss-mapping.md |
转换样式、处理单位、排查不兼容属性时 |
references/miniprogram-layout-rules.md |
处理导航栏、tabBar 占位、页面 JSON 配置时 |
references/interaction-patterns.md |
转换按钮点击、表单、页面跳转、弹窗等交互时 |
references/icon-handling-guide.md |
处理设计稿中的图标字体时(新增) |