HTML、CSS 和 JavaScript 是构成网页的三项基础技术:HTML 决定页面结构,CSS 决定页面外观,JavaScript 负责页面交互。本讲将从零开始,完成新闻系统中新闻展示页的页面重构,依次介绍 Web 应用的基本架构、HTML 页面结构、CSS 页面布局,以及基于 Swiper 与 jQuery 的界面特效和数据驱动渲染,最终产出一个与设计稿一致的新闻首页。

《企业技能实习》· 新闻系统全栈项目实训

前置讲义

  • 无,本讲是全栈项目实训的第一讲。

资源下载

  • 前端素材包(images 图片素材、jquery/swiper/axios 库文件),由教师统一发放
  • 页面设计稿:设计稿/index_01.jpg
  • 成品参考图:成品效果图/index.png

1. Web 应用基础概念

1.1 浏览器、服务器与数据库

网页上显示的新闻并不存放在浏览者的电脑里。当用户打开一个新闻网站时,数据的流动过程如下:

  1. 浏览器(Browser)向服务器(Server)发出请求:"我需要新闻列表";
  2. 服务器接收请求,到数据库(Database)中取出数据;
  3. 服务器将数据返回给浏览器,浏览器将其渲染为用户看到的页面。
浏览器(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-orientline-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.jsaxios.min.jsswiper.min.js 拷入 src/jsswiper.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 里;"有序列表"的标签是 olli 只是列表项标签。

④ 表格: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 环境打卡 + 卡在第几步":

  1. 安装 Node.js LTS 版本:https://nodejs.org/en/download ,安装完成后打开 cmd 执行 node -vnpm -v,能输出版本号即为成功;
  2. 安装 MongoDB Community:https://www.mongodb.com/try/download/community
  3. 安装 MongoDB Compass(数据库可视化管理工具):https://www.mongodb.com/try/download/compass
  4. 确认 VSCode、Chrome 可正常使用(本讲已在使用的可跳过);
  5. 验证数据库安装:cmd 执行 mongod --version,输出版本信息即为成功;
  6. 在 D 盘创建 D:\mongodb\data 文件夹(数据库文件的存放位置),路径中不要出现中文
  7. 将教师发放的新闻数据文件 news.newsList.json 拷贝到本机(建议存放于 D 盘根目录);
  8. 思考题(写入日志):新闻一直写死在 HTML 中,会带来哪些问题?

8. 学习日志

每讲结束前填写以下日志(计入过程性考核):

# 学习日志 Day 1  姓名:____  日期:____
1. 本讲我学会了:____(3 条以内,写具体的标签/属性/代码)
2. 我遇到的问题与解决:____(什么现象 → 怎么定位 → 怎么解决;未解决也如实记录)
3. 我想进一步弄明白:____
4. 环境准备打卡:已完成 / 未完成(卡在哪一步:____)

9. 本讲小结

  • 页面构成:HTML 骨架 + CSS 外观 + JS 交互;
  • 布局三板斧:盒模型(border-box)、浮动(float 配合清除浮动)、定位(子绝父相);
  • 核心思维:列表不应写死在页面中,而应由数据循环生成——数据驱动页面;
  • 基础扩展:表格与表单结构、CSS 三种引入方式与常用选择器、盒模型对照实验、JS 的变量 / 函数 / DOM 事件三件套(演示文件 testcode/day1other.html)。