资讯动态

网页设计作业复盘:原生HTML/CSS/JS打造个人作品集实战

发布时间:2026/10/18 3:32:42 来源:尧图企业网站定制
去年底我收到一位学生发来的代码压缩包文件名很朴素赵佳荣的作业。这是某高校网页设计课程的期末大作业要求用原生 HTML/CSS/JavaScript 实现一个个人作品展示网站不规定主题但目录里必须包含响应式布局、表单、动画和图表。类似的作业我每年经手几十份多数人把它当成“交了就行”的任务可这一份让我想专门拆开写篇东西——因为他从拿到题目的第一分钟起就把作业当成一个微型真实项目来做前后两周的过程几乎复现了一名前端新手从“能写页面”到“会做项目”的关键转变。这篇文章不评价分数只还原过程如何拆需求、定技术方案、搭骨架、写样式、做交互、查问题、做验收。不管你是正在上大学还是准备转行做前端开发这份复盘里面提到的决策逻辑和踩坑记录应该比不少“从入门到精通”的教程更贴近实战。我尽量按当时的推进顺序讲该给代码给代码该讲原理讲原理中间穿插一些事后回头看才明白的经验。1. 拿到题目先别写代码需求拆解才是拿分的第一步许多初学者拿到作业题目的第一反应是“我会了赶紧写”结果往往写到一半发现缺这个漏那个。赵佳荣这份作业比较典型的做法恰恰相反他花了大半天时间在做翻译工作——把一句简单的题目要求拆成一张可以逐项打勾的任务表。1.1 作业原文背后的隐性要求原题目其实只有三行做一个个人作品展示网站主题自选包含页面不少于五个。看起来自由度很高但真要按这三个字面要求做大概率只能拿及格分。原因在于课程大纲里还有一份没写进作业文档的评分细则里面列了“响应式适配”“交互反馈”“代码注释”这类可量化项。也就是说老师真正想考察的不是你会不会套模板而是你有没有把课堂讲过的知识点用进作品里。所以拆需求的第一步就是去找“需求的需求”。赵佳荣把课程的课件目录翻了一遍把所有出现过的知识点列成清单盒模型、浮动与定位、Flexbox、Grid、媒体查询、表单验证、DOM 操作、本地存储、CSS 动画。然后对照作品自己想表达的内容再决定哪些知识点值得用、哪些用了纯粹是堆砌。1.2 把评分标准翻译成可执行的任务清单这一步我建议直接做一张表格左边写评分维度右边写“我打算怎么证明我掌握了这个能力”。赵佳荣当时的表格长这样评分维度作品中的落点验收方式页面结构语义化五个页面全部使用 header、nav、main、footer 等标签审查元素确认结构响应式布局首页自适应三种断点768px、1024px、1280px缩放浏览器窗口测试表单交互联系页表单含必填校验和成功提示提交空表单看拦截效果动画与体验导航栏吸顶和卡片悬停效果滚动与鼠标悬停操作代码规范命名采用 BEM 风格CSS 分组注释抽查文件头部注释这张表最大的作用不是交作业时给老师看而是给自己划定了“做完”的边界。新手特别容易在自由度高的题目里无限加功能加了三天三夜还不满意最后核心页面反而粗糙。任务清单让赵佳荣在做到第七天时就可以很理性地说“功能清单已经全部完成剩下的时间用来测试和打磨。”2. 技术方案选型与项目骨架设计需求拆完之后赵佳荣面临第二个决策用什么技术实现。这个选择看起来简单其实非常能反映一个人对项目的理解深度。2.1 为什么选原生三件套而不是框架选题方向是个人作品集页面以展示为主交互复杂度不高。赵佳荣当时已经自学了一点 Vue 的基础也纠结过要不要用框架。我看了他的初版计划后给了一个明确的建议这轮作业不要用框架。原因有三。第一课程的评分标准建立在原生 DOM 操作和 CSS 布局能力上用框架会把评分点“隐藏”起来老师看不到你对基础知识的掌握第二个人作品站的业务复杂度用框架属于杀鸡用牛刀反而引入脚手架、依赖、打包这些与作业无关的变量一旦环境出问题排错成本比写代码还高第三对于新人来说用原生方式把页面做一遍之后再学框架时能更清楚地理解“框架到底帮我解决了什么问题”。这不是说框架不好而是在这个具体场景里技术选型的标准不是“越新越炫”而是“与目标匹配”。作业要展示的是基础能力原生三件套就是最优解。2.2 目录结构与命名规范项目骨架看起来是小事但真到改需求的时候才能体会到组织代码的好处。赵佳荣最终定的目录结构是这样的project/ ├── index.html ├── works.html ├── about.html ├── blog.html ├── contact.html ├── assets/ │ ├── css/ │ │ ├── common.css │ │ ├── nav.css │ │ └── page/ │ │ ├── index.css │ │ ├── works.css │ │ └── ... │ ├── js/ │ │ ├── nav.js │ │ ├── form-validate.js │ │ └── data.js │ └── images/ │ ├── works/ │ └── avatar.jpg这套结构有三个我很赞同的做法一是公共样式单独抽文件二是页面级样式按页面名分文件三是 JS 按功能拆文件而不是开一个巨大的 main.js。后期的实际效果是改导航栏样式只需要动 nav.css完全不会误伤其他页面的排版加一个新页面只需要引入 common.css 和它自己的页面样式不必在全局样式里来回翻找。2.3 用线框图先定版式再写代码赵佳荣在写第一个 HTML 标签之前先画了一份线框图。这份图不是用专业设计工具画的就是纸笔草图加一个在线白板。他的做法是把五个页面各自的主视觉区域、信息层级、导航位置、页脚内容都标出来每画一版就问自己“如果我是用户第一眼看到什么接下来会看什么。”这一步看起来浪费时间实际上极大减少了返工。很多初学者喜欢边写代码边想版式结果 CSS 改来改去一个上午就消耗在按钮放左边还是右边上了。线框图固定的不是像素而是信息结构和视觉动线。等真正写代码时他心里已经有了“每个区域大概放什么内容”的底稿写出来自然顺畅很多。3. 核心页面实现细节与关键代码解读进入编码阶段的头两天赵佳荣完成得很快因为骨架规划得清晰后面就是填肉。这里挑几个有代表性的实现细节展开说都是作业中最容易暴露问题的地方。3.1 响应式导航栏与吸顶效果导航栏是每个页面都有的公共模块也是响应式布局的重点。赵佳荣的做法是桌面端横向排列移动端折叠成汉堡菜单。核心代码并不复杂关键在两点断点设置和状态管理。media (max-width: 768px) { .nav__list { display: none; flex-direction: column; width: 100%; } .nav__list.is-open { display: flex; } .nav__toggle { display: block; } }const toggle document.querySelector(.nav__toggle); const list document.querySelector(.nav__list); toggle.addEventListener(click, () { list.classList.toggle(is-open); });这里面容易踩的坑是在移动端展开菜单后点击菜单项跳转到对应锚点菜单并不会自动收起。赵佳荣第一次测试时就发现了这个问题修复方式是给所有菜单项绑定点击事件手动移除 is-open 类。这种小细节在评分表上没有明确写但老师实际操作时只要点一下就会发现属于典型的“加分类”体验。吸顶效果他用了 position: sticky 而不是 fixed这样能避免吸顶后页面内容被遮挡的问题。这是很多新手容易搞混的地方fixed 是相对视口固定元素脱离文档流sticky 则是元素在滚动到临界值前保持原位置到达后才“粘住”不脱离文档流。同样是吸顶后者对后续布局零侵入。3.2 作品卡片 Grid 布局与自适应作品展示页是整份作业的视觉中心。赵佳荣用 Grid 实现了一套随屏幕宽度自动调整列数的卡片墙.works__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }这句 CSS 是整个页面最值钱的一行。auto-fit 会让浏览器根据容器宽度自动排列卡片minmax(280px, 1fr) 规定每张卡片最小 280px小于这个宽度就换行大于则均分剩余空间。也就是说从手机到宽屏显示器不需要写任何额外媒体查询卡片数量会自动匹配。但这里有一个新手容易忽略的问题卡片里的图片如果不加约束Grid 布局会被撑破。赵佳荣的解决方案是给图片容器设固定宽高比.works__card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }aspect-ratio 加 object-fit: cover 的组合可以保证图片无论源文件是什么比例都被裁剪成统一规格且不变形。很多作业里图片东一张西一张不同尺寸排版乱得没法看根因就是缺这两行。3.3 CSS 动画的克制使用动画是加分项但也是最容易做“土”的地方。赵佳荣的作业里动画用量很少集中在两处卡片悬停时的轻微上浮加阴影扩散以及页面滚动到作品区域时的渐显效果。.works__card { transition: transform 0.25s ease, box-shadow 0.25s ease; } .works__card:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08); }选择 0.25s 而不是更快的 0.1s是因为人眼对 0.1s 的过渡基本感知不到反而会觉得生硬但也不建议超过 0.4s否则页面显得拖沓。transition 的 ease 曲线适合大多数 UI 动效不要随便换 cubic-bezier除非你明确知道自己在追求什么效果。关于渐显效果他用了 IntersectionObserver 而不是滚动事件监听理由是性能和体验都更好这个选择我在下一节详细说。4. 交互与功能补全让页面“活”起来作业如果只有静态页面哪怕布局再漂亮评分上限也不高。赵佳荣这轮的重点放在三个交互功能上每一个都对应一个真实的用户场景而不是为了交作业硬加。4.1 滚动监听与导航高亮作品集页面较长用户滚动浏览时导航栏如果始终停留在初始状态会失去“当前在哪一块”的方位感。赵佳荣用 IntersectionObserver 实现了某个区块进入视口后导航对应菜单项自动高亮。const sections document.querySelectorAll(section[id]); const observer new IntersectionObserver( (entries) { entries.forEach((entry) { if (entry.isIntersecting) { const id entry.target.getAttribute(id); document.querySelectorAll(.nav__link).forEach((link) { link.classList.toggle(is-active, link.getAttribute(href) #${id}); }); } }); }, { rootMargin: -20% 0px -70% 0px } ); sections.forEach((section) observer.observe(section));这里的 rootMargin 是调参的重点。它表示观察区域的扩展范围我把它理解为“观察窗口的敏感带”-20% 从顶部收窄-70% 从底部收窄相当于只取视口中间大约 10% 高的区域作为触发点。这样做的好处是只有当区块进入视口核心区域时才切换高亮避免页面顶部稍微划过就疯狂闪烁。新手做滚动监听时最容易出现的问题就是高亮乱跳根源往往是触发阈值设得太宽。为什么不用 scroll 事件因为 scroll 事件在浏览器主线程上高频触发哪怕只做简单的计算也可能造成卡顿。IntersectionObserver 是浏览器原生提供的异步观察接口触发频率由浏览器自动控制对移动端尤其友好。这个设计放到面试里是能稳拿一分的细节。4.2 表单校验与提交反馈联系页的表单很多人的做法是“能填、能点提交按钮”就算做完但赵佳荣把校验流程做了完整的闭环空值拦截、格式校验、错误提示、提交成功反馈。const form document.querySelector(#contact-form); const emailInput document.querySelector(#email); const errorMsg document.querySelector(#email-error); form.addEventListener(submit, (e) { e.preventDefault(); const validEmail /^[^\s][^\s]\.[^\s]$/.test(emailInput.value.trim()); if (!emailInput.value.trim()) { errorMsg.textContent 邮箱不能为空; errorMsg.hidden false; return; } if (!validEmail) { errorMsg.textContent 邮箱格式不正确; errorMsg.hidden false; return; } errorMsg.hidden true; fetch(/api/contact, { method: POST, body: new FormData(form), }) .then((res) { if (res.ok) return res.json(); throw new Error(network error); }) .then(() { form.reset(); showToast(提交成功我会尽快回复); }) .catch(() showToast(提交失败请稍后重试)); });这里有两个值得展开的点。一是正则校验只做格式判断不做长度和字符集限制因为邮箱格式检查没必要做得过严过严反而把合法地址拦在外面。二是 fetch 提交到 /api/contact 这个地址但作业其实没有后端所以他额外做了兼容处理当请求失败时显示“提交失败”的提示而不会让用户以为页面卡死了。后来的答辩中老师专门问了这个行为赵佳荣说是“假装有后端但至少失败路径是可感知的”。能在没有后端的情况下把错误路径想周全说明他已经开始用产品思维思考问题了。4.3 聊天数据的本地存储实践首页的“最近项目动态”区域赵佳荣一开始直接写死了一个 JSON 数组。后来他想到如果这些数据能通过一个简单的管理入口实时更新交互感会更强。于是他给页面加了一个小功能通过一个隐藏的输入面板把新增的动态数据存进 localStorage页面刷新后依然保留。const feedData JSON.parse(localStorage.getItem(feedData)) || initialData; function saveFeed() { localStorage.setItem(feedData, JSON.stringify(feedData)); }功能本身不复杂但我建议所有作业里都尝试类似的数据持久化写法。它的意义不在于代码量而在于让新人第一次真实理解“状态”这个概念——页面不仅仅是静态的 DOM还可以有需要被保存的数据。把 localStorage 类比成一个小型储物柜刷新页面相当于离开房间再回来储物柜里的东西还在这就是状态持久化。5. 调试过程与常见问题排查实录调试是这次作业里实际耗时最长、学到的经验最多的环节。赵佳荣在测试时遇到的问题几乎都是新手会遇到的典型坑我把它整理成一份排查表按顺序讲讲当时的定位思路和解决办法。现象原因排查路径修复方案移动端菜单点不开JS 引用放在 head 里DOM 还没加载完就绑定事件打开控制台看报错发现 null 引用将 script 移到 body 末尾或用 DOMContentLoaded 包裹卡片图片撑破网格Grid 子项默认 min-width:auto内容最小宽度超过 280px检查计算样式看卡片实际宽度给 img 加 width:100% 和 aspect-ratio导航高亮乱闪观察区域太大多个区块同时可见临时把 rootMargin 调成 0 观察触发情况改成只留视口中央 10% 的触发带页面字体在手机上偏小未设置 responsive 视口 meta 标签用手机访问发现整个页面是缩放状态补上 viewport meta 标签表单提交后控制台报 404没有后端接口fetch 请求找不到地址查看 Network 面板确认请求失败添加 catch 分支给用户可见反馈这里面我想多说一个“控制台报错不一定是代码问题”的经验。那次菜单点不开赵佳荣一开始在 CSS 里反复找毛病因为控制台并没有红色报错。后来打开开发者工具的 Console 面板才发现脚本在 head 里执行时document.querySelector(.nav__toggle) 返回的是 null因为那段 HTML 还没被解析到。这类问题和浏览器渲染顺序有关属于非常典型的前端入坑点。排查定位的经验是先看控制台有没有报错再看 Network 面板有没有异常请求最后才怀疑样式问题——这个顺序别反了。6. 交作业之前自查清单与进一步提升的建议在正式提交前赵佳荣做了一次完整的验收把第 1 节的那张任务表逐项打勾。我把他最终使用的自查清单简化成下面这份可以作为任何网页项目交付前的通用检查项。结构层面HTML 是否包含完整的 head 和 body每个页面是否有唯一的 title图片是否都有 alt 属性表单控件是否有配套 label语义化标签是否被正确使用而不是 style 容器。样式层面是否有任意水平滚动条出现字体是否统一颜色对比度是否足够断点处是否出现布局错位hover 和 focus 状态是否都有明确反馈。行为层面刷新后导航高亮是否恢复正常浏览器前进后退是否影响页面状态移动端点击菜单、表单、链接是否都顺畅没有网络时页面是否优雅降级。特别提醒一点很多人在提交前最常漏掉的是检查“图片外链”。如果用了外链图床一旦对方域名过期或防盗链作品就会大面积破图。赵佳荣在验收时把所有图片全部打包进本地 images 目录这个决定从源头杜绝了这类风险。关于这份作业的后续赵佳荣在提交后没有停手而是把作品集页面继续扩展成了自己的线上简历并把部署流程走了一遍。他后来跟我说两周作业最大的收获不是分数而是终于体会到一个“完整”的项目应该长什么样从需求到设计从编码到测试每一步都有章法可循。这其实也是我写这篇文章最想传达的东西——作业从来不该是糊弄的任务它是你第一次以“开发者”而不是“做题人”的身份去完成一个作品的机会。下一次再看到类似“×××的作业”这样的文件名时希望被你写进代码里的不止是功能还有对整个流程的理解。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑