移动端适配
VitePress 默认主题已内置基本的响应式支持,但生产环境中的移动端体验仍需针对性优化。本文介绍如何系统性地适配移动端。
视口配置
基础视口设置
VitePress 默认已在主题中配置了视口,如需自定义,在配置中添加:
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=5' }]
]
})禁止缩放(不推荐)
注意
禁止用户缩放会影响可访问性,仅在特殊场景下使用。
ts
// 仅在特定场景使用
['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' }]主题色配置
移动浏览器会根据 theme-color 调整 UI 颜色:
ts
export default defineConfig({
head: [
['meta', { name: 'theme-color', content: '#ffffff' }],
// 深色模式需要动态切换(见下文)
]
})在主题入口动态切换主题色:
ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import { useData, watch } from 'vitepress'
export default {
extends: DefaultTheme,
setup() {
const { isDark } = useData()
watch(isDark, (dark) => {
const meta = document.querySelector('meta[name="theme-color"]')
if (meta) {
meta.setAttribute('content', dark ? '#1b1b1f' : '#ffffff')
}
}, { immediate: true })
}
}响应式布局
CSS 断点约定
VitePress 默认主题使用的断点:
| 断点 | 宽度 | 说明 |
|---|---|---|
| 移动端 | < 640px | 手机竖屏 |
| 平板 | 640px - 959px | 平板竖屏 |
| 桌面 | ≥ 960px | 桌面浏览器 |
| 宽屏 | ≥ 1440px | 大屏幕 |
自定义响应式样式
css
/* .vitepress/theme/styles/responsive.css */
/* 移动端隐藏侧边栏辅助信息 */
@media (max-width: 639px) {
.sidebar-helper {
display: none;
}
}
/* 平板端调整内容间距 */
@media (min-width: 640px) and (max-width: 959px) {
.content-container {
padding: 0 24px;
}
}
/* 桌面端宽内容 */
@media (min-width: 960px) {
.content-container {
max-width: 900px;
}
}首页响应式定制
css
/* 移动端首页 Hero 文字大小 */
@media (max-width: 639px) {
.VPHero .name {
font-size: 2rem !important;
}
.VPHero .text {
font-size: 1.25rem !important;
}
.VPHero .tagline {
font-size: 0.95rem !important;
}
}Features 网格响应式
VitePress 首页 Features 默认 3 列,移动端自动调整为单列。如需自定义:
css
/* 平板 2 列 */
@media (min-width: 640px) and (max-width: 959px) {
.VPFeatures .items {
grid-template-columns: repeat(2, 1fr) !important;
}
}触控优化
触控目标尺寸
确保可点击元素满足最小触控尺寸(48×48px):
css
/* 增大导航链接触控区域 */
.VPNav .link {
padding: 8px 12px;
min-height: 48px;
display: flex;
align-items: center;
}
/* 增大侧边栏链接触控区域 */
.VPSidebar .link {
padding: 8px 16px;
min-height: 44px;
}触控反馈
css
/* 按钮触控反馈 */
.vp-button {
-webkit-tap-highlight-color: transparent;
transition: background-color 0.15s ease;
}
.vp-button:active {
background-color: var(--vp-c-brand-soft);
}滚动优化
css
/* 平滑滚动 */
html {
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
/* 移动端侧边栏滚动优化 */
.VPSidebar {
overscroll-behavior: contain;
}移动端性能
图片优化
ts
// .vitepress/config.mts
export default defineConfig({
head: [
// 预连接图片 CDN
['link', { rel: 'preconnect', href: 'https://images.example.com' }]
]
})在 Markdown 中使用响应式图片:
html
<picture>
<source srcset="/images/hero-mobile.webp" media="(max-width: 639px)">
<source srcset="/images/hero-tablet.webp" media="(max-width: 959px)">
<img src="/images/hero-desktop.webp" alt="Hero 图片" loading="lazy">
</picture>代码块移动端优化
VitePress 默认对代码块做了水平滚动处理。进一步优化:
css
/* 代码块移动端优化 */
.vp-code {
font-size: 13px;
-webkit-text-size-adjust: 100%;
}
/* 长代码块添加滚动提示 */
.vp-code-group {
position: relative;
}
.vp-code-group::after {
content: '→';
position: absolute;
right: 8px;
top: 8px;
opacity: 0.3;
pointer-events: none;
}
@media (min-width: 640px) {
.vp-code-group::after {
display: none;
}
}减少移动端动画
css
/* 尊重用户偏好:减少动画 */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}iOS 适配
安全区域
适配 iPhone 刘海屏和底部安全区域:
css
/* 适配安全区域 */
.VPNav {
padding-top: env(safe-area-inset-top);
}
.VPFooter {
padding-bottom: env(safe-area-inset-bottom);
}
.VPSidebar {
padding-bottom: env(safe-area-inset-bottom);
}禁止文字大小调整
iOS Safari 会自动调整文字大小,可能导致布局异常:
css
body {
-webkit-text-size-adjust: 100%;
}处理 iOS 弹性滚动
css
/* 防止 iOS 弹性滚动导致的布局问题 */
html, body {
overscroll-behavior: none;
}
/* 侧边栏允许弹性滚动但不影响主内容 */
.VPSidebar {
overscroll-behavior-y: contain;
}Android 适配
地址栏颜色
Chrome Android 支持通过 meta 标签自定义地址栏颜色:
ts
export default defineConfig({
head: [
['meta', { name: 'theme-color', content: '#ffffff' }]
]
})PWA 支持
为移动端添加 PWA 支持,实现离线访问和添加到主屏幕:
ts
// .vitepress/config.mts
import { withPwa } from '@vite-pwa/vitepress'
export default withPwa(defineConfig({
pwa: {
registerType: 'autoUpdate',
manifest: {
name: 'VitePress 学习指南',
short_name: 'VP 指南',
theme_color: '#6366f1',
icons: [
{
src: '/pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
}
}
}))移动端测试
Chrome DevTools 模拟
- 打开开发者工具(F12)
- 点击设备模拟按钮(Ctrl+Shift+M)
- 选择设备或自定义尺寸
常用测试设备
| 设备 | 宽度 | 高度 | DPR |
|---|---|---|---|
| iPhone SE | 375 | 667 | 2 |
| iPhone 14 | 390 | 844 | 3 |
| iPhone 14 Pro Max | 430 | 932 | 3 |
| iPad | 768 | 1024 | 2 |
| iPad Pro | 1024 | 1366 | 2 |
| Pixel 7 | 412 | 915 | 2.625 |
| Galaxy S23 | 360 | 780 | 3 |
真机调试
bash
# 使用 Vite 的 host 选项允许局域网访问
vitepress dev docs --host
# 输出类似:
# ➜ Local: http://localhost:5173/
# ➜ Network: http://192.168.1.100:5173/在手机浏览器中访问 Network 地址即可调试。
自动化测试
使用 Playwright 进行移动端自动化测试:
ts
// tests/mobile.spec.ts
import { test, expect } from '@playwright/test'
test.use({
viewport: { width: 375, height: 667 },
isMobile: true
})
test('移动端导航菜单可展开', async ({ page }) => {
await page.goto('/')
// 点击汉堡菜单
await page.click('.VPNavBarHamburger')
// 验证侧边栏可见
await expect(page.locator('.VPSidebar')).toBeVisible()
})
test('移动端代码块可水平滚动', async ({ page }) => {
await page.goto('/basics/markdown')
// 验证代码块可滚动
const codeBlock = page.locator('.vp-code').first()
await expect(codeBlock).toBeVisible()
})常见问题
移动端侧边栏闪现
在页面加载时侧边栏可能短暂闪现,添加以下 CSS:
css
/* 防止移动端侧边栏闪现 */
@media (max-width: 959px) {
.VPSidebar {
opacity: 0;
transition: opacity 0.25s ease;
}
.VPSidebar.open {
opacity: 1;
}
}移动端字体过大
iOS Safari 有时会自动调大字体,添加:
css
body {
-webkit-text-size-adjust: 100%;
}移动端 100vh 问题
移动端浏览器的 100vh 包含地址栏高度,使用 dvh 修复:
css
.VPHero {
/* 现代浏览器 */
height: 100dvh;
/* 回退方案 */
height: calc(var(--vh, 1vh) * 100);
}ts
// .vitepress/theme/composables/useViewportHeight.ts
import { onMounted, onUnmounted } from 'vue'
export function useViewportHeight() {
const setVH = () => {
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`)
}
onMounted(() => {
setVH()
window.addEventListener('resize', setVH)
})
onUnmounted(() => {
window.removeEventListener('resize', setVH)
})
}检查清单
- [ ] 视口 meta 标签已配置
- [ ] 主题色已配置(含深色模式)
- [ ] 触控目标尺寸 ≥ 44px
- [ ] 代码块移动端可水平滚动
- [ ] 图片使用响应式加载
- [ ] iOS 安全区域已适配
- [ ] 禁止文字大小自动调整
- [ ] 至少在 3 种设备尺寸下测试
- [ ] 已处理
prefers-reduced-motion - [ ] PWA manifest 已配置(可选)