一、面试题:同一个 URL,手机打开是移动端页面,电脑打开是 PC 页面,怎么实现?
1. 核心思路(一句话)
主要看移动端和 PC 端的页面差异:差异小,用响应式布局共用一套页面;差异大,服务端根据 User-Agent 等请求信息识别终端,再分别提供两套页面。
主要矛盾
不是"怎么判断设备",而是"两个端到底应该共用多少代码"。
次要矛盾
- 屏幕尺寸怎么适配
- 是否需要不同的 HTML 结构
- 是否需要不同的 JavaScript 逻辑
- 是否需要分别部署
- 首屏性能和资源体积
- SEO 和缓存策略
二、解决方案流程图
text
同一个 URL
│
▼
移动端和 PC 端差异大吗?
┌──────┴──────┐
小 大
│ │
▼ ▼
共用一套页面 分开提供页面
│ │
▼ ▼
响应式设计 服务端识别终端
│ │
┌──────┴──────┐ ▼
│ │ User-Agent
▼ ▼ │
CSS媒体查询 流体布局 ▼
│ │ PC / Mobile
│ │ │
▼ │ ┌───┴───┐
少量布局差异 │ ▼ ▼
直接调整 CSS │ PC页面 移动页面
│ │
└──────┬──────┘
▼
共用代码较多
三、方案一:流体布局
核心思路
让页面尺寸随着视口变化,通过百分比、相对单位、弹性布局等方式自动缩放和排列。
例如:
text
屏幕变宽
↓
容器变宽
↓
内容跟着变宽
屏幕变窄
↓
容器变窄
↓
内容跟着变窄
示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
/* 页面主体宽度随视口变化 */
.container {
width: 80%;
max-width: 1200px;
margin: 0 auto;
/*
* 使用 Flexbox 让内部内容具备弹性布局能力。
*/
display: flex;
gap: 20px;
}
.sidebar {
/*
* 左侧固定一个相对稳定的宽度。
*/
width: 240px;
flex-shrink: 0;
}
.content {
/*
* 剩余空间全部交给内容区域。
*/
flex: 1;
min-width: 0;
}
img {
/*
* 图片不能超过父容器。
*/
max-width: 100%;
height: auto;
}
</style>
</head>
<body>
<div class="container">
<aside class="sidebar">
左侧菜单
</aside>
<main class="content">
<h1>文章标题</h1>
<p>页面内容随着容器宽度变化。</p>
<img src="image.jpg" alt="示例图片" />
</main>
</div>
</body>
</html>
底层原理
流体布局本质上是:
text
视口尺寸
↓
CSS布局计算
↓
百分比 / vw / rem / Flexbox / Grid
↓
重新计算元素尺寸和位置
↓
页面随着空间变化
它解决的是:
"同一套页面,在不同尺寸下怎么自然缩放和排列?"
但它不一定解决:
"手机和 PC 的页面结构、功能、交互完全不同怎么办?"
因此,不能简单把"响应式设计"等同于"所有页面只需要缩放"。
四、方案二:CSS 媒体查询
核心思路
页面和组件基本共用,只在不同屏幕条件下修改布局、尺寸、显示状态等样式。
这是实际开发中非常常见的方案。
text
同一份 HTML
│
▼
CSS 媒体查询
│
┌────┴────┐
▼ ▼
PC样式 移动端样式
示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<style>
/*
* 默认按照移动端设计。
*/
.container {
display: flex;
flex-direction: column;
gap: 16px;
}
.sidebar {
width: 100%;
}
.desktop-only {
display: none;
}
/*
* 当视口宽度达到 768px 时,
* 切换到更适合平板/PC的布局。
*/
@media (min-width: 768px) {
.container {
flex-direction: row;
}
.sidebar {
width: 240px;
flex-shrink: 0;
}
.desktop-only {
display: block;
}
}
</style>
</head>
<body>
<div class="container">
<aside class="sidebar">
菜单
</aside>
<main>
<h1>商品详情</h1>
<!-- PC端额外展示的内容 -->
<section class="desktop-only">
PC端扩展信息
</section>
<p>商品描述</p>
</main>
</div>
</body>
</html>
底层原理
浏览器会根据当前媒体环境判断 CSS 条件是否成立:
text
浏览器获取视口尺寸
↓
匹配 @media 条件
↓
条件成立
↓
应用对应 CSS
↓
重新计算样式和布局
例如:
css
@media (max-width: 767px) {
/* 移动端 */
}
@media (min-width: 768px) {
/* PC / 平板 */
}
它的优势
如果两端:
text
业务逻辑相同
组件结构基本相同
数据基本相同
只有布局和少量展示差异
那么:
text
一套 HTML
+
一套 JavaScript
+
少量媒体查询 CSS
开发和维护成本比较低。
五、JavaScript 如何根据屏幕尺寸执行不同逻辑?
如果只是样式变化,优先使用 CSS 媒体查询。
如果确实存在:
不同屏幕尺寸需要执行不同 JavaScript 逻辑
可以使用 window.matchMedia()。
示例
javascript
// 创建一个媒体查询对象。
// 判断当前视口是否属于移动端。
const mediaQuery = window.matchMedia("(max-width: 767px)");
function handleViewportChange(event) {
if (event.matches) {
// 移动端逻辑
console.log("当前是移动端布局");
} else {
// PC端逻辑
console.log("当前是 PC 端布局");
}
}
// 页面初始化时先执行一次。
handleViewportChange(mediaQuery);
// 当视口尺寸发生变化并导致匹配结果变化时执行。
mediaQuery.addEventListener("change", handleViewportChange);
六、方案三:服务端识别终端,分别提供页面
如果移动端和 PC 端已经是两套完全不同的页面,那么可以在服务端根据请求信息判断终端。
最常见的信息之一就是:User-Agent 请求头。
text
浏览器
│
│ 请求同一个 URL
▼
反向代理 / Web服务器
│
│ 读取 User-Agent
▼
判断终端类型
│
├──────────────┐
▼ ▼
移动端 PC端
│ │
▼ ▼
移动端页面 PC页面
注意
User-Agent 负责提供终端识别依据;服务器可以根据判断结果直接返回对应页面,也可以选择重定向到另一个 URL。
七、服务端直接返回不同页面
例如 Node.js:
javascript
import http from "node:http";
const server = http.createServer((req, res) => {
/*
* User-Agent 是浏览器发送过来的请求头。
*
* 注意:
* User-Agent 只能作为终端判断依据之一,
* 不能把它当成绝对可靠的"设备身份证"。
*/
const userAgent = req.headers["user-agent"] || "";
// 这里只演示最简单的移动端判断。
const isMobile = /Android|iPhone|iPad|Mobile/i.test(userAgent);
res.setHeader("Content-Type", "text/html; charset=utf-8");
if (isMobile) {
// 移动端直接返回移动端 HTML。
res.end(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>移动端页面</title>
</head>
<body>
<h1>这是移动端页面</h1>
</body>
</html>
`);
} else {
// PC端直接返回 PC HTML。
res.end(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>PC端页面</title>
</head>
<body>
<h1>这是 PC 端页面</h1>
</body>
</html>
`);
}
});
server.listen(3000);
这种情况下:
text
https://example.com/
始终是同一个 URL。
但是:
text
手机请求
↓
服务器识别 User-Agent
↓
返回移动端 HTML
PC请求
↓
服务器识别 User-Agent
↓
返回 PC HTML
URL 可以完全不变。
八、如果需要跳转到移动端 URL,可以使用 302
例如:
text
PC:
https://example.com/
移动端:
https://m.example.com/
服务器判断移动端后:
http
HTTP/1.1 302 Found
Location: https://m.example.com/
浏览器收到以后,再请求:
text
https://m.example.com/
流程:
text
手机
│
│ GET /
▼
服务器
│
│ 判断 User-Agent
▼
移动端
│
│ 302 + Location
▼
浏览器
│
│ GET https://m.example.com/
▼
移动端服务器
│
▼
移动端页面
301 和 302 的区别
text
301
→ 永久重定向
302
→ 临时重定向
302 是临时重定向,301 是永久重定向。
如果只是根据当前设备临时把用户引导到另一个地址,通常不会因为"设备不同"就直接把它理解成永久迁移。
九、三种方案到底怎么选?
| 方案 | 页面关系 | 核心特点 | 适用场景 |
|---|---|---|---|
| 流体布局 | 基本同一套页面 | 尺寸随空间变化 | 页面结构非常简单 |
| CSS 媒体查询 | 高度复用 | 不同尺寸应用不同 CSS | 两端差异较小 |
| 服务端识别终端 | 可以完全不同 | 服务端决定返回哪套页面 | 两端差异较大 |
| 独立部署 | 两套应用 | 独立开发、部署、维护 | 两端业务和交互差异非常大 |
十、真正应该怎么判断?
不要简单问:
"页面复杂不复杂?"
更应该问:
"移动端和 PC 端的差异有多大?"
例如:
场景一:电商详情页
text
PC:
商品图片 + 商品信息 + 推荐商品
移动端:
商品图片 + 商品信息 + 推荐商品
主要只是:
text
布局变化
字号变化
间距变化
部分内容隐藏
这种情况下适合:
text
响应式布局
+
CSS 媒体查询
场景二:管理后台
PC:
text
左侧导航
顶部导航
多列数据表格
复杂筛选
移动端:
text
底部导航
卡片列表
抽屉筛选
完全不同的交互
如果强行使用一套 DOM:
text
大量 if 判断
大量 CSS 覆盖
大量移动端/PC端分支
最终容易变成:
text
一套代码
↓
大量条件判断
↓
维护困难
↓
构建产物也可能包含两端不需要的代码
这时候可以考虑:
text
PC端应用
+
移动端应用
+
公共组件 / 公共业务模块
+
服务端路由或网关进行终端分流
十一、一个更合理的现代工程方案
实际项目不要简单理解成:
"移动端和 PC 端不同,就复制两份代码。"
更合理的是:
text
同一个业务
│
┌────────────┴────────────┐
▼ ▼
PC端应用 移动端应用
│ │
└────────────┬────────────┘
▼
公共业务层
│
┌────────┼────────┐
▼ ▼ ▼
API层 工具函数 公共组件
这样可以做到:
text
页面层
→ 两端独立
业务逻辑
→ 尽可能复用
接口层
→ 复用
工具函数
→ 复用
设计 Token
→ 尽可能复用
基础组件
→ 根据实际情况复用
这样比"所有东西都共用"或者"所有东西全部复制"都更容易长期维护。
十二、边界场景
1. User-Agent 不是绝对可靠
不能简单理解成:
text
User-Agent = 真实设备
因为:
- 浏览器可以修改 User-Agent
- 爬虫可以伪造 User-Agent
- 平板设备判断存在复杂情况
- 新设备和新浏览器可能出现新的标识
所以服务端设备识别应该设计成:
text
User-Agent
+
设备能力
+
业务规则
而不是依赖某几个字符串永久判断。
2. 不要为了隐藏几个元素就拆成两套页面
例如:
text
PC 多一个搜索框
移动端少一个搜索框
没必要直接拆成:
text
PC项目
移动端项目
这种场景通常:
css
@media (...)
就足够了。
3. 不要用 JavaScript 代替 CSS 做纯样式响应式
例如:
javascript
if (window.innerWidth < 768) {
element.style.display = "none";
}
如果只是控制样式,这通常不如:
css
@media (max-width: 767px) {
.element {
display: none;
}
}
因为:
text
样式问题
→ CSS解决
业务逻辑问题
→ JavaScript解决
这是比较重要的职责划分。
十三、底层原理总结
这道题实际上涉及三个层次:
text
第一层:CSS布局层
↓
视口变化
↓
媒体查询 / Flexbox / Grid / 相对单位
↓
调整页面布局
第二层:浏览器逻辑层
↓
window.matchMedia()
↓
JavaScript感知媒体条件变化
↓
执行不同业务逻辑
第三层:服务端分流层
↓
HTTP请求
↓
读取 User-Agent 等请求信息
↓
识别终端
↓
返回不同HTML
或者
返回重定向响应
因此要记住:
CSS 媒体查询解决的是"同一页面怎么适配不同屏幕";
window.matchMedia()解决的是"JavaScript 是否需要感知媒体条件";服务端分流解决的是"不同终端是否应该拿到不同的页面"。
十四、结构化逻辑思维
遇到这道题,可以按照下面的顺序回答:
text
① 先确认需求
↓
同一个 URL
↓
手机和 PC 是否需要不同页面?
② 看差异程度
↓
差异小
→ 共用页面
→ 响应式布局
→ CSS媒体查询
差异大
→ 页面结构和交互都不同
→ 服务端分流
→ 分别提供 PC / 移动端页面
③ 再看 JavaScript
↓
只是样式变化
→ CSS
需要不同 JS 逻辑
→ window.matchMedia()
④ 再看部署方式
↓
差异较大
→ 可以独立构建、部署两端应用
⑤ 最后考虑工程问题
↓
公共代码复用
缓存
SEO
首屏性能
资源体积
维护成本
十五、满分答案
同一个 URL 实现 PC 和移动端不同页面,核心看两端差异有多大。
如果两端结构和业务基本一致,只是布局、尺寸、部分内容展示不同,我会采用响应式设计,主要用 CSS 媒体查询,必要时用 window.matchMedia() 让 JavaScript 感知屏幕条件。
如果两端页面结构、交互甚至业务逻辑差异很大,我会考虑 PC 和移动端分别开发、构建和部署,然后由服务端或反向代理根据请求中的 User-Agent 等信息进行终端识别,直接返回对应页面;如果采用不同 URL,也可以通过 302 临时重定向到移动端地址。
所以这道题真正的判断标准不是页面复杂不复杂,而是 PC 和移动端的差异大小:差异小就尽量复用,差异大就适当拆分,同时把公共业务和基础能力抽出来复用。