
第二次作业在web前端这条路上是一个特别微妙的分水岭。第一次作业大家基本都在“复刻”一个静态页面标签会用、颜色会调就算过关了。可到了第二次作业页面开始变长、模块开始变多、导航要能跳、表单要有反馈很多人就是从这个阶段开始掉队的。这篇东西不是给你抄答案的而是帮你把“第二次作业”这五个字拆开来看——它到底在考核什么、为什么你的页面总是跟设计稿差那么一点、以及那些在你提交前最容易踩进去的坑。无论你是在校学生、培训机构学员还是准备冲一把web前端开发技能大赛试题的选手这套基本功都值得你从头捋一遍。1. 第二次作业到底在练什么1.1 为什么说这次作业是分水岭第一次作业的典型题目是“做一个个人介绍页”或者“仿写一个静态首页”结构上基本是一张长图从上往下摆顶多中间加个背景色。这时候你写HTML本质是在练“标签记忆”——h1是标题、p是段落、img是图片写对了就赢了。第二次作业不一样。页面通常会被拆成多个区块顶部导航、横幅区、内容卡片列表、表单、底部信息。这些区块之间要排布整齐、间距统一、滚动时层次分明。这逼着你从“认识标签”进化到“组织结构”本质是让你建立三种核心能力语义化结构能力、布局控制能力、基础交互能力。特别提一句这三项正是各类技能竞赛“web前端开发”赛项里反复出现的考核维度——它不是老师随口布置的而是在模拟真实前端开发的日常形态。1.2 拆开看看作业需求里的三类常规诉求你拿到的作业描述翻来覆去大概率逃不出这三类模块典型内容背后技术点导航区块Logo 菜单 按钮Flex布局、垂直居中、hover反馈内容区块卡片列表、图文混排、栅格盒模型、flex-wrap、gap间距表单区块输入框、选择、提交按钮表单控件、事件监听、校验反馈这三种形态几乎覆盖了日常网页中80%以上的界面模式。你如果能在第二次作业里把这三块做得干净利落后面学什么框架都会轻松得多——因为框架解决的是“怎么少写代码”但结构拆解和样式组织永远是自己的内功。1.3 技术选型这次作业别急着上框架我见过不少学生第二次作业就直接引Bootstrap或者Element UI拖几个现成组件进去效果图看起来确实高级。但我必须泼一盆冷水这个阶段用框架等于把这次作业最重要的训练目标给丢了。你把别人的CSS一套确实很快能拼出一个好看的页面可一旦要你改布局、调间距、适配某个特殊尺寸你就会发现无从下手因为你根本没看到底层是怎么工作的。第二次作业的意图是让你亲手完成页面“从零到一”的搭建自己写reset、自己算间距、自己控制换行。哪怕你最后实现的视觉效果不如框架精致这段亲手踩坑的过程才是真正长本事的地方。框架、组件库、CSS预处理器都留到下一次作业再去碰。2. 拿到设计稿先把结构拆明白2.1 从“看图”到“建骨架”的拆块思路很多同学上手就写代码结果写一半发现结构和样式拧巴在一起改一处崩三处。我的经验是动手之前先拿纸笔把设计稿拆一遍。找大块、找重复项、找层级关系——哪个区域是全页只出现一次的哪个卡片结构是重复了很多遍的重复的做成组件化结构只出现一次的先定好容器。比如你拿到一个带导航、横幅、三张卡片、一个表单的页面。结构应该是header classsite-header div classcontainer nav-wrap h1 classlogo标志/h1 nav classmain-nav ul lia href#intro介绍/a/li lia href#cards服务/a/li lia href#contact联系/a/li /ul /nav /div /header main section classbanner.../section section idcards classcards-section.../section section idcontact classcontact-section.../section /main footer classsite-footer.../footer这里用header、nav、main、section、footer这种语义化标签不只是为了“规范”而是让浏览器、屏幕阅读器和搜索引擎能直接理解页面结构。技能大赛的评分细则里语义化标签也经常是明确的加分项或扣分项。2.2 class命名别乱来后期改样式全靠它第二次作业的代码量还没大到逼你去学BEM方法论但你至少应该建立一套固定的命名习惯。一个很实用的原则是“块名-元素名--状态”比如卡片区域里外层卡片叫card卡片的标题就叫card__title被选中的状态就叫card__title--active。这样做的好处很直接当你回头改样式时看到class名就能立刻定位到页面里的哪个模块、哪个元素。反面案例我也见过不少用“.div1”、“.box2”、“.a3”这种没有语义的命名写作业的时候勉强能跑隔两天再看自己都认不出哪个是哪个。作业可以交但代码习惯一旦养歪后面项目大了根本没法维护。2.3 全局样式第一刀reset与盒模型浏览器默认样式一直是布局错乱的第一大来源ul自带padding、h1自带margin、body默认8px边距。所以在写任何布局之前都要先做一步“清零”。* { margin: 0; padding: 0; box-sizing: border-box; }这条路只要跑通你的尺寸焦虑能少掉一半。 :root里设置字体基线也是一个职业习惯:root { --primary-color: #2c6dfe; --text-main: #1a1a1a; --text-muted: #666; --gap-base: 16px; } html { font-size: 16px; } body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif; line-height: 1.6; color: var(--text-main); }把颜色、间距、字体都抽成变量你的第二次作业可能还看不出多大优势但当你写第三、第四次作业时改主题色只需要动一个地方。这种习惯越早建立越好。3. 布局实现的核心细节3.1 导航栏与垂直居中的一套黄金写法导航栏是第一道坎。用flex就是最直接的解法.nav-wrap { display: flex; align-items: center; justify-content: space-between; max-width: 1200px; margin: 0 auto; padding: 0 24px; } .main-nav ul { display: flex; gap: 24px; list-style: none; }注意几个细节align-items: center同时解决了logo和菜单的垂直居中对齐max-width 加上 margin: 0 auto 让导航内容在宽屏时居中而不是贴着屏幕左右边缘gap 替代 margin,让菜单项之间保持等距。很多人喜欢用float做横向排布——不是不能用而是float本来就是为“文字环绕图片”设计的拿来做布局你得反复清浮动、算宽度、处理高度塌陷。与其跟这些历史遗留问题缠斗不如直接用flex一条路走到底。3.2 flex-wrap与卡片间距别让内容撑破布局卡片列表是第二次作业里最容易“看起来还行、一缩放就崩”的部分。.cards-section { display: flex; flex-wrap: wrap; gap: 24px; } .card { flex: 0 1 calc((100% - 48px) / 3); min-width: 260px; }这里每个值都有讲究flex: 0 1 表示“不拉伸、可收缩”基础宽度是计算好的三分之一减去的48px是三列间两个24px的gapmin-width: 260px 保证在窄屏上卡片不会缩到没法看的程度一旦小于260pxflex-wrap就会自动把卡片换行到下一行。这比用百分比纯硬算要稳得多。实际倒腾过的同学应该体会过百分比布局面对不同屏幕宽度时经常出现卡片被挤压变形或者间距跑偏的问题。flex-wrap配合gap效果立刻正常而且代码量少。3.3 什么时候改用 Grid 更合理Flex擅长一维排列Grid擅长二维排布。第二次作业里如果遇到比较规整的栅格——比如“一行两列左边图片右边文字”这种重复结构用Grid写起来会更利落.feature-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; }这比用flex一行一行控制对齐来得直观。但建议你先别急着学花活flex练熟了再去用grid也不迟。很多炫技式的grid写法在比赛代码评审里并不加分反而容易因为对不同浏览器支持度不够白白丢分。4. 表单基础交互第二次作业加一点JS才完整4.1 为什么要用addEventListener而不是onclick第二次作业里最容易拿到“出彩”评价的是做一个看起来“活”的交互。表单校验是性价比最高的选择它既有明确输入又有明确反馈。但写法上有讲究。很多人一开始会写οnclickcheckForm()把函数挂到全局。这个写法的问题在于第一多个事件绑定会互相覆盖第二全局函数污染第三内联事件在代码审查里通常被视为不推荐。简单绕开就行const form document.querySelector(#contact-form); const username document.querySelector(#username); username.addEventListener(blur, function () { validateUsername(); });addEventListener可以给同一个元素挂多个事件监听器彼此互不干扰也方便解耦。以后你在框架里写代码这种事件处理思维也是直接通用的。4.2 一次表单校验的完整实现思路拿最常见的“用户名”输入框举例校验要覆盖三种情况不能为空、长度大于等于3、只能是字母和数字。function validateUsername() { const value username.value.trim(); const errorEl document.querySelector(#username-error); if (value ) { errorEl.textContent 用户名不能为空; return false; } if (value.length 3) { errorEl.textContent 用户名至少需要3个字符; return false; } if (!/^[a-zA-Z0-9]$/.test(value)) { errorEl.textContent 用户名只能包含字母和数字; return false; } errorEl.textContent ; return true; }这里其实可以做两个层次的反馈一个是在blur失焦时检查——用户填完离开输入框后立刻提示一个是在input输入过程中实时反馈——用户边输入边看到状态。实战时blur和input两种事件可以同时挂。HTML5原生也有required和pattern属性可以用来兜底但原生校验的弹窗样式你控制不了跨浏览器的表现也千奇百怪。自己写校验提示才能和页面设计风格统一。4.3 事件处理里的两个隐藏深坑一个坑在form里的button如果不写typebutton”会被当成提交按钮。点击后页面默认执行提交逻辑你还没看到校验结果整页就刷新了。这个坑特别隐蔽因为你在单独打开HTML调试时form没有action属性浏览器刷新页面过后一切看起来“正常”你反而找不到问题在哪。另一个坑向页面上展示用户输入内容时用innerHTML直接拼接会有明显的注入风险。// 可选方案当作纯文本插入 errorEl.textContent 输入不合法;哪怕在客户端页面里也尽量不用innerHTML去插入用户可控的内容。改成textContent内容统一按纯文本处理安全得多。这个习惯在以后的vue/react项目里同样适用——凡是需要渲染用户输入的地方先把“内容”和“HTML结构”分开这个意识越早建立越省事。5. 提交前的自测清单与经典翻车现场5.1 三个必测动作帮你提前拦下八成问题我第一次做类似作业时习惯只在自己电脑的大屏幕上反复看觉得“挺完美”结果一交上去就被打了回来。后来学乖了每次提交前固定做三件事测试动作能发现的问题把浏览器窗口缩到375px宽度是否有横向滚动条、卡片是否挤爆、导航是否乱掉浏览器页面缩放调大到150%文字是否溢出容器、按钮是否被压弯换一个浏览器打开Chrome之外至少过一遍Firefox或Edge样式兼容性、字体渲染差异尤其是第一条页面出现横向滚动条通常意味着某个容器宽度超过了视口。这时候不要在浏览器里乱猜打开开发者工具面板选中元素查看盒模型就能一眼看出到底是哪个块撑破了页面。5.2 经典BUG外边距塌陷这个坑几乎人人都会遇到但很多人遇到后只能靠“加个border试试”来瞎蒙。它的原理是垂直方向上相邻元素或者父子元素的margin会被合并取较大的那个值。比如说你在section里放了一个margin-top: 40px的h2理论上section顶部应该被推下去40px实际效果却是h2把section的margin也给带跑了整体看起来只有那40px的偏移位置还对不上。解决方案有三个/* 方案一父级加内边距 */ .parent { padding-top: 1px; } /* 方案二父级加边框 */ .parent { border-top: 1px solid transparent; } /* 方案三父级开启BFC */ .parent { overflow: hidden; }大多数情况下我会推荐优先用padding而不是margin去控制子元素与父容器之间的距离。因为padding不会产生塌陷直观且易维护。5.3 经典BUG图片底部留白和Flex子项溢出图片底部莫名其妙多出几像素空隙是另一个高频问题。原因很简单img默认是inline元素基线对齐导致底部留白。一行img { display: block; }就根治了。Flex子项溢出就稍复杂些。常见场景三个flex子项本来设置好三等分宽度结果某个卡片里文字太长直接把整个布局撑破出现横向滚动。排查思路是先看子项是否允许收缩flex-shrink默认值是1如果被设成了0那无论容器怎么变它都不会缩小溢出几乎是必然的再看是否有固定宽度属性比如给子项定了宽度400px但这个宽度在当前屏宽下根本放不下。这类问题我的固定排查套路是开浏览器开发者工具逐个选中父容器和子元素盯着盒模型的高亮区域看——绿色是padding、蓝色是content、橙色是margin。哪一层超出了父级边界立刻就能锁定。6. 关于这次作业我的一点真心话我自己在带新人时一直强调一句话第二次作业最值钱的不是结果而是你为它熬过的每一个“为什么”。结构写对远比你用高超的技巧去做一个完美像素的复刻更重要。如果你交上去的页面能在375px宽度下不乱、在150%缩放下不破、去掉样式后依然有清晰的阅读顺序那这份作业就已经能拿出手了。做完这次作业之后如果你还有精力我建议顺手做两件事第一把页面里的导航切换交互改成事件委托的方式处理感受一下事件冒泡的威力第二把表单里的校验逻辑改为一个可复用的函数传入不同的校验规则返回对应的错误信息。这两个小改动会把你从“完成作业”推向“写出通用方案”的层级。等你真正开始处理复杂项目时回看这次作业里写下的每一行代码你会感谢那个愿意改三遍的自己。