面试题:同一个 URL,手机打开是移动端页面,电脑打开是 PC 页面,怎么实现?

一、面试题:同一个 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 和移动端的差异大小:差异小就尽量复用,差异大就适当拆分,同时把公共业务和基础能力抽出来复用。

相关推荐
互联网散修6 个月前
鸿蒙应用开发UI基础第三十四节:媒体查询核心解析 —— 响应式布局与工具类封装
ui·harmonyos·媒体查询
暖木生晖1 年前
引入资源即针对于不同的屏幕尺寸,调用不同的css文件
前端·css·媒体查询
安冬的码畜日常2 年前
【CSS in Depth 2 精译_048】7.2 CSS 响应式设计中的媒体查询原则(中):页面断点(breakpoint)样式的添加
前端·css·css3·html5·媒体查询·css响应式设计
安冬的码畜日常2 年前
【CSS in Depth 2 精译_049】7.2 CSS 响应式设计中的媒体查询原则(下):响应式列的添加
前端·css·css3·html5·媒体查询·css响应式设计
儒雅的烤地瓜2 年前
CSS | 面试题:你知道几种移动端适配方案?
css·html·css3·vw·媒体查询·移动端适配·vh
一条晒干的咸魚2 年前
响应式CSS 媒体查询——WEB开发系列39
前端·css·html·css3·响应式设计·媒体查询
一只小阿乐2 年前
前端 css3 媒体查询实现 响应式布局
前端·css·html·css3·媒体·媒体查询·响应式布局
凡夫贩夫2 年前
CSS的媒体查询:响应式布局的利器
前端·javascript·css·css3·媒体查询·响应式布局
程序员大侠2 年前
如何判断 是否 需要 CSS 中的媒体查询
前端·css·媒体查询