从静态页面到可部署Web项目:第二次作业完整实践复盘
发布时间:2026/9/15 2:53:09 作者:尧图编辑部 阅读量:1,286

说实话第二次听到“web作业”这四个字的时候我内心是有点复杂的。第一次作业交了个纯静态页面当时觉得自己能摆个居中的盒子、换几个颜色就挺厉害了结果老师点评里写了一句“页面结构偏简单交互不足”。那会儿还没太当回事直到第二份作业要求出来——要做一个完整的信息展示站带表单、带校验、带数据渲染最好还能部署到服务器上访问。我这才意识到web作业从“画个页面”进化到“做一个东西”了这一关躲不过去只能硬着头皮把那套早已陌生的“web工程”流程重走一遍。这篇文章不聊什么高大上的架构也不讲框架源码我就以一个普通到不能再普通的“第二次做web作业”的学生视角把整份作业从需求拆解、技术选型、页面实现到本地部署、问题排查的完整过程复盘一遍。里面包含了我实际踩过的坑、查了半天才弄明白的原理以及很多在官方文档里根本不会写清楚的“小经验”。如果你也在做类似的信息展示型web项目或者即将面对第一次需要部署的web作业这篇文章应该能帮你少走不少弯路。1. 从需求到方案第二次web作业的整体设计思路1.1 先搞明白作业到底要什么很多同学拿到作业要求的第一反应是“打开IDE直接开写”我第二次学乖了。第一份作业之所以做得稀碎就是因为我连需求都没看全就开始调颜色最后结构乱、功能缺改起来比重写还难受。所以这次我拿到题目后先做了一件事把要求里的每一条拆出来列成一个清单再对照着打分标准逐项确认。比如这次作业的要求大致是做一个主题自选的信息网站不少于4个栏目页需包含表单页且表单要有前端校验逻辑数据展示部分不能是死数据要通过数组或接口动态渲染支持在本地服务器环境运行Tomcat或Nginx代码结构清晰命名规范。我把这些要求翻译成“人话”就是页面要多、交互要有、数据得是动态的、还得能部署起来访问。这里有个非常关键的心态转变——第二次作业考查的重点已经不是“你会不会写标签”而是“你有没有一套完整的web工程思维”。什么叫工程思维就是你要考虑目录怎么规划、CSS和JS怎么拆、公共头尾怎么做、数据从哪来、部署到服务器后资源路径会不会失效。这些才是成绩拉开差距的地方。1.2 技术选型不折腾但也不完全躺平技术选型是第二次作业里最让人纠结的环节。我在选型时看到了很多热搜词比如“web vue 开发 配电工艺图”“python django搭建web项目”“idea2024版本创建web项目”说实话每个方向都能做但每个人的基础条件不一样我的结论是除非老师明确要求必须用框架否则第二次作业最稳的方案是原生三件套HTML CSS JavaScript配合一个轻量的本地服务器比如Tomcat或Nginx。为什么这么选第一原生三件套的排错成本最低浏览器打开就能看效果不用处理npm安装失败、依赖版本冲突这类问题第二老师评分时最容易看懂的也是原生代码你把逻辑写在明面上比包了一层框架让老师找不到你的代码在哪要好得多第三也是最重要的第二次作业的根本目的还是打基础框架什么时候都能学但HTML结构、CSS布局、JavaScript操作DOM这些底层能力才是决定你以后能不能真正理解Vue或Django到底帮你干了什么的前提。不过这里我也做了个折中——虽然不引入框架但我用了模块化的文件组织方式把公共样式、工具函数、页面脚本按职责拆开。这在工程思路上已经比“单文件怼到底”前进了一大步老师在代码结构这一项上通常也会给不错的评价。1.3 项目目录结构与模块划分目录结构是第二次作业里容易被忽略但实际最能体现“工程感”的地方。我第一次作业是所有文件平铺在一个文件夹里写到最后自己都分不清哪个CSS是给哪个页面用的。这次我提前规划了目录实际上参考了很多真实web项目的做法web-homework2/ ├── index.html ├── pages/ │ ├── list.html │ ├── detail.html │ └── contact.html ├── css/ │ ├── common.css │ ├── index.css │ └── list.css ├── js/ │ ├── common.js │ ├── list.js │ └── contact.js ├── assets/ │ ├── images/ │ └── data/ │ └── products.json └── README.md这个结构的好处是每个文件职责单一改动某个页面不会影响全局公共样式和公共脚本可以复用不用在四个页面里复制粘贴同样的导航栏代码数据文件单独拎出来后续如果接后端接口只需要改数据获取那一层不用动页面结构。一个小提示命名规范尽量统一用字母小写加短横线不要用中文文件名也不要用大写字母开头的驼峰命名因为Linux服务器上的Tomcat和Nginx对文件名大小写是敏感的本地Windows打开正常一部署到服务器就404这种问题排查起来特别折腾。2. 从静态页到动态感核心页面和交互的实现2.1 页面布局与样式让浏览器先“听话”第二次作业在页面上最明显的要求就是“不再只是居中的几个盒子”。我在设计时给自己定的标准是至少有一个页面要有完整的头部导航、主体内容区、侧边栏和底部信息区列表页要有卡片式布局表单页要有一到两个自适应断点。这套要求听起来复杂但落地时用的核心工具其实就两样Flexbox和Grid。以列表页举例我采用Grid布局做卡片网格核心代码大概是这样的.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } media (max-width: 768px) { .card-grid { grid-template-columns: repeat(2, 1fr); } } media (max-width: 480px) { .card-grid { grid-template-columns: 1fr; } }这里的原理是Grid布局天生适合做二维排列repeat(3, 1fr)会严格按照比例生成3个等宽的列。假如你不小心把gap写成px的负值或者忘了加box-sizing: border-box就会发现卡片宽度算来算去都对不上最后溢出屏幕或者换行错乱。样式这块我重点说两个最容易出问题的地方。第一个是全局盒模型强烈建议在任何新项目的CSS开头加上* { margin: 0; padding: 0; box-sizing: border-box; }不加这个padding和border会把元素的宽度撑破尤其是你有多个并列盒子的时候差几个像素就会导致最后一个盒子被挤到下一行。第二个是字体和颜色要抽成CSS变量我这次用了:root { --primary-color: #2563eb; --text-color: #333333; --bg-color: #f8fafc; --border-radius: 8px; }这样做的意义在于后期如果你觉得主题色不好看只需要改这一个变量全站所有用到这个颜色的地方都会同步变不用按CtrlF一个一个找。2.2 表单交互与数据渲染用JavaScript把页面“激活”第二次作业最核心的动态能力要求就是表单校验和数据渲染。表单校验这块我优先用JavaScript写了一套自定义校验而不是全部依赖HTML5的required等内置属性。为什么因为系统内置校验的样式非常原生而且不同浏览器表现不一致很难做到统一且美观的反馈效果。我的做法是监听表单的submit事件在校验函数里逐项检查输入内容并动态生成错误提示。核心逻辑如下document.getElementById(contactForm).addEventListener(submit, function (e) { e.preventDefault(); const name document.getElementById(name).value.trim(); const email document.getElementById(email).value.trim(); const msg document.getElementById(message).value.trim(); let isValid true; if (!name) { showError(name, 姓名不能为空); isValid false; } else { clearError(name); } if (!/^[\w.%-][\w.-]\.[A-Za-z]{2,}$/.test(email)) { showError(email, 邮箱格式不正确); isValid false; } else { clearError(email); } if (msg.length 5) { showError(message, 留言内容至少5个字符); isValid false; } else { clearError(message); } if (isValid) { alert(提交成功感谢留言); } });这里最关键的一行是e.preventDefault()。如果你不阻止浏览器默认行为表单会在提交时刷新页面那你写的校验逻辑还没执行完整个页面就重置了看起来就像“点提交一片空白”。这也是很多同学遇到的“我的校验怎么不生效”的常见原因。数据渲染部分我准备了一个products.json文件里面存了一批商品或资讯数据然后通过fetch()读取并渲染到列表页。原理就是从接口拿数组再把数组里的每一项生成DOM元素塞进容器fetch(./assets/data/products.json) .then(res res.json()) .then(data { const container document.getElementById(productList); const cards data.map(item div classcard h3${item.title}/h3 p${item.summary}/p span${item.price || }/span /div ).join(); container.innerHTML cards; });注意用file://协议直接双击打开html文件时浏览器会拦截fetch的本地文件请求报CORS跨域错误。解决办法是把项目放到Tomcat的webapps目录下或者用VS Code的Live Server插件起一个本地静态服务让页面通过http://localhost访问fetch才能正常工作。这是一个非常典型的“本地双击正常启动服务器反而报错”的反向坑我刚开始也困惑了很久。2.3 实用性扩展试着加一点“毕业后的职场感”第二次作业如果想拿高分我强烈建议在基础要求之上加一个能体现“实用性”的小功能。我在这次作业里加的是打印样式表和页面访问计数两个小功能参考了“web页面pdf打印”这个热点方向。打印样式表的作用是保证用户点击打印按钮后打印出来的页面是干净的纯内容版本没有导航栏、侧边栏和广告位之类的干扰元素。实现方式是用CSS的media printmedia print { .navbar, .sidebar, .footer, .btn-print { display: none !important; } .main-content { width: 100%; margin: 0; padding: 0; } }然后在页面上加一个打印按钮绑定window.print()。这个功能在很多信息管理类网站里是刚需作业里出现它会让人觉得你考虑问题很务实。页面访问计数可以用localStorage实现。localStorage是浏览器提供的本地存储同一个域名下的页面共享数据适合统计用户在自己浏览器上访问过几次。代码非常简单let count parseInt(localStorage.getItem(visitCount) || 0); count 1; localStorage.setItem(visitCount, String(count)); document.getElementById(visitCount).textContent count;这类小功能技术上不难但能让你的作业在一堆“静态页面大集合”中显得有血有肉老师也更容易给到高一级的评分档位。3. 本地调试与部署让作业换个环境也能跑3.1 在IDEA 2024中创建并运行Web项目部署这部分是我第二次作业里花时间最多、也最崩溃的环节。先说工具选择因为我用的是IntelliJ IDEA 2024版本所以我直接在IDEA里创建了一个Web项目。如果你也用IDEA操作路径大概是File - New - Project - Java Enterprise然后勾选Web Application应用服务器选择Tomcat。新建完项目后IDEA会自动生成一个web目录下面有WEB-INF和index.jsp或index.html。这里有个关键知识点要搞明白Tomcat部署时的结构和你本地写的静态网页结构不太一样。Tomcat默认的站点根目录是webapps你把项目打包成war包丢进去或者直接把整个项目文件夹拷到webapps下Tomcat启动后就能通过http://localhost:8080/项目名/访问。如果你用的是IDEA的Tomcat集成方式它实际上是帮你把项目部署到了Tomcat的webapps目录下。启动时如果端口冲突会报Address already in use: JVM_Bind这时需要到Tomcat的conf/server.xml里修改Connector端口或者找出占用8080端口的进程结束掉。Windows上可以用netstat -ano | findstr 8080查看占用进程PID再用taskkill /PID 进程号 /F结束。3.2 Tomcat与Nginx从单项目到多项目部署我在部署作业的过程中还顺便研究了一下“nginx部署多个web项目”这个场景。因为如果你以后要把多个作业或者多个应用部署到同一台服务器上Nginx是一个非常实用的入口。Tomcat默认端口是8080Nginx默认端口是80。如果服务器上要同时跑好几个web项目常见做法是不同项目用不同的Tomcat端口再用Nginx做反向代理按访问路径转发到不同端口。举个例子Nginx配置可以这样写server { listen 80; server_name localhost; location /homework2/ { proxy_pass http://127.0.0.1:8080/homework2/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /homework3/ { proxy_pass http://127.0.0.1:9090/homework3/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这样浏览器打开http://localhost/homework2/就能访问端口8080上的项目打开http://localhost/homework3/则访问9090端口上的另一个项目。从用户视角看他们只会接触到80端口不需要关心内部端口分配这就是Nginx最常见的应用场景之一。不过要注意如果是前端静态页面加接口的项目proxy_pass后面的路径匹配非常容易出错。Nginx的location路径是否带结尾斜杠决定了转发时路径是否会被替换或拼接这一步我调试了整整一个晚上最后发现就是少带了一个斜杠导致页面能打开但CSS全部加载失败。3.3 部署后的文件夹结构与路径检查清单部署完之后最重要的一件事就是检查资源路径。很多同学在本地双击打开页面一切正常部署到Tomcat或Nginx后图片没了、CSS丢了、JS不加载了十有八九是路径写成了绝对路径或错误的相对路径。本地写页面时常见写法是link relstylesheet hrefcss/common.css这种相对路径在本地file协议下能正常解析但如果你把静态资源放在WEB-INF目录下Tomcat会直接拒绝外部访问因为WEB-INF是受保护目录。所以静态资源应该放在webapps/项目名/css、webapps/项目名/js这种能被外部直接访问的路径下。另外还有一种情况你用的是“/css/common.css”这种以斜杠开头的绝对路径这种写法会直接去站点根目录找文件。本地时站点根目录就是你的项目文件夹没问题部署到Tomcat后站点根目录变成了http://localhost:8080/这时候它找的其实是http://localhost:8080/css/common.css而不是http://localhost:8080/项目名/css/common.css于是又404了。解决办法是相对路径或者用JSP的EL表达式、后端模板语法动态拼接项目上下文路径。部署完成后我列了一个检查清单就是关闭浏览器缓存重新访问每个页面按F12打开开发者工具在Network面板里逐个检查有没有红色的404资源。有就立刻看是路径问题还是文件真的没传上去。这一步做得越早你部署阶段越轻松。4. 常见运行错误与排查思路实录4.1 “加载Web视图时出错”这类问题的本质我在做作业的过程中搜到一个高频热搜词叫“加载 web 视图时出错: error: could not register service worker: invalidstatee”乍一看特别吓人其实这个报错大多数时候发生在你打开安装了PWA渐进式Web应用相关脚本的页面时。Service Worker是浏览器在后台运行的一段脚本用于离线缓存和消息推送但它对协议有严格限制必须在HTTPS或localhost环境下才能注册。如果你在浏览器里直接双击html文件用file://协议打开或者在普通HTTP的非localhost域下访问浏览器就会拒绝注册Service Worker于是控制台抛出一大堆看不懂的英文报错。这个报错通常不会影响页面打开因为你的业务代码和Service Worker是两套体系页面主体内容该加载还是会加载。但只要页面报了这个错很多人就慌得不行以为是自己的代码写坏了。排查思路其实很简单先看页面基本功能是否正常。如果只是工作台报错、页面能打开能交互多半就是Service Worker环境问题不用动代码。如果你压根没写过Service Worker却在控制台看到这个报错很可能是引入了某个第三方库或模板附带的注册脚本。把注册逻辑找出来删掉或者改为仅在production模式下启用就可以消除这个问题。4.2 Service Worker注册失败与缓存困扰Service Worker还有一个更让人头疼的副作用就是缓存。它一旦注册成功被它控制的页面就会优先走缓存导致你明明改了CSS和JS刷新浏览器就是不生效看起来跟“代码改了个寂寞”一样。我这次就遇到了这个问题——改了样式后刷新了十几次都没变化最后发现是早期测试PWA功能时注册的Service Worker在作祟。解决办法分两条路。临时解决打开开发者工具 - Application - Service Workers点击Unregister取消注册然后右键刷新按钮选“清空缓存并硬性重新加载”。长期解决把Service Worker的注册逻辑从业务代码里移除或者只在生产环境部署后的正式地址启用本地开发一律不注册。这里我想特意提一下Web开发中有几个错误信息看着像天书其实背后的原理极其简单。比如“could not register service worker”本质是协议限制“无效状态”本质是当前上下文不满足约束条件。遇到这种报错最忌讳的就是对着英文报错逐字翻译然后瞎改代码正确做法是先确认你当前访问方式是不是http://localhost或HTTPS先把这个前置条件解决了再说。4.3 部署后打不开页面三个排查方向最后说说“部署了但打不开”这个经典问题。我这次部署时也遇到过当时内心真的很奔溃明明本地跑得好好的放到服务器上就是访问不了。后来按下面三个方向排查问题通常都能解决我整理成了一张排查路径表排查方向常见原因检查方式解决方案端口问题Tomcat或Nginx端口没放行或端口被占用netstat -ano | findstr 8080防火墙放行启动前查端口云服务器在安全组里放行80/8080路径问题资源路径是绝对路径或大小写不一致开发者工具Network面板看404记录改相对路径或统一使用小写文件名上下文路径问题项目名不一致导致访问地址不对查看Tomcat的webapps目录下的实际文件夹名访问URL中的项目名与文件夹名保持一致这里再单独强调一下防火墙。很多人部署完web项目在服务器本机用curl或者浏览器访问没问题但从自己电脑就是打不开。这种情况十有八九是云服务器安全组或者Linux防火墙把端口挡了。如果你的系统是Ubuntu 22.04可以用sudo ufw allow 8080/tcp sudo ufw reload如果是CentOS 7及以上或兼容系统常用的是firewalldfirewall-cmd --zonepublic --add-port8080/tcp --permanent firewall-cmd --reload这个知识点在很多“web服务器安全”相关话题里都会被提到。不要只想着代码部署阶段有一半的坑都来自操作系统环境和网络策略。4.4 我踩过的其他几个小坑除了上面那几个大问题我这次做作业还积累了一些零零散散的小经验这里一次性分享出来。第一个是编辑器编码问题。Windows下IDEA新建文件默认可能是GBK编码而Tomcat默认用UTF-8读取结果就是页面上全是乱码。解决方案是新建文件时把Encoding选成UTF-8另外可以在server.xml里给Connector加上URIEncodingUTF-8。第二个是中文文件名问题。一旦你在页面中引用了中文名字的图片比如“首页背景图.png”部署到Linux服务器后很容易404。因为Tomcat和Nginx在Linux下对URL的中文编码处理比较麻烦我建议所有静态资源文件全部重命名为字母数字组合一劳永逸。第三个是“页面已提交表单刷新时提示重新提交”。这是表单POST提交后刷新页面导致的浏览器会弹出“确认重新提交表单”的提示。解决方法是在提交成功后用location.href跳转到一个成功页或者用前端拦截后改成AJAX/Fetch异步提交刷新页面就不会再有重新提交确认。我当时用了fetch异步提交体验顺滑很多这个方案也推荐给你。第二次作业做完之后我最大的感觉不是“我会写网页了”而是终于明白了“web工程”和“写几个文件”之间的差别。一个能部署、能交互、结构清楚、出了问题能顺着思路排查的项目才叫web工程而一堆双击打开能看的HTML只能叫练习文件。在做这次作业的过程中我养成了一个习惯每次启动服务器、部署项目、修改资源路径后都强制自己先清缓存再访问先看控制台再动代码先确认环境再怀疑逻辑。这套排错顺序听起来简单但真的能帮你少走很多弯路。最后再分享一个小技巧如果你在做web作业时遇到了某个报错别急着复制报错原文去到处问先用自己的话把问题描述清楚——你输入什么URL、打开了哪个页面、做了什么操作、浏览器控制台有没有红色报错、服务端日志有没有异常。把这段信息整理完你会发现大部分问题自己就知道怎么解决了。