
简介一份用于前端基础练习的小米官网首页静态页面资源面向初学HTML与CSS的开发者通过复刻真实电商首页来巩固网页结构、选择器、盒模型、Flexbox/Grid布局、媒体查询、伪类与字体图标等核心技能。资源共38个文件压缩包约2.64MB以HTML页面、CSS样式表和图片素材为主另有字体文件与图标方便直接打开预览和修改。已有667人学习适合作为第一份完整的前端练手项目也适用于自学或教学案例。内容包含完整的首页HTML结构与按重置、基础、页面等模块拆分的CSS样式并配有所需banner图、商品图、图标与字体资源能帮助学习者对照真实页面理解样式分层与命名规范逐步模仿小米官网的导航、轮播区、商品陈列和底部布局。通过动手修改样式和布局可以快速掌握响应式设计与浏览器兼容性处理思路。额外附有演示页面与站点图标目录结构简洁清晰方便快速定位文件、对照源码调整提升实际编码能力。1. 小米官网首页的静态页面为什么前端新人都在复刻它你可能觉得“静态页面”不就是写完 HTML、CSS 放浏览器里能打开吗有什么好讲的但如果你真把小米官网首页截图摆在旁边一行一行去像素级还原你会发现这活儿一点都不轻松顶部的二级菜单、滚轮上的轮播图、商品卡片在鼠标悬停时的阴影变化、底部一堆链接对小屏的折叠逻辑随便哪个环节没处理好页面就会肉眼可见地“散架”。这个标题的实质是用纯前端技术复刻一个真实商业网站的首页不碰后端接口不碰数据库所有内容和交互都在浏览器里完成。它适合两类人一类是把“小米官网首页静态页面”当练习作品的前端初学者做完能拿出来给面试官看另一类是已经在写页面但总觉得自己对布局、响应式、浏览器兼容性心里没底想通过一个完整项目把这块补齐。你需要掌握的东西很明确HTML 结构设计、CSS 布局与动画、原生 JavaScript 的轮播与交互以及一套能应对不同屏幕的响应式策略。这文章我就按自己做这个项目的顺序来讲从拆解页面到写成代码最后把最容易踩的坑一个一个列给你。2. 动手前的技术准备把小米首页拆成可复现的模块2.1 静态页面到底指什么区别于动态渲染的边界做静态页面之前先得把“静态”两个字界定清楚。静态页面不是指页面不能动而是指没有服务端参与数据不来自接口页面文件直接存在于本地或静态服务器上访问时拿到的是原封不动的 HTML、CSS、JavaScript。小米官网首页实际上是一个动态渲染的页面有大量商品数据来自接口但你要做的这个“静态页面”版本是用写死的数据和图片去模拟它的外观和交互。这带来的好处是逻辑简单不需要考虑鉴权、接口超时、数据序列化坏处是如果图片资源过大、路径写错整个页面会显得很假。所以复刻时我会把图片资源放在本地 images 目录里用相对路径引用。一个常见误区是认为静态页面不需要工程化。实际上做这种还原练习时我建议还是用一个最简单的目录结构方便后期维护。下面是我常用的文件布局文件/目录作用index.html页面唯一入口包含完整结构和引用的 css/jscss/main.css所有样式按模块注释分块js/main.js轮播、菜单、返回顶部等交互逻辑images/本地图片资源按模块命名如 banner-1.jpg没有构建工具没有框架纯手写。这样你才能把每个细节吃透。如果一开始就上 Vue 或 React 组件化反而看不清布局和样式是怎么互相作用的。2.2 我用的工具与文件组织方式在动手写代码之前我会先做三件事用浏览器打开小米官网首页按 F12 打开开发者工具把页面从头到尾截一张长图把顶部栏、轮播图、商品分区、底部栏这几块单独截图存成参考图。代码方面我只用两个工具VS Code 做编辑器Chrome 做调试。没有装插件因为静态页面不需要热更新改完代码手动刷新浏览器就够了。不过如果你想让调试方便一点VS Code 里装一个 Live Server 插件也可以它的作用仅是起一个本地静态服务器让页面通过 http 协议访问从而避免 file 协议下某些浏览器限制。文件组织上我坚持一个原则每个模块一块样式注释块。比如在 main.css 里我会写/* header */这样的注释然后再写这个模块的样式。这样当你写完 1000 行 CSS 后找某个区块依然很快。创建一个基础页面骨架时我会这样写!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title小米官网首页静态复刻/title link relstylesheet hrefcss/main.css /head body !-- 页面主体 -- script srcjs/main.js/script /body /html逻辑说明meta charsetUTF-8保证中文不乱码meta nameviewport是响应式的基础因为后面要用媒体查询适配小屏如果不写这个手机浏览器会按 980px 宽度渲染你的媒体查询就失效了。样式放在 head 里优先加载脚本放在 body 末尾避免阻塞渲染。参数说明如果你想让页面在电脑端和手机端都正常widthdevice-width, initial-scale1.0是标配不要省略。后面拆轮播图时JavaScript 也是在这个 main.js 里面写。2.3 复制像素级页面从测量到写码的工作流小米官网这种大厂页面设计风格偏简洁间距和字体大小非常有规律。复刻的关键不是“看个大概”而是测量出具体数值。我一般这样操作在 Chrome 的开发者工具里选中某个元素看它的padding、margin、font-size、border-radius直接记下来。如果用截图对比也可以用截图软件的取色器获取背景色。我的工作流是先把页面从上到下切成几个大区块每个区块放进一个div再给每个div一个 class 名。然后逐块写 CSS先从顶部导航开始接着轮播图再做商品区最后是底部。比如小米的顶部导航背景是白色高度约 60px内容区宽度是 1226px左右居中。我会这样实现/* 顶部导航栏 */ .topbar { width: 100%; height: 60px; background: #fff; border-bottom: 1px solid #e0e0e0; } .topbar-inner { width: 1226px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; }逻辑说明外层topbar占满全宽让背景色横向铺开内层topbar-inner固定 1226px 宽度并水平居中让内容在宽屏下不散得太开。display: flex配合justify-content: space-between可以把左侧 logo 和右侧菜单分别推到两端。这个“内外两层”写法是小米官网这类居中布局页面的通用套路你会反复用到。参数说明1226px 是小米官网内容区的标准宽度参考自原站。如果你复刻的页面不同就用你自己测量到的数值替换即可。margin: 0 auto是让固定宽度块居中最常用的方式没有之一。3. 按模块还原小米首页布局、样式与交互的落地步骤3.1 顶部导航栏先用浮动还是 Flexbox顶部导航是访客第一眼看到的内容也是你写页面时建立的第一个布局基础。很多教程还在用float: left/right做导航但这在垂直方向上极易遇到高度塌陷。我建议直接用 Flexbox理由有两点一是代码更短二是不需要额外清除浮动。做个具体例子左边是“小米商城”Logo右边是“首页、手机、电视、智能家居”等链接。我会这样写nav classtopbar-inner a classlogo href#小米商城/a ul classnav-list lia href#首页/a/li lia href#手机/a/li lia href#电视/a/li /ul /nav.logo { font-size: 20px; font-weight: bold; color: #ff6700; text-decoration: none; } .nav-list { display: flex; list-style: none; gap: 20px; } .nav-list a { color: #333; text-decoration: none; font-size: 14px; } .nav-list a:hover { color: #ff6700; }逻辑说明display: flex作用于ul上让li水平排列gap: 20px设置每个链接之间的间距不需要再给每个li写margin-right。list-style: none去掉默认圆点。链接的悬停颜色用:hover伪类实现与小米官网的橙色品牌色一致。参数说明这里的#ff6700是小米橙用这个颜色能让还原度更高。text-decoration: none去掉下划线是导航链接必须做的否则会显得很业余。如果你未来要适配移动端这个gap在窄屏时可以改写为 10px。3.2 轮播图区域静态定位与自动播放的 JS 实现小米首页顶部下方是一个大图轮播通常每隔几秒切换一张。静态页面的轮播有两种做法纯 CSS 轮播动画和 JavaScript 控制位移。为了可维护我选择用 JavaScript 控制一个滑动容器。HTML 结构大概是div classbanner div classbanner-track div classslide stylebackground-image: url(images/banner-1.jpg);/div div classslide stylebackground-image: url(images/banner-2.jpg);/div div classslide stylebackground-image: url(images/banner-3.jpg);/div /div button classprev‹/button button classnext›/button /divCSS 核心是让banner-track横向排列并通过transform: translateX()来移动它.banner { position: relative; width: 1226px; height: 460px; margin: 0 auto; overflow: hidden; } .banner-track { display: flex; height: 100%; transition: transform 0.4s ease; transform: translateX(0); } .slide { min-width: 100%; height: 100%; background-size: cover; background-position: center; }逻辑说明.banner-track的子元素.slide都设min-width: 100%因此每个子元素宽度等于容器宽度它们会排成一行容器通过overflow: hidden只露出第一张。切换时只需要计算一个currentIndex变量然后修改translateX(-currentIndex * 100%)即可。配合transition: transform 0.4s ease图片滑动就有了顺滑过渡。JavaScript 自动播放部分const track document.querySelector(.banner-track); const slides track.querySelectorAll(.slide); const prevBtn document.querySelector(.prev); const nextBtn document.querySelector(.next); let currentIndex 0; let timer; function goTo(index) { if (index 0) index slides.length - 1; if (index slides.length) index 0; currentIndex index; track.style.transform translateX(- (currentIndex * 100) %); } function nextSlide() { goTo(currentIndex 1); } function startAutoPlay() { timer setInterval(nextSlide, 3000); } function stopAutoPlay() { clearInterval(timer); } prevBtn.addEventListener(click, () { goTo(currentIndex - 1); }); nextBtn.addEventListener(click, nextSlide); track.addEventListener(mouseenter, stopAutoPlay); track.addEventListener(mouseleave, startAutoPlay); startAutoPlay();逻辑说明goTo函数是核心它根据索引计算偏移量。负数索引会从末尾接上超出索引会回到第一张形成一个首尾循环。setInterval每 3 秒调用nextSlide实现自动播放。鼠标悬停时停止移开后重启这是电商首页常见的交互细节。参数说明3000是自动播放间隔单位毫秒。你想调整轮播速度就改它。0.4s的 transition 是时长感觉比较舒服的值太短会跳太长会拖沓。如果你发现按钮点击后轮播跳了两次多半是两个事件都触发了注意看监听器是否重复绑定。3.3 商品卡片网格用 Grid 做出响应式排列小米首页的商品区是一排排卡片每张卡片包含图片、标题、价格。这类整齐网格很适合用 CSS Grid 来做因为你能直接控制列数和间距。我习惯把商品区写成div classproduct-grid a href# classproduct-card img srcimages/xiaomi-14.jpg alt小米手机 h4小米手机/h4 p¥3999/p /a !-- 更多卡片 -- /div对应样式.product-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; width: 1226px; margin: 30px auto; } .product-card { background: #fff; padding: 20px; text-align: center; border-radius: 8px; transition: box-shadow 0.2s; } .product-card:hover { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); } .product-card img { max-width: 100%; height: auto; }逻辑说明grid-template-columns: repeat(4, 1fr)表示每行 4 列每列等分可用宽度。gap: 15px同时控制列距和行距。这样你不需要操心计算百分比宽度Grid 会自动完成分配。鼠标悬停阴影是本页还原的重点细节配合transition让阴影渐变出现而不是突然跳出来。参数说明1fr是 Grid 单位代表剩余空间中的一份。改成repeat(3, 1fr)就变成 3 列适合放到平板宽度。商品卡片的圆角我用 8px与原站接近你也可以用 6px 或 10px 做微调。这里的图片没有固定高度所以不会发生裁切但如果你的卡片图片尺寸不统一布局就会参差后面避坑会提到。3.4 底部与侧边栏绝对定位和 z-index 的经典用法小米首页底部有一个区域左边是商品分类的侧边菜单右边是内容主体。侧边菜单在鼠标悬停时会弹出二级面板这里需要用到绝对定位和z-index。侧边菜单的结构div classcategory-area ul classcategory-menu li a href#手机/a div classsubmenu a href#小米13/a a href#小米14/a /div /li /ul div classcategory-content主体内容/div /divCSS 部分.category-area { position: relative; } .submenu { position: absolute; left: 100%; top: 0; width: 300px; background: #fff; border: 1px solid #e0e0e0; display: none; z-index: 100; } .category-menu li:hover .submenu { display: block; }逻辑说明position: absolute让二级菜单脱离文档流以最近的有position: relative的祖先为参照物。这里left: 100%把它放到菜单右侧。display: none默认隐藏鼠标悬停时改为block。z-index: 100保证它在内容之上不会被后面的元素盖住。参数说明width: 300px是二级菜单宽度你可以按内容多少调整。如果你发现二级菜单被父元素裁掉检查父元素的overflow是否设成了hidden。z-index不是越大越好只要比同级其他元素大就行一般用 100 足够了。底部导航栏的样式与顶部类似但更简单一排链接用小字号和灰色。这里略过后面避坑重点讲一下为什么底部链接在小屏上容易挤成一团。4. 让静态页面在不同屏幕下不翻车响应式与兼容性4.1 断点设计参考小米常见的屏幕宽度静态页面做完后最怕的一件事是把浏览器窗口拖小一看整个布局挤得没法看。响应式的核心是媒体查询。根据小米官网常见布局我会设置三个断点1200px 以上为桌面768px 到 1200px 为平板768px 以下为手机。注意内容区的宽度也要跟着调整。/* 默认样式桌面端 */ .product-grid { grid-template-columns: repeat(4, 1fr); } /* 平板 */ media (max-width: 1200px) { .product-grid { grid-template-columns: repeat(3, 1fr); } .topbar-inner, .banner, .product-grid { width: 100%; padding: 0 15px; } } /* 手机 */ media (max-width: 768px) { .product-grid { grid-template-columns: repeat(2, 1fr); } .topbar-nav-list { display: none; /* 隐藏菜单用汉堡按钮代替 */ } }逻辑说明由于默认样式写的是桌面端手机端的媒体查询只需覆盖需要变化的属性。这里把.topbar-inner这些固定 1226px 的容器改成width: 100%并加padding防止内容贴屏幕边。商品卡片的列数从 4 降到 3 再降到 2是大多数电商页面在小屏上的处理方式。参数说明max-width: 1200px表示当视口宽度小于等于 1200px 时应用其中样式。建议不要写死太多类似 999px 这样的断点否则每次微调都会很痛苦。如果你做的是电商首页记住手机端 2 列卡片通常比 1 列更符合用户密度预期前提是图片足够清晰。4.2 图片与字体rem/vw 和媒体查询怎么配合静态页面里图片是最容易让页面变重的元凶。小米官网的轮播图win size在 1226x460 像素左右如果直接用这张大图手机端加载会很慢。常见做法是给img设置max-width: 100%让图片在容器变窄时按比例缩放。我一般还会准备两套图片用srcset让浏览器自己选img srcimages/goods-300.jpg srcsetimages/goods-600.jpg 600w, images/goods-300.jpg 300w sizes(max-width: 768px) 300px, 600px alt商品图片逻辑说明srcset提供不同宽度的图片资源sizes告诉浏览器在不同屏幕下图片的渲染宽度。浏览器会根据视口宽度和sizes计算并选择加载哪张图。这样可以避免手机端下载桌面大图减少流量和加载时间。字体方面我建议根元素用px固定配合媒体查询调整根字号或者直接写rem。一个常用做法是html { font-size: 16px; } media (max-width: 768px) { html { font-size: 14px; } } .title { font-size: 1.5rem; /* 等于24px */ }逻辑说明rem是相对根元素的单位。当手机端根font-size变为 14px 时所有用rem定义的字号都会等比缩小比逐条改px省事。1.5rem在桌面是 24px在手机是 21px实现了整体缩放。参数说明srcset里的600w是图片真实宽度不是物理像素。如果你不确定图片尺寸可以不写这个属性直接依靠max-width: 100%但加载性能会差一些。4.3 浏览器兼容性必须测的三个场景复刻静态页面时我会至少在三种环境测试最新 Chrome、Safari 手机端、微信内置浏览器。最常见的坑是 Flexbox 的gap在旧版 Safari 上不生效以及 CSS Grid 的1fr在旧浏览器上的兼容问题。你的静态页面如果只是练习不必追求所有远古浏览器都完美但至少要保证不出现布局错乱。一个保险做法是关键模块不用 Grid而是用 Flex 布局加justify-content: space-between或者用百分比宽度。比如商品卡片可以写成.product-card { width: calc(25% - 15px); margin-bottom: 15px; }逻辑说明calc(25% - 15px)减去gap的实际像素模拟出四列效果。这种做法兼容性更广缺点是计算麻烦。如果你使用 Grid我建议在写完代码后到 caniuse.com 查一下grid-template-columns在你的目标浏览器版本的支持情况。一般说2020 年后发布的浏览器都已支持 Grid所以不必过度担心。5. 静态页面避坑指南我复刻小米首页时踩过的 7 个坑5.1 图片路径失效相对路径与斜杠的坑现象浏览器打开 index.html图片区域是一片空白F12 控制台提示 404。原因我最初把图片路径写成了/images/banner-1.jpg这个以斜杠开头的路径是“服务器根目录”下的绝对路径。本地用 file 协议打开时它会被解析成file:///images/banner-1.jpg这当然找不到文件。哪怕用 Live Server根目录也可能不是项目的images目录。解决统一使用相对路径。在index.html里引用图片时写images/banner-1.jpg相对于 index.html 所在目录。在 CSS 里引用背景图片时要相对于 CSS 文件所在目录比如../images/banner-1.jpg。这个细节新手特别容易忽略。5.2 轮播图定时器叠加鼠标移入移出导致切换加速现象轮播图一开始正常鼠标多移入移出几次后图片切换速度明显变快甚至几秒内连续跳多张。原因startAutoPlay每次被调用时都会创建一个新的setInterval但旧的timer没有被清理。鼠标每次移出都会叠加一个定时器所有定时器同时运行轮播自然就加速了。解决在startAutoPlay开头先clearInterval已有的定时器再创建新的function startAutoPlay() { clearInterval(timer); // 防止重复叠加 timer setInterval(nextSlide, 3000); }这样每次重新启动都先取消旧的保证同一时刻只有一个定时器在跑。代码逻辑看起来是小事但实属轮播组件最容易踩的坑。5.3 高度塌陷与 margin 折叠浮动布局的连锁反应现象我最初把商品卡片用float: left实现结果卡片容器高度变 0底部背景色不见了后面元素直接顶上来。原因浮动元素脱离文档流父容器不再计算它们的高度这就是“高度塌陷”。同时如果相邻元素有上下 margin它们会合并成较大值也就是“margin 折叠”这个也容易导致间距不对。解决不要用浮动布局做这种现代页面。使用 Flexbox 或 Grid 就没有塌陷问题。如果你必须用浮动比如兼容老代码可以在父容器上加overflow: hidden或者display: flow-root来清除浮动。5.4 商品卡片图片尺寸不一致导致网格错位现象四张卡片三张图片高度 300px一张高度 350px整行高度被撑高后面卡片排列参差不齐。原因浏览器默认按图片原始高度渲染Grid 行高由该行最高的元素决定。不同产品图片比例不一致就会出现这个问题。解决给商品卡片的图片设置一个固定的高度或宽高比.product-card img { width: 100%; height: 200px; object-fit: cover; }object-fit: cover会让图片按比例裁剪铺满固定区域不会变形。200px可以换成你需要的任何值但注意不同商品图片内容中心可能不同裁剪后可能会切掉主体需要微调。5.5 底部链接在小屏上挤成一团现象手机端打开页面底部一排链接全部堆在一起文字重叠。原因底部链接通常是一个长条容器里面用多个span或a排成一行但没有做换行或响应式处理。窄屏时容器宽度不够元素依然强制在一行内排列就重叠了。解决在小屏媒体查询里给底部链接设置为可换行的 Flex 布局media (max-width: 768px) { .footer-links { flex-wrap: wrap; justify-content: space-between; } .footer-links a { width: 50%; margin-bottom: 8px; } }flex-wrap: wrap允许子元素换行再配合固定宽度让链接变成两列排列。这个坑说明一个道理响应式不只是改主容器任何长条元素都可能在小屏上出问题。6. 进阶把静态首页变成可维护的模板用 CSS 变量统一主题当你把小米官网首页的静态版本复刻完下一步不是急着做其他页面而是回头重构一遍样式让代码更容易维护。我最推荐做的一件事是把颜色、圆角、间距这些重复出现的值收拢成 CSS 变量。比如:root { --primary-color: #ff6700; --text-main: #333; --text-light: #999; --gap-sm: 8px; --gap-md: 15px; --gap-lg: 30px; --radius-md: 8px; --shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.1); }然后在你写过的所有样式里替换成var(--primary-color)。这样做的好处是如果将来想换成其他品牌色只需要改:root这一个地方。所有按钮、链接、菜单悬停色都会同步变化。我复盘自己写第一版时的代码由于把#ff6700散落写了十几个地方后来调整起来只能全局替换这就是“后悔药”没吃对。另一个值得做的改进是把 HTML 中重复的块抽成组件模板。静态页面虽然不能像 Vue 那样做单文件组件但你可以把轮播图、商品卡片、底部导航分别写成一个独立的.html片段文件然后用简单的脚本拼接。如果你打算把这个项目继续发展为模板可以尝试把商品数据放到一个data.js文件里通过 JavaScript 循环生成卡片const products [ { name: 小米手机, price: 3999 }, { name: 小米电视, price: 1999 } ]; const grid document.querySelector(.product-grid); grid.innerHTML products.map(item a href# classproduct-card img srcimages/${item.img} alt${item.name} h4${item.name}/h4 p¥${item.price}/p /a ).join();逻辑说明用数组加模板字符串让静态页面也具备一点“数据驱动”的能力。将来增加商品条目只需要改products不用再复制 HTML。这样做的边界是这仍然不算前后端分离只是前端渲染。最后一个建议是把整个项目放到本地静态服务器上跑一遍然后开着 Chrome 的 Network 面板看加载时间。如果某张图片超过 200KB就换用压缩版本如果 CSS 文件超过 100KB看看是不是有大量重复代码。静态页面虽然简单但对加载性能的约束和动态页面是一样的用户不会因为它是静态页面就多等你一秒。我自己的习惯是每次写完一个静态页面都强制自己做一遍删除代码重构把用不到的 class 删掉把重复的 CSS 合并。这样一来这个复刻项目才不只是“能打开”而是变成一份可以继续扩展的模板。如果你能做到这一步那这个“小米官网首页静态页面”就算真正消化了。希望帮到你。本文还有配套的精品资源点击获取