跳转到内容

HTML + CSS + JavaScript 基础:网页的三件套

完成时间:约 45 分钟。读完后你能理解网页是怎么构成的,看懂 AI 生成的前端代码。

任何你在浏览器里看到的网页,都是由三样东西组成的:

角色技术类比
骨架HTML一栋房子的钢筋结构——决定有几个房间、门在哪、窗户在哪
装修CSS墙面颜色、地板材质、灯光——决定好不好看
电器JavaScript电灯开关、电梯、自动门——决定能不能互动

三者缺一不可。HTML 没有 CSS 就是毛坯房,没有 JavaScript 就是静态海报。


HTML(HyperText Markup Language)用”标签”来描述页面内容。每个标签告诉浏览器”这是什么东西”。

<标签名>内容</标签名>

标签成对出现:开始标签 <h1> + 结束标签 </h1>。有些标签不需要关闭,比如 <img /><input />

结构类

<div>...</div> <!-- 通用容器,最常用的"盒子" -->
<header>...</header> <!-- 页面头部(导航栏) -->
<main>...</main> <!-- 主要内容区 -->
<footer>...</footer> <!-- 页面底部 -->
<section>...</section> <!-- 一个内容区块 -->
<nav>...</nav> <!-- 导航区域 -->

文本类

<h1>一级标题</h1> <!-- 最大的标题,页面通常只有一个 -->
<h2>二级标题</h2> <!-- 次级标题 -->
<h3>三级标题</h3> <!-- 更小的标题,最多到 h6 -->
<p>这是一段文字</p> <!-- 段落 -->
<span>行内文字</span> <!-- 一小段文字,不换行 -->
<a href="/about">关于</a> <!-- 链接,点击跳转 -->

表单类(用户输入)

<input type="text" /> <!-- 文本输入框 -->
<input type="password" /> <!-- 密码输入框(内容显示为点) -->
<input type="email" /> <!-- 邮箱输入框 -->
<textarea>...</textarea> <!-- 多行文本框 -->
<select> <!-- 下拉选择 -->
<option>选项1</option>
<option>选项2</option>
</select>
<button>提交</button> <!-- 按钮 -->

媒体类

<img src="photo.jpg" alt="照片描述" /> <!-- 图片 -->
<video src="demo.mp4"></video> <!-- 视频 -->

列表类

<ul> <!-- 无序列表(圆点) -->
<li>苹果</li>
<li>香蕉</li>
</ul>
<ol> <!-- 有序列表(数字) -->
<li>第一步</li>
<li>第二步</li>
</ol>

标签像”套娃”一样嵌套,形成层级结构:

<div> <!-- 外层容器 -->
<h2>用户列表</h2> <!-- 标题 -->
<ul> <!-- 列表 -->
<li>小明</li> <!-- 列表项 -->
<li>小红</li>
</ul>
</div>

标签可以有”属性”,写在开始标签里:

<a href="https://example.com" target="_blank">外部链接</a>
<!-- ^^^^ ^^^^^^^^^^^^^^
链接地址 在新标签页打开 -->
<img src="avatar.jpg" alt="用户头像" width="100" />
<!-- ^^^ ^^^ ^^^^^
图片地址 描述文字 宽度 -->
<input type="text" placeholder="请输入用户名" required />
<!-- ^^^^ ^^^^^^^^^^^ ^^^^^^^^
输入类型 提示文字 必填 -->
<!DOCTYPE html> <!-- 声明这是 HTML5 -->
<html lang="zh"> <!-- 页面根元素,中文 -->
<head> <!-- 页面元信息(用户看不到) -->
<meta charset="UTF-8"> <!-- 字符编码 -->
<title>我的记账工具</title> <!-- 浏览器标签页标题 -->
</head>
<body> <!-- 页面正文(用户看到的) -->
<header>
<h1>记账工具</h1>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
</header>
<main>
<h2>今日支出</h2>
<p>总计:¥128.50</p>
</main>
</body>
</html>

你不需要从零写 HTML。 AI 会帮你生成。你需要的是看到 <div><button><input> 时知道它们是什么。


CSS(Cascading Style Sheets)控制 HTML 元素的外观:颜色、大小、位置、间距、动画等。

选择器 {
属性: 值;
属性: 值;
}

示例:

h1 {
color: red; /* 文字颜色:红色 */
font-size: 24px; /* 字体大小:24像素 */
margin-bottom: 16px; /* 下方间距:16像素 */
}

选择器:告诉 CSS “给谁加样式”

Section titled “选择器:告诉 CSS “给谁加样式””
/* 标签选择器 — 所有 <p> 标签 */
p { color: gray; }
/* class 选择器 — 所有 class="title" 的元素 */
.title { font-size: 20px; }
/* id 选择器 — id="header" 的那一个元素 */
#header { background: blue; }
/* 后代选择器 — .card 里面的所有 <p> */
.card p { color: white; }

在 HTML 里用 classid 对应:

<h2 class="title">这是标题</h2>
<div id="header">这是头部</div>

文字相关

color: #333; /* 文字颜色 */
font-size: 16px; /* 字体大小 */
font-weight: bold; /* 粗体 */
text-align: center; /* 文字居中 */
line-height: 1.5; /* 行高 */

盒模型:每个元素都是一个”盒子”

┌─────────── margin(外边距,元素和外部的间距)────────────┐
│ ┌──────── border(边框)────────┐ │
│ │ ┌───── padding(内边距,内容和边框的间距)──┐ │
│ │ │ │ │
│ │ │ content(内容) │ │
│ │ │ │ │
│ │ └─────────────────────────────────────────┘ │
│ └──────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
.card {
padding: 16px; /* 内边距:内容到边框的距离 */
margin: 20px; /* 外边距:这个盒子和其他盒子的距离 */
border: 1px solid #ddd; /* 边框:1像素、实线、灰色 */
border-radius: 8px; /* 圆角 */
}

背景和尺寸

background-color: #f5f5f5; /* 背景颜色 */
width: 100%; /* 宽度(占满父容器) */
height: 200px; /* 高度 */
max-width: 800px; /* 最大宽度 */

显示和隐藏

display: none; /* 完全隐藏(不占位置) */
display: block; /* 块级元素(独占一行) */
display: flex; /* 弹性布局(最常用的排列方式) */
visibility: hidden; /* 隐藏但仍占位置 */
opacity: 0.5; /* 半透明 */

Flexbox 布局(现代 CSS 最核心的布局方式)

Section titled “Flexbox 布局(现代 CSS 最核心的布局方式)”

AI 生成的前端代码中 display: flex 无处不在。它的作用是让子元素灵活排列

.container {
display: flex; /* 开启 flex 布局 */
flex-direction: row; /* 横排(默认)。column = 竖排 */
justify-content: center; /* 主轴居中(横向居中) */
align-items: center; /* 交叉轴居中(纵向居中) */
gap: 16px; /* 子元素之间的间距 */
}

常见的 Flexbox 组合:

/* 水平居中 */
display: flex; justify-content: center;
/* 垂直 + 水平居中(完美居中) */
display: flex; justify-content: center; align-items: center;
/* 两端对齐(左右各一个) */
display: flex; justify-content: space-between;
/* 竖着排列 */
display: flex; flex-direction: column;
/* 默认样式(手机优先) */
.sidebar {
display: none; /* 手机上隐藏侧边栏 */
}
/* 屏幕宽度 >= 768px 时(平板/电脑) */
@media (min-width: 768px) {
.sidebar {
display: block; /* 大屏幕上显示侧边栏 */
}
}

传统 CSS 写在单独文件里,Tailwind 把样式直接写在标签的 class 属性上。AI 生成的代码几乎都用 Tailwind。

<!-- 传统 CSS:写样式文件 + 给标签起 class 名 -->
<div class="card">
<h2 class="card-title">标题</h2>
</div>
<!-- Tailwind:样式就在标签上,不需要额外的 CSS 文件 -->
<div class="bg-white p-6 rounded-lg shadow-md">
<h2 class="text-xl font-bold text-gray-800">标题</h2>
</div>

Tailwind 类名速查表

类别类名示例含义
文字大小text-sm / text-lg / text-2xl小 / 大 / 特大
文字颜色text-red-500 / text-gray-700红色 / 深灰
文字粗细font-bold / font-normal粗体 / 正常
背景bg-white / bg-blue-100白色 / 浅蓝
内边距p-4 / px-6 / py-2四周 / 左右 / 上下
外边距m-4 / mt-2 / mb-8四周 / 上方 / 下方
圆角rounded / rounded-lg / rounded-full小圆角 / 大圆角 / 圆形
阴影shadow / shadow-md / shadow-lg小阴影 / 中阴影 / 大阴影
布局flex / grid / hidden弹性 / 网格 / 隐藏
排列justify-center / items-center主轴居中 / 交叉轴居中
间距gap-4 / space-x-2flex 间距 / 水平间距
宽度w-full / w-1/2 / w-64满宽 / 一半 / 固定宽度
响应式前缀md:flex / lg:text-xl中屏以上生效 / 大屏以上生效
状态hover:bg-blue-600 / focus:ring-2鼠标悬停 / 获得焦点

命名规则总结:

  • 方向t(top) b(bottom) l(left) r(right) x(左右) y(上下)
  • 大小:数字越大越大(p-1 < p-4 < p-8
  • 颜色:颜色名 + 深度(gray-100 最浅,gray-900 最深)

不用背。AI 会自动选择合适的 Tailwind 类名。你只需要看到 bg-blue-500 text-white p-4 时,知道这是”蓝色背景、白色文字、内边距 4”。


JavaScript(简称 JS)让网页从”静态海报”变成”可交互应用”。用户点击按钮、输入文字、滚动页面时发生的所有事,都是 JS 在控制。

// 三种声明方式
const name = "小明" // const — 不可变(推荐,AI 默认用这个)
let count = 0 // let — 可变
var old = "别用" // var — 旧写法,现在不推荐
// 常见数据类型
const age = 25 // 数字
const name = "小明" // 字符串
const isAdmin = true // 布尔值
const items = ["苹果", "香蕉"] // 数组(列表)
const user = { name: "小明", age: 25 } // 对象(字典)
// 传统写法
function greet(name) {
return "你好," + name
}
// 箭头函数(AI 生成的代码几乎都用这种)
const greet = (name) => {
return "你好," + name
}
// 单行箭头函数(更简洁)
const greet = (name) => "你好," + name
// 调用
greet("小明") // → "你好,小明"
if (score >= 90) {
console.log("优秀")
} else if (score >= 60) {
console.log("及格")
} else {
console.log("不及格")
}
// 三元表达式(AI 经常用的简写)
const result = score >= 60 ? "及格" : "不及格"
// 条件 ? 真 : 假
const fruits = ["苹果", "香蕉", "橙子"]
// for 循环(传统方式)
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i])
}
// forEach(遍历数组)
fruits.forEach(fruit => console.log(fruit))
// map(把每项转换成新的东西,AI 最常用)
const upper = fruits.map(f => f + "🍎")
// → ["苹果🍎", "香蕉🍎", "橙子🍎"]
// filter(筛选)
const long = fruits.filter(f => f.length > 2)
// → ["苹果", "香蕉", "橙子"](中文都是两字,这里只是示例)
// find(找第一个符合条件的)
const found = fruits.find(f => f === "香蕉")
// → "香蕉"
const user = {
name: "小明",
age: 25,
role: "admin"
}
// 读取
user.name // → "小明"
user["role"] // → "admin"
// 修改
user.age = 26
// 解构(AI 代码里非常常见)
const { name, age } = user
// 等价于:const name = user.name; const age = user.age;

前端经常需要向后端请求数据,这是异步操作——发出请求后不会傻等,而是继续做别的事,等数据回来了再处理。

// async/await 写法(最现代、最清晰)
async function loadUsers() {
const response = await fetch('/api/users') // 发请求,等响应
const data = await response.json() // 解析 JSON
console.log(data) // 使用数据
}
// 错误处理
async function loadUsers() {
try {
const response = await fetch('/api/users')
if (!response.ok) { // 状态码不是 200
throw new Error('请求失败: ' + response.status)
}
const data = await response.json()
return data
} catch (error) {
console.error('出错了:', error)
}
}
// 找到元素
const btn = document.getElementById('submit-btn')
const items = document.querySelectorAll('.item')
// 修改内容
btn.textContent = '加载中...'
// 监听事件(用户点击时执行某个操作)
btn.addEventListener('click', () => {
alert('你点击了按钮')
})

注意:在 React 项目中你几乎不会直接操作 DOM,React 会帮你管理。但了解 DOM 概念有助于理解 AI 写的代码。


本课程前端使用 React。它不是一种新语言,而是用 JavaScript 写 UI 的一套组织方式

React 把页面拆成一个个组件,每个组件是一个独立的 UI 块。

整个页面
├── Header 组件(导航栏)
├── Sidebar 组件(侧边栏)
└── Main 组件(主内容)
├── SearchBar 组件(搜索框)
└── ExpenseList 组件(支出列表)
├── ExpenseItem 组件(单条支出)
├── ExpenseItem 组件
└── ExpenseItem 组件
// 最简单的组件
function Welcome() {
return <h1>欢迎回来</h1>
}
// 接收参数的组件
function Welcome({ name }) {
return <h1>欢迎回来,{name}</h1>
}
// 使用组件(像 HTML 标签一样)
<Welcome name="小明" />

{ } 里面可以写任何 JavaScript 表达式——变量、计算、函数调用都行。

React 组件里的 “HTML” 其实是 JSX——一种让你在 JavaScript 里写 HTML 的语法。

function ExpenseItem({ category, amount, date }) {
return (
<div className="flex justify-between p-4 border-b">
<div>
<span className="font-bold">{category}</span>
<span className="text-gray-500 ml-2">{date}</span>
</div>
<span className="text-red-500">{amount}</span>
</div>
)
}

注意几个 JSX 和 HTML 的区别:

  • classclassName(因为 class 是 JS 保留字)
  • 样式用 Tailwind 类名
  • {表达式} 可以嵌入任何 JS 值
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
// ^^^^^ ^^^^^^^^ ^
// 当前值 修改函数 初始值
return (
<div>
<p>你点击了 {count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
)
}

useState 的规则:

  • 调用 setCount(新值)count 变成新值 → 页面自动更新
  • 不要直接改 count = 5,必须用 setCount(5)
import { useState, useEffect } from 'react'
function UserList() {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
// 这个函数在组件加载时自动执行
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data) // 把数据存到状态
setLoading(false) // 加载完成
})
}, []) // [] 表示只执行一次(组件首次加载时)
if (loading) return <p>加载中...</p>
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
function LoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const handleSubmit = async (e) => {
e.preventDefault() // 阻止表单默认提交行为
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
})
const data = await response.json()
// 处理登录结果...
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)} {/* 输入时更新状态 */}
placeholder="请输入邮箱"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">登录</button>
</form>
)
}
function Dashboard({ user }) {
return (
<div>
{/* 方式1:&& 短路 — 条件为真才渲染 */}
{user.role === 'admin' && <AdminPanel />}
{/* 方式2:三元表达式 — 二选一 */}
{user.isLoggedIn ? <Welcome /> : <LoginForm />}
{/* 方式3:提前 return */}
{!user && <p>请先登录</p>}
</div>
)
}
function ExpenseList({ expenses }) {
return (
<div>
{expenses.map(item => (
<ExpenseItem
key={item.id} // key 是必须的,用唯一标识
category={item.category}
amount={item.amount}
date={item.date}
/>
))}
</div>
)
}

下面是一个”支出记录”页面的完整组件,把 HTML + CSS(Tailwind) + JS(React) 都用上了:

import { useState, useEffect } from 'react'
function ExpensePage() {
// ---- JavaScript 部分:状态和逻辑 ----
const [expenses, setExpenses] = useState([])
const [total, setTotal] = useState(0)
useEffect(() => {
fetch('/api/expenses')
.then(res => res.json())
.then(data => {
setExpenses(data)
const sum = data.reduce((acc, item) => acc + item.amount, 0)
setTotal(sum)
})
}, [])
const handleDelete = async (id) => {
await fetch(`/api/expenses/${id}`, { method: 'DELETE' })
setExpenses(expenses.filter(e => e.id !== id))
}
// ---- HTML + CSS(Tailwind) 部分:界面 ----
return (
<div className="max-w-2xl mx-auto p-6"> {/* 居中容器 */}
<h1 className="text-2xl font-bold mb-4">支出记录</h1> {/* 标题 */}
<div className="bg-blue-50 p-4 rounded-lg mb-6"> {/* 汇总卡片 */}
<p className="text-gray-600">本月总支出</p>
<p className="text-3xl font-bold text-blue-600">
¥{total.toFixed(2)}
</p>
</div>
<div className="space-y-2"> {/* 列表 */}
{expenses.map(item => (
<div key={item.id}
className="flex justify-between items-center p-3 bg-white
rounded border hover:shadow-md transition">
<div>
<span className="font-medium">{item.category}</span>
<span className="text-gray-400 text-sm ml-2">{item.date}</span>
</div>
<div className="flex items-center gap-3">
<span className="text-red-500 font-bold">{item.amount}</span>
<button
onClick={() => handleDelete(item.id)}
className="text-gray-400 hover:text-red-500 text-sm"
>
删除
</button>
</div>
</div>
))}
</div>
</div>
)
}
对应部分做了什么
JS 逻辑useState、useEffect、handleDelete管理数据、请求后端、处理删除
HTML 结构div、h1、p、span、button定义页面有哪些元素
CSS 样式Tailwind 类名控制大小、颜色、布局、间距

三者混在一起写,但职责分明。看到 className="..." 就是样式,看到 {...} 就是逻辑,其余是结构。


React 组件的 return (...) 就是页面长什么样。先看 return 理解界面结构,再往上看逻辑。

这段 React 代码在做什么?帮我逐行解释:
[粘贴代码]

看到一串不认识的类名,直接问:

Tailwind CSS 里 "flex justify-between items-center p-3" 分别是什么意思?
你看到的代码它在做什么
useState([])初始化一个空数组状态
useEffect(() => { fetch... }, [])组件加载时请求数据
{items.map(x => <Comp key={x.id} />)}循环渲染列表
{condition && <Component />}条件成立才显示
{a ? <A /> : <B />}条件为真显示 A,否则显示 B
onChange={(e) => setValue(e.target.value)}输入框内容变化时更新状态
onSubmit={handleSubmit}表单提交时触发函数
className="flex ..."Tailwind 样式
async/await + fetch请求后端 API

技术核心作用你需要掌握到什么程度
HTML定义页面有什么元素看到 <div><input><button> 知道是什么
CSS / Tailwind控制元素的样式看到 bg-blue-500 p-4 知道是蓝色背景、有内边距
JavaScript控制交互和逻辑看懂变量、函数、if/else、map、async/await
React用组件化方式写 UI看懂 useState、useEffect、JSX、事件处理

你不需要写这些代码,AI 会帮你写。 但你需要:

  1. 看懂 AI 写了什么
  2. 判断结构和逻辑是否合理
  3. 发现明显错误时能告诉 AI 去改

下一步:回到主线,开始 Step 1:思考框架