HTML、CSS 和 JavaScript 是构成网页的三项基础技术:HTML 决定页面结构,CSS 决定页面外观,JavaScript 负责页面交互。本讲将从零开始,完成新闻系统中新闻展示页的页面重构,依次介绍 Web 应用的基本架构、HTML 页面结构、CSS 页面布局,以及基于 Swiper 与 jQuery 的界面特效和数据驱动渲染,最终产出一个与设计稿一致的新闻首页。
《企业技能实习》· 新闻系统全栈项目实训
前置讲义
- 无,本讲是全栈项目实训的第一讲。
资源下载
- 前端素材包(
images图片素材、jquery/swiper/axios库文件),由教师统一发放 - 页面设计稿:
设计稿/index_01.jpg - 成品参考图:
成品效果图/index.png
1. Web 应用基础概念
1.1 浏览器、服务器与数据库
网页上显示的新闻并不存放在浏览者的电脑里。当用户打开一个新闻网站时,数据的流动过程如下:
- 浏览器(Browser)向服务器(Server)发出请求:"我需要新闻列表";
- 服务器接收请求,到数据库(Database)中取出数据;
- 服务器将数据返回给浏览器,浏览器将其渲染为用户看到的页面。
浏览器(HTML/CSS/JS) ←→ 服务器 Node+Express(接口) ←→ 数据库 MongoDB
Day 1 Day 3 Day 2
三个角色的一句话分工:
| 角色 | 职责 | 本项目使用的技术 |
|---|---|---|
| 浏览器 | 负责"显示",解析 HTML/CSS/JS 并渲染页面 | Chrome |
| 服务器 | 负责"接待",接收请求、提供数据 | Node.js + Express |
| 数据库 | 负责"存储",数据真正的存放处 | MongoDB |
动手观察:打开任意新闻网站 → 按
F12打开开发者工具 → 切换到 Network 标签 → 刷新页面,可以看到浏览器发出的一条条数据请求。完成本课程后,我们就能自己编写这样的请求。
1.2 HTML:页面的骨架
HTML(HyperText Markup Language,超文本标记语言) 用标签(Tag)为内容标注语义,浏览器根据标签渲染页面。可以将它理解为页面的"骨架",只负责回答"页面上有什么"。
一份 HTML 文档具有固定的骨架结构:
<!DOCTYPE html> <!-- 声明:这是 HTML5 文档 -->
<html lang="zh-CN">
<head> <!-- head:给浏览器看的配置,用户不可见 -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>新闻</title> <!-- 浏览器标签页标题 -->
</head>
<body> <!-- body:用户看到的所有内容 -->
</body>
</html>
1.3 常用标签速查
本讲会用到的标签如下:
| 标签 | 用途 |
|---|---|
h1 ~ h3 |
标题 |
ul / li |
无序列表(新闻列表、导航菜单均由它构成) |
img |
图片(自闭合标签,src 属性指定图片路径) |
a |
链接(href 属性指定跳转地址) |
div |
通用容器,用于划分页面区块 |
header / main |
语义化分区标签:页头 / 主体 |
相对路径:../images/1.jpg 中的 .. 表示上一级目录。由于 html 文件位于 tmt 文件夹内,而图片位于上一级的 src/images 中,因此需要先 .. 回到上一级再进入 images。
1.4 从设计稿到代码:页面分区
对照设计稿 设计稿/index_01.jpg,将页面自上而下切分为四个区块:
wrap
├─ header
│ ├─ .header-top 顶栏:左侧 logo,右侧菜单
│ └─ .banner 轮播图(由 Swiper 驱动)
└─ .main
├─ .menu-list 频道菜单(横向滚动)
└─ .news 新闻列表(ul>li,最终由数据动态生成)
编写原则:先骨架后血肉,每完成一个区块就刷新浏览器查看效果,不要全部写完才验证。
2. 实战:新闻首页的页面结构
2.1 创建项目骨架
在 D:\news 下建立以下目录结构(路径中不要出现中文):
D:\news
└─ src
├─ css/ 样式文件
├─ images/ 图片素材(拷贝教师发放的素材包)
├─ js/ JS 库文件(拷贝素材包中的三个库文件)
├─ tmt/index.html 新闻展示页
└─ system/index.html 新闻管理系统页
src/system/index.html 为后台页面,本讲先写入占位内容:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>新闻管理系统</title>
</head>
<body>
<h1>新闻管理系统</h1>
</body>
</html>
注意:目录结构从现在起就按最终形态建立。Day 3 搭建服务器后,整个
src文件夹将被托管到服务器上直接访问。
2.2 编写页面结构
在 src/tmt/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>
</head>
<body>
<div class="wrap">
<!-- ① 顶部导航:logo + 右侧菜单 -->
<header>
<div class="header-top">
<img src="../images/h5_logo.png" alt="logo" />
<div class="nav">
<ul>
<li><a href="#">PRO</a></li>
<li><a href="#">科股宝</a></li>
<li><a href="#">视频</a></li>
<li><a href="#">音频</a></li>
<li><a href="#">EN</a></li>
</ul>
<img src="../images/h5_menu.png" alt="menu" />
</div>
</div>
<!-- ② 轮播图:结构先行,稍后由 Swiper 驱动 -->
<div class="banner swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide"><img src="../images/1.jpg" alt="" /></div>
<div class="swiper-slide"><img src="../images/2.jpg" alt="" /></div>
<div class="swiper-slide"><img src="../images/3.jpg" alt="" /></div>
<div class="swiper-slide"><img src="../images/4.jpg" alt="" /></div>
</div>
<div class="swiper-pagination"></div>
</div>
</header>
<div class="main">
<!-- ③ 频道菜单 -->
<div class="menu-list">
<ul>
<li><a class="active" href="#">推荐</a></li>
<li><a href="#">最新</a></li>
<li><a href="#">专题</a></li>
<li><a href="#">标识人物</a></li>
<li><a href="#">深度</a></li>
<li><a href="#">产业</a></li>
</ul>
</div>
<!-- ④ 新闻列表:先写 1 条假数据用于查看样式,稍后改为 JS 动态生成 -->
<div class="news">
<ul>
<li>
<img src="../images/place_280_210.png" alt="" />
<div class="info">
<h3 class="title">这里是一条假新闻标题,用来占位看样式</h3>
<a class="author" href="#">占位作者</a>
<span class="time">2026-09-07 08:30</span>
</div>
</li>
</ul>
</div>
</div>
</div>
</body>
</html>
2.3 验证页面与开发者工具
使用 Chrome 打开该 html 文件进行预览。开发者工具(F12)中两个必须掌握的面板:
- Elements 面板:点击左上角箭头图标后在页面选取元素,可以直观看到"标签 ↔ 页面元素"的对应关系;
- Console / Network 面板:图片路径错误时 Console 会输出 404 报错,Network 中可以看到具体失败的请求。
排查报错是本课程最重要的基本技能:遇到页面异常,先看 Console 与 Network。
3. CSS 页面布局
3.1 盒模型
页面中每个元素都是一个盒子,从内到外分为四层:content(内容)→ padding(内边距)→ border(边框)→ margin(外边距)。
默认情况下 width 仅指内容区宽度,设置 padding 后盒子会被撑大,导致页面出现横向滚动条。通用解决方式:
box-sizing: border-box; /* 声明的宽度包含 padding,向内计算 */
3.2 浮动与清除浮动
- 新闻列表"左图右文"的排版:
img与.info分别向左右浮动; - 菜单横排:
li逐个float: left; - 副作用:子元素浮动后脱离文档流,父级高度会塌陷。解决方式是给父级添加
overflow: hidden清除浮动影响。
3.3 定位:子绝父相
轮播图标题压在图片左下角,使用的是经典组合——父级相对定位、子级绝对定位:
- 父级(
.swiper-slide)设置position: relative; - 子元素(标题)设置
position: absolute; left: 20px; bottom: 20px; - 遮罩层
.mask同样使用 absolute 铺满整个父级。
3.4 三个高频使用的特效
① 渐变遮罩(将图片底部压暗,保证白色文字可读):
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,.6) 100%);
② 多行文本溢出显示省略号(新闻标题最多显示两行):
display: -webkit-box;
-webkit-box-orient: vertical;
box-orient: vertical; /* 标准属性,补上以消除编辑器黄色警告 */
-webkit-line-clamp: 2;
line-clamp: 2; /* 标准属性,补上以消除编辑器黄色警告 */
overflow: hidden;
注意:单独使用
-webkit-box-orient与-webkit-line-clamp时,VS Code 会给出两条黄色警告,提示"同时定义标准属性"。按上方代码在同一条规则中补上标准的box-orient与line-clamp即可消除警告;前缀写法与标准写法同时存在,不影响显示效果。
③ 横向滚动菜单:容器 overflow-x: auto,ul 不换行 white-space: nowrap,li 转为行级块 display: inline-block。
3.5 实战:编写页面完整样式
先为每个轮播 slide 补充遮罩结构:
<div class="swiper-slide">
<img src="../images/1.jpg" alt="" />
<div class="mask">
<h2>年轻人痴迷的玄学,是一门好生意吗?</h2>
</div>
</div>
然后在 <head> 中添加 <style>,按自上而下的顺序编写(每完成一个区块刷新浏览器,与 设计稿/index_01.jpg 对照):
* { margin: 0; padding: 0; } /* 清除默认边距 */
/* ===== 顶栏 ===== */
.header-top {
width: 100%; height: 50px; padding: 14px 15px;
box-sizing: border-box; /* 阻止 padding 撑大盒子 */
background-color: #fff;
overflow: hidden; /* 清除子级浮动的影响 */
}
.header-top img { width: 60px; float: left; }
.header-top .nav { float: right; font-size: 16px; outline: none; }
.header-top .nav ul { list-style: none; float: left; }
.header-top .nav ul li { float: left; margin-right: 15px; }
.header-top .nav ul li a { color: #333; font-size: 14px; text-decoration: none; font-weight: bold; }
.header-top .nav img { float: right; width: 18px; margin-top: 1px; }
/* ===== 新闻列表:左图右文 ===== */
.news ul { list-style: none; }
.news ul li {
border-bottom: 1px solid #eeeeee; padding: 20px 15px;
overflow: hidden; /* 清除浮动,撑起 li 的高度 */
}
.news ul li img { width: 31.4vw; border-radius: 5px; float: left; }
.news ul li .info { float: right; width: 57vw; }
.news ul li .info .title {
font-weight: normal; color: #333; font-size: 16px;
min-height: 2.63em; /* 一行标题时,作者与时间的位置不上浮 */
display: -webkit-box; -webkit-box-orient: vertical; box-orient: vertical;
-webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
word-wrap: break-word; white-space: normal;
}
.news ul li .info .author { color: #999; font-size: 11px; display: block; margin: 10px 0; }
.news ul li .info .time { color: #999; font-size: 11px; }
/* ===== 频道菜单:横向滚动 ===== */
.main .menu-list { background: #f4f4f4; box-sizing: border-box; overflow-x: auto; padding: 15px; }
.main .menu-list ul { list-style: none; white-space: nowrap; }
.main .menu-list ul li { display: inline-block; margin-right: 25px; }
.main .menu-list ul li a { color: #999; font-size: 16px; text-decoration: none; }
.main .menu-list ul li a.active { color: #333; }
/* ===== 轮播图:遮罩 + 标题 + 分页器 ===== */
.swiper-container { width: 100vw; height: 60vw; position: relative; }
.swiper-container .swiper-slide { position: relative; }
.swiper-container .swiper-slide img { width: 100%; }
.swiper-container .swiper-slide .mask {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,.6) 100%);
}
.swiper-container .swiper-slide h2 {
color: #fff; position: absolute; left: 20px; bottom: 20px;
font-weight: normal; font-size: 16px;
display: -webkit-box; -webkit-box-orient: vertical; box-orient: vertical;
-webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
.swiper-pagination-bullet { width: 15px; height: 2px; background: #fff; border-radius: 2px; opacity: .49; }
.swiper-pagination-bullet-active { width: 30px; background: #49c2c8; opacity: 1; }
3.6 验证样式效果
- 缩放浏览器窗口,布局不发生错乱;
- 标题超过两行时出现省略号;
- 菜单可以横向滚动,选中项文字为深色。
4. 界面特效与数据驱动渲染
4.1 引入第三方库
将素材包中的 jquery-3.5.1.min.js、axios.min.js、swiper.min.js 拷入 src/js,swiper.min.css 拷入 src/css,然后按以下顺序引入:
<!-- css 引入放在 head 中 -->
<link rel="stylesheet" href="../css/swiper.min.css" />
<!-- js 引入放在 body 底部,注意顺序 -->
<script src="../js/jquery-3.5.1.min.js"></script>
<script src="../js/axios.min.js"></script>
<script src="../js/swiper.min.js"></script>
三个库各司其职:Swiper 负责轮播图、jQuery 负责页面操作、axios 负责请求服务器数据(axios 在 Day 3 正式使用,本讲先完成引入)。
4.2 Swiper 轮播图
var mySwiper = new Swiper(".swiper-container", {
loop: true, // 循环播放
autoplay: true, // 自动播放,手动滑动后停止
pagination: { el: ".swiper-pagination" }
});
验证:轮播图自动切换,分页器显示为蓝色长条。
4.3 数组、forEach 与模板字符串
动态渲染列表涉及三个新知识:
- 数组:
[]中存放多条数据,每条是一个对象{ title: ..., author: ... }; - forEach 循环:数组中有几个元素就执行几次,
item代表当前正在处理的那一条; - 模板字符串:用反引号
`包裹的字符串,内部可用${变量}直接嵌入数据。
4.4 实战:用假数据动态渲染新闻列表
删除 2.2 中写死的占位 li,改为脚本生成:
var fakeNews = [
{ title: "短缺、被涨价、停产,汽车芯片被\"卡脖子之痛\"", author: "连线Insight", date: "2020-06-20 11:40", imgSrc: "../images/place_280_210.png" },
{ title: "组织中台,开启平台型组织的真正钥匙", author: "穆胜", date: "2020-06-20 11:38", imgSrc: "../images/place_280_210.png" },
{ title: "净利下滑超90%,拯救中国真皮鞋王的,难道只有狂犬疫苗?", author: "深氢新消费", date: "2020-06-20 11:39", imgSrc: "../images/place_280_210.png" }
];
fakeNews.forEach(function (item) {
var li = $("<li class='slide-news'>");
li.html(`<img src="${item.imgSrc}" alt=""><div class="info"><h3 class="title">${item.title}</h3><a class="author" href="#">${item.author}</a><span class="time">${item.date}</span></div>`);
$(".news ul").append(li);
});
关键点:当前数组中是假数据;到 Day 3 仅需将数组替换为服务器请求,页面代码几乎无需改动——这就是数据驱动页面。
5. 补充章节:HTML / CSS / JS 其他基础
除新闻首页用到的标签与样式外,还有一些贯穿整个项目的基础知识。本节配套演示文件为 testcode/day1other.html,在浏览器中打开即可逐段对照演示,页面中的示例均可直接交互。
5.1 HTML 常用标签补充
① 标题、段落与文本
- 标题共 6 级:
h1最大、h6最小,一个页面通常只用一个h1; p是段落,br换行,hr是水平分隔线;strong加粗强调,em倾斜强调。
② 两种容器:div 与 span
| 标签 | 类型 | 特点 | 项目中的例子 |
|---|---|---|---|
div |
块级 | 独占一行,用于划分区块 | 新闻首页的 header、main |
span |
行内 | 只占内容本身大小 | 一段文字中的局部变色 |
③ 列表:li 必须写在 ul 或 ol 里面
<!-- 无序列表:每项前是小圆点 -->
<ul>
<li>无序列表第一项</li>
<li>无序列表第二项</li>
</ul>
<!-- 有序列表:每项前是数字 -->
<ol>
<li>有序列表第一项</li>
<li>有序列表第二项</li>
</ol>
注意(易错):
ul/ol中不能直接写文字,列表内容必须放在li里;"有序列表"的标签是ol,li只是列表项标签。
④ 表格:table → tr(行)→ th(表头)/ td(内容)
后台管理系统的新闻表格就是这一结构:
<table>
<tr>
<th>序号</th>
<th>标题</th>
</tr>
<tr>
<td>1</td>
<td>这是一条测试新闻</td>
</tr>
</table>
⑤ 图片的 alt 属性
alt 是图片加载失败时的提示文字,必须书写:
<img src="../课表.png" alt="课程表" width="300" />
⑥ 链接在新标签页打开
<a href="https://eogee.com" target="_blank">eogee官网</a>
⑦ 表单控件
Day 4 后台"添加新闻"的模态框就是一个表单。常用控件如下:
| 控件 | 写法 | 说明 |
|---|---|---|
| 文本框 | <input type="text" placeholder="请输入用户名" /> |
placeholder 是占位提示 |
| 密码框 | <input type="password" /> |
输入显示为圆点 |
| 单选 | <input type="radio" name="gender" /> |
同一组 name 必须相同才能单选 |
| 多选 | <input type="checkbox" /> |
可勾选多个 |
| 下拉框 | <select><option>推荐</option></select> |
选项写在 option 里 |
| 文本域 | <textarea rows="3"></textarea> |
多行输入 |
| 按钮 | <button>保存</button> |
点击触发事件 |
5.2 CSS 补充:三种引入方式、选择器与盒模型
① 三种引入方式
| 方式 | 写法 | 使用场景 |
|---|---|---|
| 行内样式 | <p style="color: red;"> |
单个元素临时调整 |
| 内部样式 | <style> p { color: red; } </style> |
单个页面(Day 1 新闻首页) |
| 外部样式 | <link rel="stylesheet" href="xx.css" /> |
多页面共用(swiper.min.css) |
② 四种常用选择器
p { color: #333; } /* 标签选择器:选中所有 p */
.box { background: #49c2c8; } /* 类选择器(.):class="box" 的元素,可重复使用 */
#main-title { color: #20a0ff; } /* id 选择器(#):id="main-title",全页唯一 */
.menu li { color: #666; } /* 后代选择器(空格):.menu 里面的所有 li */
.menu li:hover { color: #20a0ff; cursor: pointer; } /* 伪类:鼠标移入时生效 */
③ 盒模型对照实验
两个盒子声明的 width 都是 200px,内边距 20px、边框 5px:
.box-model {
width: 200px;
padding: 20px; /* 内边距 */
border: 5px solid #333; /* 边框 */
margin: 20px; /* 外边距 */
background: #ffe8c8;
}
.box-model-border-box {
width: 200px;
padding: 20px;
border: 5px solid #e2483d;
box-sizing: border-box; /* 宽度把 padding、border 都算进去 */
background: #d8f0d8;
}
验证:第一个盒子实际总宽 250px(200 + 20×2 + 5×2),第二个固定 200px——这正是 3.1 节"padding 撑大盒子"的直观证明。
5.3 JavaScript 补充:变量、函数、事件与 DOM
JS 代码写在 body 底部的 <script> 中,等页面元素加载完成后再执行。四个核心概念:
| 概念 | 写法示例 | 作用 |
|---|---|---|
| 变量 | var name = document.getElementById("nameInput").value; |
存储数据 |
| 函数 | function sayHello() { ... } |
打包一段可复用的代码 |
| DOM | document.getElementById("xx") |
通过 id 找到页面元素 |
| 事件 | onclick="sayHello()"(写在按钮上) |
点击时执行函数 |
常用 DOM 操作:
.value读取输入框内容;.innerText读写元素文字;.innerHTML写入标签(动态生成列表时使用);.style.backgroundColor = "#49c2c8"修改样式(JS 中样式名用驼峰写法);console.log("...")打印到控制台(F12查看),调试代码最常用。
综合示例:点击按钮生成新闻列表(数据驱动页面的最小版)
var newsArr = ["新闻标题一", "新闻标题二", "新闻标题三", "新闻标题四", "新闻标题五"];
function renderNews() {
var listHtml = "";
// for 循环:i 从 0 数到 4,共执行 5 次
for (var i = 0; i < newsArr.length; i++) {
listHtml = listHtml + "<li>第" + (i + 1) + "条:" + newsArr[i] + "</li>";
}
document.getElementById("newsList").innerHTML = listHtml;
}
<button onclick="renderNews()">生成 5 条新闻标题</button>
<ul id="newsList"></ul>
验证:点击按钮后页面出现 5 条列表项。回顾 4.4 节——把这里的数组换成 res.data.list(服务器返回的数据),就是新闻首页的真实渲染。
演示文件
testcode/day1other.html中还有三个可交互示例(点击打招呼、点击计数器、成绩等级判断),分别演示输入框读写、变量自增与if / else if / else多分支判断,课堂上可逐一演示。
6. 动手练习
6.1 任务清单
| 层级 | 任务 |
|---|---|
| 基础必做 | ① 页面结构与样式完整,与设计稿基本一致;② Swiper 自动轮播、分页器为蓝色长条;③ 列表渲染出 5 条以上假数据 |
| 进阶选做 | ① 点击频道菜单切换 active 高亮(提示:点击时先移除所有 active,再为当前点击的 a 添加);② 标题 hover 变色;③ 向数组中添加 2 条自己喜欢的新闻 |
| 挑战加分 | ① 菜单栏吸顶(可查阅 position: sticky);② 为列表项添加淡入动画;③ 思考:数组换成 axios.get(...) 后代码需要修改哪几处? |
6.2 自查清单
- [ ] 缩放浏览器窗口,布局不错乱
- [ ] 图片全部显示、不变形
- [ ] 标题超过两行出现省略号
- [ ] F12 Console 中无红色报错
6.3 常见问题排查
| 现象 | 常见原因 |
|---|---|
| 图片不显示,Console 报 404 | 路径写错、文件名大小写不一致 |
| 轮播图不动 | swiper.min.js 未引入,或类名写错(应为 swiper-container) |
| 布局塌陷 | 未清除浮动(父级添加 overflow: hidden) |
| 修改样式后无变化 | 按 Ctrl + F5 强制刷新浏览器缓存 |
遇到问题的处理顺序:先自行查看 Console / Network 排查 10 分钟 → 再请教邻座 → 最后举手求助老师。互相帮助计入平时成绩。
7. 环境准备(下一讲:MongoDB 数据库)
下一讲进入数据库环节,请在课程开始前逐项完成以下准备,并在群内打卡"Day2 环境打卡 + 卡在第几步":
- 安装 Node.js LTS 版本:https://nodejs.org/en/download ,安装完成后打开 cmd 执行
node -v、npm -v,能输出版本号即为成功; - 安装 MongoDB Community:https://www.mongodb.com/try/download/community
- 安装 MongoDB Compass(数据库可视化管理工具):https://www.mongodb.com/try/download/compass
- 确认 VSCode、Chrome 可正常使用(本讲已在使用的可跳过);
- 验证数据库安装:cmd 执行
mongod --version,输出版本信息即为成功; - 在 D 盘创建
D:\mongodb\data文件夹(数据库文件的存放位置),路径中不要出现中文; - 将教师发放的新闻数据文件
news.newsList.json拷贝到本机(建议存放于 D 盘根目录); - 思考题(写入日志):新闻一直写死在 HTML 中,会带来哪些问题?
8. 学习日志
每讲结束前填写以下日志(计入过程性考核):
# 学习日志 Day 1 姓名:____ 日期:____
1. 本讲我学会了:____(3 条以内,写具体的标签/属性/代码)
2. 我遇到的问题与解决:____(什么现象 → 怎么定位 → 怎么解决;未解决也如实记录)
3. 我想进一步弄明白:____
4. 环境准备打卡:已完成 / 未完成(卡在哪一步:____)
9. 本讲小结
- 页面构成:HTML 骨架 + CSS 外观 + JS 交互;
- 布局三板斧:盒模型(border-box)、浮动(float 配合清除浮动)、定位(子绝父相);
- 核心思维:列表不应写死在页面中,而应由数据循环生成——数据驱动页面;
- 基础扩展:表格与表单结构、CSS 三种引入方式与常用选择器、盒模型对照实验、JS 的变量 / 函数 / DOM 事件三件套(演示文件
testcode/day1other.html)。
举手提问