4.1 rpx 单位详解
rpx(Responsive Pixel,响应式像素)是 uni-app(以及微信小程序)中最核心的尺寸单位。它解决了一个根本问题:如何在不同屏幕尺寸的设备上保持一致的视觉效果?
rpx 的计算原理
rpx 的设计假设:无论设备屏幕多宽,其逻辑宽度均为 750rpx。
这意味着:
- 在 375px 宽的 iPhone(@2x)上:
1rpx = 375/750 = 0.5px - 在 414px 宽的 iPhone Plus 上:
1rpx = 414/750 ≈ 0.552px - 在 390px 宽的 iPhone 14 上:
1rpx = 390/750 = 0.52px
设计师用 750px 宽的设计稿(一倍图的两倍分辨率,即 @2x),量出来多少像素,写代码时就写多少 rpx,各设备会自动等比缩放。
设计稿到代码的换算公式
设计稿标注值(px) = 代码中的 rpx 值
即:设计稿 32px → 代码写 32rpx(前提是设计稿宽度为 750px)
如果设计稿是 375px 宽(@1x),则设计稿标注值 × 2 = rpx 值
即:设计稿标注 16px → 代码写 32rpx
rpx vs px vs rem vs vw
| 单位 | 跨端支持 | 适用场景 | 备注 |
|---|---|---|---|
rpx | 全平台 | 布局尺寸、间距、字体 | 首选,随屏幕宽度等比缩放 |
px | 全平台 | 1px 边框、固定尺寸元素 | 物理像素,不随屏幕缩放 |
upx | App 端(旧版) | 同 rpx | 旧版 App 端单位,已弃用,统一用 rpx |
rem | H5、App | H5 端字体缩放 | 小程序不支持 |
vw/vh | H5、App(部分) | 全屏布局 | 微信小程序已支持,但兼容性需测试 |
% | 全平台 | 相对父元素的比例布局 | 需要父元素有明确尺寸 |
在高分屏(@2x、@3x)上,1rpx 等于 0.5px,会渲染为非常细的线,效果接近"真 1px 物理像素"。如果要实现真正的细边框,使用 1rpx 而不是 1px。这是小程序和 App 端的常见最佳实践。
4.2 Flexbox 布局
在 uni-app 中,Flexbox 是唯一推荐的布局方式。这并不是 uni-app 的限制,而是因为:
- nvue 和 uvue(原生渲染模式)不支持 CSS Grid、position: fixed 等传统布局
- Flexbox 在所有平台(H5、小程序、App)都有一致的支持
- Flexbox 本身足够强大,能满足绝大多数布局需求
常用 Flexbox 布局模式
/* 垂直排列(默认),子元素从上到下 */
.column {
display: flex;
flex-direction: column;
}
/* 水平排列,子元素从左到右 */
.row {
display: flex;
flex-direction: row;
align-items: center; /* 垂直居中 */
}
/* 经典两端对齐 + 垂直居中(常用于列表项) */
.list-item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 24rpx;
}
/* 完全居中(登录页、空状态) */
.center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
/* 等分布局(底部导航、Tab 栏) */
.tab-bar {
display: flex;
flex-direction: row;
}
.tab-item {
flex: 1; /* 等分剩余空间 */
display: flex;
flex-direction: column;
align-items: center;
}
/* 换行瀑布流(商品列表,每行两个) */
.goods-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
padding: 20rpx;
}
.goods-item {
width: 345rpx; /* (750 - 40 - 20) / 2 */
margin-bottom: 20rpx;
}
flex 属性详解
flex 是 flex-grow、flex-shrink、flex-basis 的简写:
flex: 1 1 0%。子元素均分剩余空间,这是最常用的写法,实现等分布局。flex-grow: 2 的元素会获得 flex-grow: 1 元素两倍的剩余空间。auto 表示使用元素自身 width/height,0 表示从零开始分配空间。4.3 nvue 与 uvue 的样式限制
当使用原生渲染模式(nvue 文件用于传统 uni-app,uvue 文件用于 uni-app x)时,CSS 支持范围有所限制,因为底层不再是 WebView,而是原生视图系统。
nvue/uvue 不支持的 CSS 特性
position: fixed— 原生端用<cover-view>或 uni-app x 的层叠 API 替代float— 完全不支持- CSS Grid — 仅 Flexbox 可用
background-image: url()(在 uvue 中有限制)— 用<image>组件代替- CSS 伪元素
::before、::after— 不支持 - CSS 动画的部分属性 — 需使用
uni.createAnimation或原生动画 - 多个 class 的层叠(某些情况下)— 建议合并为单一类
nvue/uvue 必须使用的样式规范
/* ✅ 正确:单位用 px(nvue 中没有 rpx,需要用 uni.getWindowInfo 换算)*/
.box {
width: 200px;
height: 100px;
background-color: #ffffff;
border-radius: 8px;
display: flex;
flex-direction: column;
}
/* ✅ 正确:所有样式必须写在 <style> 中,不支持内联复杂样式 */
/* ❌ 错误:不支持 scoped(nvue 页面级别作用域天然隔离) */
/* ✅ 推荐:线性渐变(部分支持)*/
.gradient {
background-image: linear-gradient(135deg, #0ea5e9, #0284c7);
}
/* ❌ 不支持:box-shadow(uvue 中有替代方案)*/
/* ❌ 不支持:text-shadow */
uni-app x uvue 的改进
uni-app x 的 uvue 相比 nvue 有了很大的样式能力提升:
- 支持
rpx单位(nvue 不支持) - 支持 CSS 变量
var(--color) - 支持更多 Flexbox 属性
- 支持
clip-path(Android 端) - 动画系统更接近 CSS 标准
4.4 样式作用域与 scoped
<!-- .vue 文件中的样式默认是全局的 -->
<style>
.title { color: red; } /* 影响所有页面 */
</style>
<!-- 添加 scoped 后,样式只影响当前组件 -->
<style scoped>
.title { color: red; } /* 仅影响当前组件内的 .title */
</style>
<!-- 深度选择器:修改子组件内部的样式 -->
<style scoped>
/* Vue 3 的深度选择器语法 */
:deep(.child-class) {
color: blue;
}
</style>
4.5 全局样式与主题
uni-app 支持通过 App.vue 的 <style> 块定义全局样式,这里定义的样式会作用于所有页面:
/* App.vue */
<style>
/* CSS 变量定义(主题色系)*/
page { /* 小程序中等同于 body */
--primary: #0ea5e9;
--primary-dark: #0284c7;
--text: #292524;
--text-2: #57534e;
--bg: #f5f5f0;
--radius: 16rpx;
font-size: 32rpx;
background-color: var(--bg);
}
/* 通用工具类 */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.mt-2 { margin-top: 16rpx; }
.mt-4 { margin-top: 32rpx; }
.p-4 { padding: 32rpx; }
.text-primary { color: var(--primary); }
.text-sm { font-size: 28rpx; }
.text-lg { font-size: 36rpx; }
.font-bold { font-weight: 700; }
</style>
4.6 安全区域适配
现代 iPhone 和 Android 设备有刘海、圆角、底部 Home Indicator 等设计,内容需要避开这些区域:
/* 适配 iOS 安全区域(刘海屏、底部 Home 条)*/
.header {
padding-top: constant(safe-area-inset-top); /* iOS 11 */
padding-top: env(safe-area-inset-top); /* iOS 11.2+ */
}
.footer {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
在 uni-app 中,可以通过 API 获取安全区域信息并动态设置:
const windowInfo = uni.getWindowInfo()
const statusBarHeight = windowInfo.statusBarHeight // 状态栏高度(px)
const safeAreaInsets = windowInfo.safeArea // 安全区域
// 通常自定义导航栏的高度 = 状态栏高度 + 44px(内容区)
const navBarHeight = statusBarHeight + 44 // 单位 px
4.7 多端样式兼容策略
面对 H5、小程序、App 的样式差异,以下是推荐的兼容策略:
策略一:优先使用 rpx + Flexbox(通吃所有平台)
这是成本最低的策略。避免使用任何平台特有的 CSS,只用 rpx 单位和 Flexbox 布局,确保在所有平台表现一致。
策略二:条件编译特殊样式
<style>
.nav-bar {
height: 88rpx;
}
/* #ifdef H5 */
.nav-bar {
height: 100px;
position: sticky;
top: 0;
}
/* #endif */
/* #ifdef APP */
.nav-bar {
/* App 端加上状态栏高度 */
padding-top: 44px;
}
/* #endif */
</style>
策略三:动态样式(JavaScript 计算)
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const navStyle = ref({})
onMounted(() => {
const info = uni.getWindowInfo()
navStyle.value = {
paddingTop: `${info.statusBarHeight}px`,
height: `${info.statusBarHeight + 44}px`
}
})
</script>
<template>
<view class="nav-bar" :style="navStyle">
<text>自定义导航栏</text>
</view>
</template>
4.8 小结
- rpx 是跨端首选单位,基于 750 等分屏幕宽度,设计稿 1:1 转换
- 1rpx 实现真 1px 细边框,比
1px更细腻 - Flexbox 是唯一可靠的跨端布局方案,CSS Grid 在原生渲染模式下不可用
- nvue/uvue 不支持
::before/::after、position: fixed、box-shadow等 CSS 特性 - 通过 CSS 变量定义主题色,实现集中式主题管理
- 使用
env(safe-area-inset-*)适配刘海屏和底部安全区域 - 复杂的平台差异通过条件编译 CSS 处理