第一次在课程平台看到“web前端第一次作业”这个标题时,我心里其实没太当回事。一个页面而已,不就是写几个标签、调一调颜色嘛。结果点开需求文档,整整三页说明,从页面结构到视觉风格再到代码规范,零零散散列了十几条。那会儿我才反应过来,这根本不是什么“练手”,而是一次特别完整的入门摸底。
这篇文章我就把完成这次作业的全过程完整地写下来,包括怎么拆需求、怎么搭目录、怎么写页面、怎么把自己从各种坑里捞出来,以及交作业前最后两小时我到底做了什么。如果你也刚开始学前端,或者正对着自己的第一个页面作业发愁,这份记录可以直接拿来当参考。我不能保证你照做就能拿高分,但至少能让你少走几个我已经走过的弯路。
1. 拿到作业要求的一脸懵:这个作业到底想让我练什么
1.1 把需求文档拆成能干的活儿
先说作业本身。题目很朴实:制作一个“个人介绍”相关的静态网页,要求包含导航栏、个人信息区、技能展示区、图片展示区、页脚这五块内容,样式上要有统一的色彩主题、合理的间距与圆角风格,并且要兼容主流浏览器宽度。读写起来感觉就是几句话,但真正动手就会发现,这其实是一个五脏俱全的个人主页项目。
我做的第一件事不是打开编辑器,而是找了一张纸,把需求逐条抄下来,然后画勾。抄的过程比想象中有用,因为很多条目在“看起来清楚”的表象下面是含糊的。比如“兼容主流浏览器宽度”这条,到底是让页面在不同宽度下自适应,还是只要在普通笔记本屏幕上不变形就行?我一开始的理解是前者,差点去写媒体查询和断点。后来冷静一想,第一次作业大概率考的是基础布局能力,把固定宽度的页面做得精致完整,比一上来就搞响应式更稳重。
我当时给自己列了一个这样的需求拆解表,写完心里就踏实了:
| 需求分类 | 作业原文要求 | 我的落地理解 |
|---|---|---|
| 页面结构 | 有导航栏、个人信息区、技能区、图片区、页脚 | 用语义化标签切分,而不是堆div |
| 视觉风格 | 统一色彩主题、合理间距、圆角 | 先定主色和辅色,再统一使用 |
| 交互行为 | 可选加分项 | 加一个切换主题颜色的按钮,用纯JavaScript实现 |
| 工程规范 | 代码可读、排版统一 | 命名字段、统一缩进、写好注释 |
这张表后来帮了大忙。因为需求一旦拆开,它就不再是“做一张网页”这种模糊的大任务,而是一个一个具体的小问题:导航栏用什么布局、头像怎么裁圆、卡片要不要阴影。每个小问题都很容易单独解决,难点只在于你不愿意拆。
1.2 需求背后真正想考察的三件事
作业做完之后我才回过味来,老师给这么一道题,并不是要一个好看的页面,而是想通过页面看到你三方面的基本盘。
第一是语义化能力。如果整页都用div堆起来,一眼就能看出你是真的理解HTML结构,还是在机械搬砖。header、nav、main、section、footer这些标签有没有用对,直接反映你是否具备“页面是有信息结构的”这个意识。我一开始就写对了这几个标签,后面调整结构时省了非常多的力气。
第二是CSS的组织意识。样式是写在行内、写在style标签里,还是拆成独立文件?类名是乱起的还是成体系的?有没有用变量统一颜色?这些比“会不会写炫酷动画”重要得多。第一次作业根本不要求你展示多么花哨的技巧,它要求的是你的样式代码能让别人读得懂,能让两周之后的你自己读得懂。
第三是细心程度。图片路径错没错、标签闭没闭合、文件叫index.html还是index.HTML、CSS文件有没有正确引入,这些都是基本功。这些问题不解决,后面不管学vue还是react,都会被同一个石头反复绊倒。
2. 动手前的准备:目录、配色和一张先画好的线框图
2.1 只用了最简单的工具组合
我的开发环境非常朴素:一台电脑、一个代码编辑器、一个浏览器。没有安装任何框架,没有跑npm,连终端都基本没怎么用。第一次作业,目的就是理解HTML、CSS、JavaScript三者本身。如果上来就整一套脚手架或者框架,页面是能跑起来,但底层原理全被工具遮住了,这等于用自动挡汽车学发动机原理,不合适。
我当时建立的目录结构是这样的:
my-page/ ├─ index.html ├─ css/ │ └─ style.css └─ js/ └─ main.js目录只有三层,但每一层都知道自己该放什么。index.html放页面结构,css目录放样式,js目录放交互。以后项目变大,这个习惯要继续延展成pages、components、assets这些更细的划分,但思路是一样的——让文件的存放位置和它的职责一一对应。
我在这个目录上还做了一个在当时看来“多余”但事后很感激的决定:把整个项目放进了git仓库。虽然只有我一个人提交,整个过程也就commit了七八次,但每一版改动都留下了记录。这个习惯以后会非常值钱,哪怕只是回退一次删除错了的样式。
2.2 先画线框图,再写代码
很多同学写页面是想到哪里写到哪里,结构写到一半发现导航栏样式难调,回头又去改HTML,结果两头打架。我没让自己掉进这个循环里,而是先打开一个空白文档,用方框把页面从头到尾画了一遍。
这个线框图非常粗糙:最上面一条横框是导航栏,下面一个大框是个人信息区,再往下是四个并排的小方块代表技能卡片,接着是一排图片缩略图,最后是页脚。整张图没有颜色、没有字体、没有圆角,但布局关系已经全部确定了。
线框图的威力在于,它把“设计”和“实现”两件事拆开了。在设计阶段我不会被任何CSS属性干扰,只需要思考一个问题:模块之间怎么排列。而真正写代码的时候,我又不需要再纠结“这一块应该放在页面哪个位置”,照着线框图填内容就行。整个过程从“边想边写”变成了“先想后写”,效率提升非常明显。
配色方面我也提前定好了:主色用一种不刺眼的深蓝,辅色是灰色,强调色用橙色。页面里所有按钮、链接、标题高亮都用这三个颜色,没有临时起意加别的颜色。这样做出来的页面风格很统一,哪怕细节粗糙,整体观感也在及格线以上。
3. 从零手写页面:HTML结构、CSS布局与一个加分的交互
3.1 HTML骨架:从doctype到语义化标签
写HTML时我牢记一个原则:先写结构,再想样式。结构是页面的骨架,样式是血肉,顺序不能反。我的index.html骨架大致长这样:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>我的个人主页</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <header class="site-header"> <nav class="nav-bar"> <a href="#intro">关于我</a> <a href="#skills">技能</a> <a href="#gallery">作品</a> <a href="#footer">联系</a> </nav> </header> <main> <section id="intro" class="section intro-section"> <!-- 个人信息区 --> </section> <section id="skills" class="section skills-section"> <!-- 技能展示区 --> </section> <section id="gallery" class="section gallery-section"> <!-- 图片展示区 --> </section> </main> <footer class="site-footer" id="footer"> <!-- 页脚信息 --> </footer> <script src="js/main.js"></script> </body> </html>这个骨架有几个细节值得说一下。meta charset写的是UTF-8,对应中文页面,漏了它后面就会出现乱码,这是新手很容易忽略的。viewport标签是为了在手机浏览器里正常显示,虽然这次作业不强制要求移动端适配,但加上它不会扣分,反而体现你注意到了适配问题。
语言属性lang="zh-CN"同样重要,它告诉浏览器这个页面是以中文为主的内容,对某些浏览器翻译功能和阅读器来说是有效信息。
导航栏的链接用了锚点href="#intro"这种写法,点击后会平滑滚动到对应区块。锚点是HTML里最容易忽视但非常实用的功能,很多第一次作业不用锚点也照样过关,但加了它,页面交互感立刻就上来了。
3.2 导航栏和首屏个人信息区的CSS实现
导航栏我用了flex布局。第一次用flex的时候觉得它简直是个神器——过去用float和inline-block调整对齐能把人逼疯,flex只需要写两行就能让子元素横着排开并且垂直居中。
.site-header { position: sticky; top: 0; background: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); z-index: 10; } .nav-bar { display: flex; justify-content: center; gap: 32px; padding: 16px 0; } .nav-bar a { text-decoration: none; color: #333; font-weight: 500; transition: color 0.2s ease; } .nav-bar a:hover { color: #e67e22; }position: sticky这个属性我当时花了好一会儿才理解。它和fixed的区别在于:sticky元素在滚动到指定位置之前是正常的文档流元素,滚动到top: 0之后就固定在视口顶部。用在这里的效果就是:页面往下滚动时,导航栏一直停留在屏幕顶部,不遮挡内容。
首屏个人信息区是我花时间最多的部分。头像用border-radius: 50%裁成圆形,名字放在头像下面,再用一行字介绍身份。居中我用的是text-align配合margin: 0 auto的方式,后来又用flex把整个区域变成垂直水平都居中,整个页面一下子就“正”了起来。
3.3 技能卡片与图片区域的布局选择
技能展示区需要放四个技能卡片。当时我在flex和grid之间犹豫了一下,最后选了grid,因为它的语义更贴合“行列整齐的卡片排列”。一行四列、间距均匀,只需要两行代码:
.skills-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; } .skill-card { background: #fff; border-radius: 12px; padding: 24px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); }卡片加了圆角和阴影之后,视觉上就有一个“浮起来”的效果。这一点看起来很轻巧,但圆角的弧度、阴影的透明度和模糊半径都需要微调。我调的阴影一开始太深,像块石头压在页面上,后来把透明度从0.2降到了0.08,效果立刻轻盈了。
图片展示区相对简单,我把三张图片用flex排列,每张图设置固定的宽高,再用object-fit: cover让图片内容填满容器不拉伸。这个属性也是第一次作业里不太要求、但实际场景中非常常用的。
3.4 主题切换按钮:第一次让页面“动”起来
加分项我选了一个主题切换按钮。想法很简单:点一下按钮,页面在浅色主题和深色主题之间切换。第一次写JavaScript交互,心里其实是打鼓的,但真正写下来发现逻辑并不复杂:
const themeToggle = document.getElementById('theme-toggle'); const body = document.body; function switchTheme() { body.classList.toggle('dark-theme'); const isDark = body.classList.contains('dark-theme'); localStorage.setItem('theme', isDark ? 'dark' : 'light'); }这里做了一件有点“超纲”但很实用的事:用localStorage把用户选择记住。刷新页面之后,主题不会丢失。仅这一行代码,就把页面从“提交后无人问津”变成了“每次打开都会记得你选择”的状态。写完之后我在浏览器里刷新了好几次,确认主题确实没变回去,那是一种非常单纯的快乐。
这个按钮本身也放在导航栏右侧,没有单独占一块地方。交互越简单,对整体页面的侵入越小,用户甚至不会觉得这是一个“功能”,而是会觉得它本来就该在那里。
4. 踩坑实录:第一次写页面时最值得记住的四个问题
4.1 CSS里的图片路径,基准点不是HTML文件
第一个坑是关于图片路径的。我在HTML里引用图片一切正常,但在CSS里用background-image设置背景时,图片始终显示不出来。浏览器控制台清清楚楚写着404。
排查了半天才发现,CSS文件里的相对路径,基准点不是index.html所在的根目录,而是CSS文件自己所在的目录。我把图片放在了images文件夹,而CSS在css文件夹,于是从style.css出发去找图片,路径应该是../images/banner.jpg而不是images/banner.jpg。
这个错误现在听起来很基础,但它几乎是每个初学者的必经之路。路径问题的本质是“相对谁去定位”,只要你时刻记得:HTML里的src相对于HTML文件,CSS里的url相对于CSS文件,就不会再犯。
4.2 给子元素加margin-top,父容器却被“带跑”了
第二个坑更加隐蔽。我想让个人信息区的标题离导航栏远一点,于是在h1上加了margin-top: 40px,结果整个section区块连同背景色一起往下挪了40px,h1本身却纹丝不动。
这就是经典的margin塌陷,也叫外边距合并。当一个元素的margin-top和父元素的margin-top相遇时,浏览器不会把两个margin相加,而是取较大的那个。解决办法有很多,我当时采用的是给父容器加上overflow: hidden。这个属性把外层变成了一个BFC区域,内部的外边距就不会“逃”出去了。
后来我做项目时更习惯的解法是:用padding给父容器撑开间距,少用margin来制造父子之间的空隙。把垂直间距交给padding,把水平间距交给gap或margin,整个页面的间距逻辑会清晰很多。
4.3 想让footer贴到底部,结果它总是“飘”在页面中间
第三个坑是布局上的。内容不够多的时候,页脚直接顶到了页面中间,底部露一大片空白,特别难看。我一开始以为是自己内容太少了,于是疯狂往页面塞内容,治标不治本。
正确的做法是用flex布局让页面高度撑满视口:把body设成display: flex; flex-direction: column; min-height: 100vh,然后让main部分flex: 1,这样内容区会自动扩展去占据剩余空间,footer被“推”到底部。
这个坑背后有一个知识点:min-height: 100vh表示最小高度等于视口高度,内容少了也能撑满屏幕,内容多了就继续向下延伸。用了这个方案之后,内容多少都不会影响footer的位置,完美。
4.4 页面中文变乱码,以及字体选择的兼容性
第四个坑是打开页面时中文字符全部变成了乱码。原因很直接:HTML文件里没有写meta charset="UTF-8"。文件保存的时候是UTF-8编码,浏览器默认却可能用了其他编码来解析,于是中文全部“炸”了。加上这个meta之后,刷新页面一切恢复正常。
字体方面也折腾了一下。我设了中文字体栈:
body { font-family: "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", sans-serif; }这么写的原因很现实:不同操作系统内置的中文字体不一样,字体栈要按目标用户来排序,前面找不到就往后一种字体找,最后用系统默认的无衬线字体兜底。字体看着是小事,但稍微在意一点,整个页面的质感就不一样了。
4.5 排查问题时的整体思路
四个坑踩下来,我养成了一个习惯:当某个东西不显示、布局不对、行为异常时,先打开浏览器开发者工具,看Console报错,再看Network请求是否404,最后在Elements面板里检查元素实际渲染出来的样式到底是哪一条规则生效了。很多时候,样式没效果并不是没写,而是被别的规则覆盖了,或者根本没匹配到。
这套“开发者工具优先”的思路,比瞎猜快十倍。说实话,我这次作业有一半的知识点是在调试过程中学会的,写教程只会告诉你“应该这样做”,而调试会告诉你“为什么不能那样做”。
5. 交作业前的自检清单:把自己从悬崖边拉回来的两小时
5.1 在开发者工具里做一轮“体检”
页面写完之后,我做的第一轮检查不是自己肉眼再看一遍页面,而是打开开发者工具,一项一项看数据。
控制台必须没有任何报错。Network面板里所有请求必须都是200。我那次检查还真发现了一张图片的http状态是404,就是图片区里有一张被我删掉但忘了移除引用的图片。这种问题自己盯着页面看根本发现不了,因为缺失的图片区域不一定显眼,但在数据层面是铁打的错误。
5.2 用设备模拟器把页面“捏”成不同尺寸
虽然作业没有强制要求移动端适配,我还是用开发者工具的设备模拟器把页面宽度调到了375像素,模拟手机屏幕。结果不出意外,导航栏四个链接挤成了一团,文字都叠在了一起。
我没有花时间去完善完整的媒体查询,只做了一处最关键的调整:把导航栏的间距从固定gap改成了可以在窄屏下弹性收缩的写法。这个改动很小,但至少让页面在手机宽度下不再惨不忍睹。第一次作业能做到“桌面端完整、移动端不崩”,我觉得就已经合格了。
5.3 检查代码规范和注释质量
功能没问题之后,我开始从头到尾过一遍代码规范。缩进统一用两个空格,类名统一用小写加连字符,CSS属性按照先定位后外观的顺序排列。这些规范不会影响页面效果,但会影响别人读你代码时的心情。
注释我只写了三处,全部都是解释“为什么”的,没有写“这是什么”。比如在sticky那个样式旁边,我写了一行注释:让导航栏滚动后固定,但注意它占位不脱离。这种注释的意义在于,一个月后我自己回来改代码,能快速回忆起当时的设计意图。如果只写一句“这里设了sticky”,等于没写。
5.4 找同学互相评审,比自己检查高效得多
最后一件事,我把页面发给了一位同学,让他帮我看。人看自己的作品会有严重的“视盲”,那些出错的地方你已经自动脑补成正确的了。同学扫了一眼就指出:导航栏的链接没有下划线区分,看起来像普通文字,用户可能意识不到可以点。
我马上给导航链接加了hover时变色的效果,又给“关于我”这类的文字加了一点视觉暗示。这个改动让页面的可用性提升了一大截。互评这件事,效率比自己埋头检查高太多了。前提是,你要真的听得进对方的意见,而不是急着解释“我本来打算改的”。
6. 做完这次作业之后,我重新理解了“前端”
6.1 前端不是“写标签”,而是“控制呈现”
以前我总觉得前端就是把设计图“翻译”成网页。做完这次作业,我发现这个理解太表面了。写HTML的时候,我在决定信息层级;写CSS的时候,我在决定用户的视觉动线;写JavaScript的时候,我在决定用户的操作反馈。前端工作里藏着大量“设计”的成分,只不过它的工具是代码。
一个明显的例子是:我原本打算把个人信息区放得特别大,占满整个首屏。后来在写的时候意识到,用户的注意力是有限的,第一屏应该快速传达最重要的信息。于是我把个人信息区压缩成“头像、名字、一句话身份描述”,其余细节全部下沉到后面的技能区。这种取舍在做作业里没人逼你,但你一旦开始思考,就再也不会回到“把所有内容都堆上去”的写法。
6.2 三个“我以为它很简单,其实不简单”的真相
这次作业让我对HTML、CSS、JavaScript有了完全不同的认知。
HTML的难点不在于写标签,而在于语义组织的合理性。同样的内容,有人用十个div装完,有人用header、nav、main、section、footer加上恰当的嵌套关系,让整个文档结构一目了然。结构清晰之后,样式和交互的实现难度都会下降。
CSS的难点不在于记住三百个属性,而在于它的作用域是“全局”的——页面上任何一条样式规则都可能影响你没注意到的地方。这就导致写CSS像是在一个所有变量都暴露在外的环境里编程,你必须时刻警惕自己写出的规则会不会“误伤”别的东西。
JavaScript的难点则在于事件和状态。它不像CSS那样“改完就能看到效果”,而是需要建立一套“什么动作触发了什么变化”的心智模型。第一次写事件监听时我总觉得它在偷偷运行,后来搞清楚了事件流,心里那团迷雾才散开。
6.3 把一份作业当成一个项目,你会多学到很多东西
最后想说的其实是一个心态问题。
如果我只是为了“交差”,这个作业花两个小时就能做完:一个div套一个div,一个样式接着一个样式贴。但那样子,做完之后除了累,什么都剩不下。我这次多花了大概两三倍的时间,把目录结构、线框图、代码规范、版本管理和自检清单都过了一遍,得到的不是一个“完成”的作业,而是一套以后每次做页面都能复用的流程。
尤其是那次自己画线框图、拆需求的经历,在这次作业之后我每次都沿用。后来我再去写别的页面,不会再花一个下午来来回回改布局了,因为结构早就想清楚了。第一次作业真正的价值,不在于页面本身多好看、分数多高,而在于它逼着你走上了这条更专业的路径。
这个作业的文件夹到现在还躺在我电脑里,偶尔翻出来看,能清楚看到自己当时哪儿幼稚、哪儿用力过猛。我觉得这就是第一次作业应该有的样子——它不用完美,但必须真实记录你当时对前端的全部理解。