
第一次作业往往是在“会用”第二次作业才开始真正“做事”。这是我在前端训练营里最深刻的体会第一次作业是写一个带标题、段落、图片的自我介绍页面代码一贴、浏览器一开能显示就算过关第二次作业直接甩过来一张设计稿要求还原到像素级那一刻我才意识到作业真正难的不是代码而是“从图变成页面”的整套思维。这篇复盘不是课程总结而是我自己从拿到设计稿到最终提交的全过程记录。里面有布局方案怎么选、Flex和Grid的取舍、字号间距圆角这些细节怎么抠、浏览器差异怎么处理还有提交前必须做的一套自检。如果你也在写前端相关的第二次作业或者第一次尝试照着设计稿还原页面这篇的内容可以直接拿来对照参考。1. 第二次作业为什么会这么“卡壳”先说结论卡壳不是因为你CSS学得差而是因为你还在用“写作文”的思路做“拼图”的活儿。第一次作业是文本导向的想到什么写什么第二次作业是视觉导向的看到的每个元素背后都有一整套尺寸、位置、层级关系。两者的本质完全不同。我当时拿到设计稿的第一反应就是打开编辑器准备直接写HTML。结果写了三行就被自己问住了这个头部的Logo和导航栏应该用什么结构是float还是Flex左边那个卡片和右边那个列表要并排宽度怎么分间距是24px还是32px背景色到底是哪种灰这些第一次作业根本不会想到的问题全在这份稿子上堆着。后来我把这个“卡壳”拆成了三个原因也是大多数人的通病第一没有先做全局观察直接进入局部。设计稿是一个整体先看它有几大区块再看区块里有哪些组件最后才到每个组件的样式。我没有做这一步等于拿着一副拼图却从中间某块开始拼自然找不到边。第二缺少“尺寸提取”的习惯。设计稿上每一个元素的宽高、间距、字号、圆角、颜色都是信息。我一开始只看大概样式忽视了这些具体数字导致页面搭出来比例不对越调越歪。第三把“还原设计稿”当成“抄代码”。设计稿只是图和代码之间没有一对一的关系。同一个视觉效果HTML可以有好几种写法CSS可以有好几种实现方式。我需要做的是选择最合理、最稳定的一种而不是照着图形硬拆。弄清了这三个原因我就把作业重新定义为“一次有翻译过程的工程实践”先把设计稿翻译成信息结构再把信息结构翻译成HTML再把HTML翻译成布局和样式。每一步之间有清晰的检查动作而不是一口气写完再看效果。下面我就按这个顺序把这个过程完整展开。2. 动手之前先把设计稿拆明白2.1 别再一上来就写代码先做“视觉拆解”拿到设计稿后我先花了一个晚上什么都没写只做一件事把设计稿当作一个陌生产品来“阅读”。我建议你用纸笔或者在线白板来做这一层拆解因为它的目的不是产出代码而是形成一张结构地图。拆解的顺序是从大到小一共三层。第一层是宏观区块把设计稿从上到下划分成若干个横向区域。比如我这版作业是“产品营销单页”我分成了顶部导航区、首屏Hero区、特性介绍区、引文区域、页脚区一共六块。这个分层要和设计稿的主视觉对齐哪里颜色变了、哪里间距明显变大通常就是区块边界。第二层是组件识别在每个区块内部找出可复用的元素。比如特性介绍区里面有四个卡片每个卡片有图标、标题、一段说明文字Hero区有一个主标题、一句副文案、两个按钮、一张产品图。组件化思考的价值在于写CSS的时候你可以从一个卡片推全四个卡片的样式只写一次结构上也不会有重复。第三层是细节标注把每个组件的颜色、字号、间距、圆角、阴影都记录下来。这个阶段我不追求精确到每一个像素但必须把有代表性的数字记下比如主色是什么、正文多大、卡片之间间距多少。设计稿如果是图片格式可以用像素测量工具如果是Figma或者即时设计这类在线工具直接看右侧的样式面板就行。做完这三层拆解我对页面的理解已经从“看起来挺好”变成了“这里导航高度64px那个Hero背景是品牌色号数18px卡片间距32px”。这种颗粒度不是我记忆力好而是拆解得足够细。后面写代码的时候我基本不用再回头反复看稿效率提升得非常明显。2.2 标签选型语义化不只是理论能减少返工拆完设计稿接下来不是写样式而是先定HTML结构。我第一次作业写结构时很随意一个div套到底反正能显示。第二次作业这么做立刻吃到了苦头当你想做响应式、想控制页面权重、想统一处理篇章间距时没有语义标签会让你陷入到处调整的泥潭。我定结构的原则是每个区块先用对应的语义化标签搭建骨架然后在里面再根据具体需要放容器。以我拿到的页面为例顶部导航用的是header里面是nav包裹的导航链接列表而不是一长串div。Logo我用a做因为它点击后就是回首页的链接天然携带可跳转语义。首屏Hero区我用section作为区块容器主标题用h1副文案用p按钮用a或者button。这里有个容易出错的小点页面里只能有一个h1如果设计稿里Logo也带名字不要把它也塞进h1普通文本或span就够了。特性介绍区我用section包裹每张卡片用article卡片标题用h2或h3。这里需要注意层级不要跳页面有h1区块级标题用h2卡片标题用h3才形成合理的文档轮廓。页脚用footer里面涉及版权信息时用small比span语义上更明确。语义化听着像概念课但实际操作收益是实打实的结构清晰后CSS的“父子选择器”写起来很自然header nav ul li a这种嵌套结构直接对应视觉层级不同区块的默认间距也有迹可循不用每个都手动加margin后面的响应式布局媒体查询只需要针对少数有语义的容器做断点切换而不是对着十几个同名div无从下手。3. 核心实操从布局骨架到像素级还原3.1 整体布局的三层结构结构定了接下来是布局。这一步是第二次作业的重头戏也是和第一次作业拉开差距的地方。第一次作业我基本靠浏览器默认的块级流从上往下排第二次作业几乎每个区块都涉及横向排列、居中对齐、元素重叠再靠默认流已经做不出来了。我把布局拆成三层来搭。第一层是页面骨架。给每个区块设置宽度、内边距、独立背景色。这一步的目标是把“页面框架”先立起来哪怕内部内容都是空的也能看出每个区块占多大面积、间距对不对。我习惯用一个临时背景色或者outline来标记区块范围这样比肉眼看样式更直观。第二层是模块内部的主轴方向。每个区块先想清楚一个核心问题里面的元素是横着排还是竖着排这个决定直接对应display: flex和flex-direction。比如导航栏是横排我用display: flex; justify-content: space-between让Logo靠左、菜单靠右Hero区主标题、副文案、按钮是竖排我用display: flex; flex-direction: column四个特性卡片需要一行四个我先把外层设成横排Flex再把每个卡片内部设为竖排Flex。第三层是细节定位。这里处理间距、对齐、嵌套容器的关系。我在这一层花的时间最多因为很多样式问题都出在“父容器和子元素的搭配”上。比如卡片里的图标、标题、文字要垂直居中又要有整齐的间距我给卡片做flex再给文字区域单独留一个容器避免间距污染。三层布局搭建还有个实用的好处排错的时候可以分层排查。页面出了问题我先看第一层的区块边界是否正常如果正常就往下看第二层方向是否对再往下才深入到间距、对齐这些细节。这个排查路径让我省掉了大量“乱猜乱试”的时间。3.2 Flex和Grid我用这套标准做选择提到横向排列很多人都知道能用Flex也能用Grid但具体到作业里到底用哪个我一开始也摇摆。后来我总结了一套很简单的选择标准分享出来供你参考。如果某个区域内元素需要在一个方向上依次排列并且内容数量不固定、换行规则复杂用Flex更灵活。典型场景包括导航栏的菜单项排列、按钮组、卡片内部的图标加文字、标签列表。Flex对“一条线上的对齐和间距”处理非常顺手主轴和交叉轴两个维度的控制能满足大多数需求。如果某个区域是“二维的、表格状的”行列关系明确元素在行和列两个维度上都要对齐就用Grid。典型场景包括四张卡片组成的特性区域、图片画廊、页面整体的主次栏结构。Grid的写法更贴近“画格子再填内容”的直觉grid-template-columns: repeat(4, 1fr)一行就能把四张卡片均分省去手动计算宽度的麻烦。我这版作业里导航和Hero内部用的是Flex四个特性卡片的外层用了Grid。实际测试下来Grid在外层控制“四列等宽”非常干脆Flex在内层控制“每个卡片内部元素垂直排列”也很顺手。两者不是互斥的完全可以嵌套使用。这里还要提一个隐蔽的坑Flex和Grid的默认行为和第一次作业习惯的块状布局很不一样。display: flex会让子元素自动变成水平排列并且不再自动占满整行display: grid的子元素会默认填进网格但如果某一格子的内容过长整个网格的行高可能被撑开。这些默认行为要靠实际调试去记住不要凭直觉判断。建议每个布局都先在DevTools里启用网格或Flex的辅助线来检查我后面会专门说这个操作。3.3 字号、间距、圆角和颜色这些细节决定还原度布局架子搭好以后页面其实还是“半成品”。真正的还原度藏在那些看上去不起眼的数字里字体大小、行高、字间距、外边距、内边距、圆角半径、阴影颜色和透明度。我在设置字号的时候遇到过一个问题设计稿里写的是“18px”但实际在浏览器中显示感觉偏小或偏大。后来我发现问题不在字号本身而在行高。CSS里line-height的默认值一般是1.2左右而设计稿中的行高往往要1.5到1.8尤其是正文文本。如果不单独设置行高文字再稠密一点整体视觉就会非常局促。正确做法是从设计稿拿到具体行高数值或按经验设置为line-height: 1.6到1.75之间的稳定值再微调。间距方面我用来统一所有视觉关系的单位是rem而不是px。这里也踩过一个坑我在根元素html上设置了font-size: 14px但设计稿是以16px为基准设计的导致很多间距换算出来是小数。修改思路是保持根字体为16px间距用0.25rem、0.5rem、1rem这样的回路16px的因数可以保证大多数间距都是整数。圆角和阴影看着简单却是设计稿还原度最容易露馅的地方。设计稿写的是8px就写8px别用50%做小卡片圆角那是圆形头像的做法。阴影也一样设计稿给出0px 4px 12px rgba(0,0,0,0.08)那就原样抄写不要自己发明。越是细节越不要自由发挥。颜色方面建议从设计稿取色后把品牌色、文字主色、辅助色统一放到:root下的CSS变量里比如--primary: #2563eb、--text-main: #1f2937。这样做有两个直接好处颜色不用反复手写记忆不会出现深灰和黑灰混用的情况后续如果设计改动主色只需要改一个变量。这个习惯对第二次作业来说可能稍显正式但从这次开始养成后面做项目会越来越省力。4. 提交前必须做的自检4.1 我用DevTools做了三次缩放检查作业写完不代表可以提交至少我第一版写完后一刷新就看到了很多预期之外的视觉效果。后来我总结了一套提交前的自检流程第一步就是在浏览器里做三次缩放检查。第一次检查是100%缩放下的桌面布局。我会按下F12打开DevTools然后把视口宽度拖到设计稿对应的宽度通常是1440或1280检查每个区块是否和设计稿一致。这一步主要看横排是否整齐、间距是否均匀、文字有没有溢出。第二次检查是响应式布局。DevTools里有一排设备模拟按钮我挨个切到常见尺寸1420、1024、768、375。重点观察导航栏在窄屏下是否换行、Hero的按钮是否挤压、Grid的卡片从四列变成一列时是否还保持可读性。如果你和当时的我一样作业要求里没提响应式这一项也不能省因为老师或审核方很可能会拖动视口看效果纯桌面版页面在小屏下会显得很不专业。第三次检查是缩放比例。我用浏览器自带的缩放功能Ctrl加加减号从80%到125%逐级切换观察页面有没有在某个缩放等级下出现横向滚动条、文字重叠、背景断裂。这个操作能发现不小心写死的宽度比如某个区块设了width: 1200px在视口小于这个数值时就会溢出。这一套检查下来我发现的问题比我自己滚动页面时发现的多得多。最典型的是某个卡片内部文字在某个宽度下溢出因为容器高度没设足够另一个是Flex子元素的最小尺寸限制导致导航在窄屏下叠行。这些问题不放大去看很容易被忽略。DevTools不只是看代码的工具更是“提前发现老板会发现的低级问题”的工具。4.2 命名规范与代码整洁度自检不仅仅是看视觉效果。我提交前会专门打开源码检查三件事命名是否语义化、类名是否重复、有没有遗留的调试代码。命名这件事我第一次作业完全没有感觉第二次作业因为在结构上做了语义化拆解CSS类名如果乱写马上就会出现“类名不知道代表什么”的困境。我强制自己用BEM的简化版规则.block__element--modifier这种三段式即使只用它的简化形式也能让类名一眼看清层级和变体。比如.card__title、.card__title--dark这样不仅自己看得懂别人接手代码也不需要猜。检查类名重复也很重要。我当时写过.content这个类名在三个区块里都用了结果后面的样式互相覆盖排查了半小时才发现是命名的锅。这个教训很直接把类名理解成“每件衣服的吊牌”同类衣服可以有不同加工方式但吊牌不能混用。后来我把所有类名做了一次整理每个类名的含义对应唯一一个组件从布局容器到组件内部的修饰符各司其职。调试代码这一项我在写第二次作业时不止一次把outline: 1px solid red这类辅助线漏在CSS里。临提交前如果漏删页面会出现奇怪的红色边框直接影响第一印象。我的检查方法很简单全局搜索outline、background: red、border: 1px dashed这类关键词有就删掉。这个动作三秒钟就能完成但能避免一个看起来很傻的低级错误。5. 一路踩过的坑附排查思路5.1 margin塌陷一段间距凭空消失当时我在Hero区下方给一个区块设置margin-top: 40px刷新后间距却完全没生效。刚开始我还以为是选择器写错了查来查去才想到是margin塌陷。这个问题在第一次作业里基本不会遇到因为都是简单的块状排列没有嵌套。到了第二次作业父容器和子容器组合变多变复杂问题就来了子元素的margin-top被父容器吞掉了表现为父容器下方没有出现期望的间距。我更愿意把这个现象理解成“margin的边界碰到一起了”而CSS的规则是相邻的外边距会合并成最大值而不是相加。排查方法是在DevTools的Elements面板中选中那个元素看Computed样式里的margin数值再对照父元素的实际尺寸。解决方式有三种我推荐给初学者的组合是给父容器设置padding代替margin或者给父容器设置overflow: hidden来阻断合并。最稳妥的还是在两个区块之间用父容器的margin来控制间距让兄弟关系的margin不会发生塌陷。不要靠拉伸某个元素的高度来补间距那样后面加内容又会乱。5.2 高度塌陷与overflow另一个频繁出现的问题是父容器的高度塌陷。我在设计卡片时卡片容器里放了图标和文字但容器本身没有设置高度结果所有子元素都浮在上方卡片背景只显示了一条细线。这个问题的根源是浮动或子元素脱离了常规布局流。虽然我没有用float但在某个区块里因为历史代码残留了一个clearfix的用法导致父容器的高度没能包裹住子元素。排查时我先给父容器设了一个临时高度确认问题确实在包裹逻辑上然后检查子元素有没有用float或position: absolute。解决高度塌陷的通用方法有几种设置display: flow-root这样父容器会形成一个独立的块格式化上下文自动包含子元素或者给父容器用overflow: hidden这也是常见做法但要注意可能裁剪掉某个真正需要溢出的部分。我当时选择的方法是给父容器追加一个display: flex因为布局本身就要横排这样父容器会以Flex容器重新计算包裹范围高度塌陷自然解决。这里也提醒一句代码问题的解法不是固定的要根据实际情况选择副作用最小的那条路。5.3 图片和文字的对齐问题第二次作业里有一处是Logo旁边有一行小字我直接用Flex排列结果文字总比图片高出一截怎么调都不对。这里涉及一个在前端日常开发里特别常见的问题内联元素的默认基线对齐与混合高度元素的对齐规则冲突。我当时用align-items: center进行垂直居中但图片本身有一个默认的边框和垂直对齐属性文字的行高也会影响最终位置。后来我找到了稳定的组合给图片设置display: block防止它参加基线对齐给图片设置一个明确的width和height不要依赖自然尺寸最后给容器用Flex加上align-items: center同时给文字设置一个固定行高。换了组合之后文字和图片的对齐效果一下子稳定了。这个事情的真实启发是页面还原过程中很多“差一点点”的问题不是代码逻辑错误而是CSS各种默认行为叠加后的结果。不要反复猜直接在DevTools里用逐个computed值排除特别是对图片这类元素显式设置尺寸和display: block能杜绝一半以上的意外对齐。6. 写在最后第二次作业教会我的事第二次作业完成的那一刻我并没有多兴奋反而有一种“终于知道自己哪里不行”的清醒。第一次作业能跑通是运气第二次作业能还原是方法。这两者的差距不在智商而在有没有把“看图、拆解、布局、自检”这个流程认真走完。如果只能总结一条经验我会说不要急着写代码。设计稿上的内容越多先拆解的时间就应该越充裕。我当时花了接近三小时做视觉拆解和标签规划后面真正写代码加调样式只用了不到六小时如果反过来我估计要在各种返工里耗上两天。另外一条对以后的自己有用的提醒是保留一份作业的过程记录。我当时把拆解图、布局方案、踩坑笔记都放在一个文档里提交作业时顺便附上了这份说明没想到得到的反馈比单纯交代码要详细很多。对看作业的人来说他看到的不是一份成品而是你在成品背后所有的选择和取舍这本身就是一份更大的作业。希望这篇复盘对正在写“第二次作业”的你有参考价值。如果你也在做前端还原类的练习可以在动手前先对照我这份流程走一遍能省掉不少原本要踩的坑。这次作业之后我对前端这个领域最大的感受是它会逼着你把一个模糊的“感觉好像会了”变成一个个精确到像素的“确定能做到”。这份细节控的耐心正好是这个阶段最该练出来的东西。