HTML、CSS、JavaScript是如何配合的#
从实例起手:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>单文件计数器示例 (这里是网页tab的标题)</title>
<style>
/* ID 选择器 (#): 匹配 id="counter-app" 的 DOM 节点
作用:设置最外层容器的整体布局
*/
#counter-app {
text-align: center; /* 内部元素居中对齐 */
margin-top: 50px; /* 顶部外边距留白 50 像素 */
font-family: sans-serif; /* 使用无衬线字体 */
}
/* 作用:定义数字文本的颜色和粗细 */
#count-display {
color: #007bff; /* 字体颜色设为十六进制的蓝色 */
font-weight: bold; /* 字体加粗 */
}
/* 作用:定义按钮的默认外观 */
#increment-btn {
padding: 10px 20px; /* 按钮内边距 (上下10px, 左右20px) */
font-size: 16px; /* 字体大小 */
background-color: #28a745;/* 背景色设为绿色 */
color: white; /* 文字颜色设为白色 */
border: none; /* 移除默认的边框 */
border-radius: 4px; /* 边框圆角 */
cursor: pointer; /* 鼠标悬浮时指针变为“手型” */
}
/* 伪类选择器 (:hover)
作用:定义鼠标悬浮在按钮上时的交互反馈状态
*/
#increment-btn:hover {
background-color: #218838; /* 颜色加深 */
}
</style>
</head>
<body>
<div id="counter-app">
<h1>当前计数:<span id="count-display">0</span></h1>
<button id="increment-btn">点击加一</button>
</div>
<script>
// 1. 获取 DOM 节点引用 (在内存中找到对应的 HTML 元素)
// 这类似于在 Java 中通过依赖注入获取某个 Bean 的实例
const displayElement = document.getElementById('count-display');
const buttonElement = document.getElementById('increment-btn');
// 2. 初始化局部状态 (State)
// 这是存储在浏览器内存中的变量,用于记录当前的计数值
let currentCount = 0;
// 3. 绑定事件监听器与业务逻辑 (Event Driven)
// 语法解析: 给 buttonElement 添加一个 'click' (点击) 事件监听。
// 当点击发生时,执行后面的匿名函数 (回调函数)。
buttonElement.addEventListener('click', function() {
// 3.1 核心业务逻辑:状态变更
currentCount = currentCount + 1;
// 3.2 动态更新视图:修改 DOM 节点的内部文本
// 浏览器引擎检测到 DOM 数据发生变化,会立即触发局部的重绘,将新数字显示在屏幕上
displayElement.innerText = currentCount;
// 注: 如果是向后端提交数据,通常会在这里使用 fetch() 或 axios 发起异步网络请求
// fetch('/api/counter/increment', { method: 'POST' }) ...
});
</script>
</body>
</html>html渲染效果:

解读:#
HTML 定义结构与标识: <span id="count-display">0</span> 和 <button id="increment-btn"> 创建了显示区和按钮。这里的 id 就相当于数据库记录的主键,是专门留给 CSS 和 JS 定位用的锚点,HTML 本身不包含任何执行逻辑。
CSS 定义样式规则: #increment-btn { background-color: #28a745; } 通过 ID 选择器精准命中那个按钮,把它渲染成绿色;#increment-btn:hover 则定义了鼠标悬停时颜色加深的交互反馈。
JavaScript 定义事件与行为:
- 获取引用: JS 引擎先通过
getElementById()查找到对应的 DOM 节点,拿到它们在内存中的指针。 - 注册监听: 用
addEventListener('click', ...)给按钮挂接一个点击事件的异步回调函数(相当于注册了一个局部的 Webhook)。 - 状态变更与渲染: 用户点击触发回调,执行
currentCount + 1更新内存变量,最后通过.innerText = currentCount强行把新数据写回那个span节点中,浏览器察觉到数据变化,自动重绘页面。 HTML 提供带“主键”的静态容器,CSS 根据主键渲染 UI,JS 则像 Controller 一样在外部获取节点、监听事件,并动态把数据塞进容器里
总之可以这样理解:
- HTML 定义页面的结构和内容,比如这里有个标题、那里有个按钮。
- CSS 定义页面的样式和布局,比如按钮的颜色的、字体多大、元素怎么排列。
- JavaScript 定义页面的行为和交互逻辑,比如点击按钮后发生什么、如何从后端获取数据并更新页面。
浏览器会首先加载 HTML 文件,然后加载其中引用的 css 文件渲染样式,最后加载其中引用的 js 文件,执行 JavaScript 代码。
上述只是为了方便举例,实际应用中,三者通常是独立的文件,通过引用的方式加载,如:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app">页面内容blabla</div>
<script src="app.js"></script>
</body>
</html>html什么是DOM#
打个比方
- HTML = 网络传输的 JSON 字符串。 它只是一段静态的纯文本报文,记录了页面的层级和初始数据。
- DOM = 内存中的 Java 对象树(POJO 实例)。 浏览器引擎(类似于
Jackson或FastJSON)将 HTML 文本**“反序列化”**成了真实存在于内存中、带有属性(如id)和方法的对象集合。 - JavaScript = 你的 Java 业务代码。 JS 不会去操作纯文本,而是通过调用内存对象的方法(类似于
domObject.setText("1")),来动态修改这些对象的状态。
React / Vue框架#
React 产生的目的:解决“状态”与“视图”的同步噩梦#
在 React 出现之前(比如 jQuery 时代),前端开发采用的是命令式编程。当数据发生变化时,开发者需要手动去寻找对应的 DOM 元素(网页上的各种标签),然后修改它们。
想象一下,你有一个非常复杂的电商页面,购物车数量、商品总价、右上角的用户提示,这三个地方的数据是联动的。一旦用户点了一下“+1”,你需要写三段代码分别去更新这三个地方的显示。如果逻辑再复杂一点,代码很快就会变成一团乱麻,不仅难以维护,还极容易出现 Bug——Facebook 当年就深受其害,他们的消息提示小红点经常会出现数字不对的 Bug。
React 产生的核心目的就是为了改变这种开发模式,引入了“声明式编程”。
它的理念是:数据驱动视图。你只需要关注“数据(状态)”现在是什么样子,React 会自动帮你把界面渲染成对应的样子。你不再需要手动去操作 DOM 元素,只需要告诉 React:“这是最新的数据,请帮我把界面更新一下。”
React 的基本用法#
要上手 React,只需要掌握三个核心概念:
- JSX: 这是一种让你能在 JavaScript 代码里直接写 HTML 标签的语法糖。一开始看着会有点违和,但习惯后会发现它让组件的结构和逻辑结合得非常紧密。
- Props (属性): 父组件传递给子组件的数据,相当于函数的“参数”。对于子组件来说,Props 是只读的。
- State (状态): 组件自身维护的内部数据,相当于类的“私有变量”。当 State 发生改变时,React 就会自动重新执行渲染逻辑,更新界面。
一个极其简化的例子:
// 这是一个名为 Counter 的组件
function Counter() {
// 定义一个状态 count,初始值为 0
const [count, setCount] = React.useState(0);
return (
<div>
<p>当前数字: {count}</p>
{/* 点击按钮时,调用 setCount 更新状态,React 会自动刷新上面的数字 */}
<button onClick={() => setCount(count + 1)}>加 1</button>
</div>
);
}plaintextreact项目-目录结构举例#
my-react-app/
├── node_modules/ # 存放所有第三方依赖包
├── public/ # 存放不需要经过编译的静态资源
│ ├── favicon.ico # 网站图标
│ └── index.html # 整个单页应用(SPA)的唯一 HTML 宿主
├── src/ # 核心源代码目录(你在前端 99% 的工作都在这里)
│ ├── api/ # 集中管理与后端的接口请求
│ ├── assets/ # 存放需要被编译的静态资源(图片、全局 CSS 等)
│ ├── components/ # 全局通用的、可复用的基础 UI 组件
│ ├── pages/ # 页面级组件(路由直接映射的组件)
│ ├── utils/ # 全局工具类函数
│ ├── App.jsx # 根组件,负责串联所有的路由和全局配置
│ └── main.jsx # 程序的入口文件,负责将 App 渲染到 index.html 中
├── .gitignore # Git 忽略配置
├── package.json # 项目的配置文件和依赖清单
└── vite.config.js # 构建工具的配置文件(如果是 Vite 创建的项目)plaintextReact 的好处#
1. 组件化开发 (Component-Based) React 鼓励将庞大的 UI 界面拆分成一个个独立、可复用的“组件”(比如一个按钮、一个导航栏、一个商品卡片)。
- 工程学视角: 这就像你在写 Java 时定义的一个个独立 Class,或者分布式架构中的微服务。 2. 虚拟 DOM (Virtual DOM) 真实的 DOM 操作是非常极其消耗性能的。如果频繁更新页面,浏览器会卡顿。
- 工程学视角: React 在内存中维护了一棵“虚拟 DOM 树”(本质上就是一个巨大的 JavaScript 对象)。当数据改变时,React 会先生成一棵新的虚拟 DOM 树,然后通过一套高效的 Diff 算法,对比出新旧两棵树的差异,最后只把真正发生变化的地方更新到真实的网页上。
引入 Vue 或 React 等框架后,DOM 对开发者来说已经“隐身”了
vue框架#
另一个流行的前端框架 国人的骄傲 这里不多说了
但是记住 浏览器只认识 HTML、CSS、JavaScript。
不管是 React 的 JSX 还是 Vue 的 .vue 文件,浏览器都不能直接运行,它们最终都要通过编译工具(如 Webpack、Vite)转换成原生的 HTML + CSS + JS 文件,才能在浏览器中运行。
JavaScript#
JavaScript 本质上是一种单线程、非阻塞的解释型脚本语言。 著名解释器如Chrome V8
Node.js#
不要将 Node.js 视为一种新语言或框架,它的定位等同于 **JRE
- 浏览器中的 JS: 运行在受限的沙箱环境中。宿主对象是
window和document,只能操作 DOM 和发起 HTTP 请求,严禁直接访问本地文件系统或操作系统底层资源。 - Node.js 中的 JS: 剥离了浏览器的 DOM API,将 V8 引擎移植到了服务器端。它提供了原生的 C++ 绑定,赋予了 JS 读写磁盘(
fs)、操作网络 Socket(http/net)、管理进程(child_process)等标准的系统级能力。
通过 Node.js,JavaScript 实现了从“前端表现层”向“后端控制层”的跨越。
CommonJS vs ES Modules#
在 Java 中,类的加载和依赖管理有明确的 import 机制和 ClassLoader 规范。而在 JS 的发展史中,出现了两套并行的模块系统规范:
| 维度 | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| 标准地位 | Node.js 社区早期事实标准 | ECMAScript 官方语言级标准 |
| 语法 | require() / module.exports | import / export |
| 加载机制 | 同步(Synchronous)、运行时加载。 | 异步(Asynchronous)、静态编译时分析。 |
| 适用场景 | 传统的 Node.js 后端应用(模块全在本地磁盘,同步读取无性能瓶颈)。 | 现代前端工程与现代 Node.js(支持 Tree-Shaking 静态死代码剔除优化)。 |
目前 Node.js 已完全兼容 ESM。在现代前端工程体系中,ESM 已经成为绝对的主流。
TypeScript:引入静态类型约束#
JavaScript 是一门弱类型、动态类型的语言。 这意味着变量类型在运行时才能确定,极易在大型工程中引发隐式的类型转换异常(如 1 + "2" = "12")。
TypeScript (TS) 的本质:
它是 JavaScript 的强类型超集。TS 并不创造新的运行时机制,它的核心价值完全体现在编译期(Compile-time)。
- 静态检查: TS 编译器(
tsc)充当了类似javac的角色,在代码运行前进行严格的类型校验,将运行时错误前置拦截为编译时错误。 - 面向对象增强: 引入了
Interface、Enum、Generics(泛型)等 Java 开发者极为熟悉的 OOP 概念。 - 类型擦除: 与 Java 的泛型擦除类似,TS 代码在编译为 JS 后,所有的类型注解将被完全擦除,最终在 V8 引擎中执行的依然是纯粹的 JavaScript。
在企业级中大型项目中,TypeScript 已是标配。
Next.js:React 生态的全栈级架构#
如果说 React/Vue 是纯粹的前端 UI 渲染库(类似于高级版的 JSP/Thymeleaf 模板引擎),那么 Next.js 则是一个完整的、开箱即用的 Web 框架(类似于 Spring Boot)。
Next.js 基于 React 构建,抹平了前后端的物理边界,允许开发者使用同一种语言(TypeScript)在一个工程内完成全栈开发。
Next.js 的核心工程特性:
-
约定优于配置(Convention over Configuration)的路由设计:
摒弃了集中式的路由配置文件。Next.js 采用基于文件系统的路由(File-system Routing)。
-
app/page.tsx自动映射为/路由。 -
app/blog/[id]/page.tsx自动映射为/blog/{id}这种带有路径变量的路由。
-
-
同构渲染(Isomorphic Rendering)能力:
这是 Next.js 的核心杀手锏。它可以根据业务需求,在组件级别灵活配置渲染策略:
-
CSR (客户端渲染): 传统 React 模式,浏览器下载 JS 后动态渲染(利于复杂交互)。
-
SSR (服务端渲染): 类似 Java JSP,Node.js 服务器在接收请求时动态拼接好完整的 HTML 下发(利于 SEO 和首屏直出)。
-
SSG (静态站点生成): 在项目
build阶段就提前把页面编译成纯静态 HTML,部署到 CDN 上(极致的访问性能)。
-
-
BFF 架构集成 (Backend for Frontend):
Next.js 专门提供了一个
app/api/目录。-
这里的代码(如
app/api/users/route.ts)完全运行在 Node.js 服务器环境中,绝对不会被打包发往浏览器。 -
它相当于 Spring Boot 中的
@RestController层。在这里,你可以直接使用安全的服务器环境连接 MySQL/Redis,处理持久化逻辑,并对外暴露 RESTful API 供前端页面调用。
-
通过 Next.js,前端工程正式走向了“微全栈”时代。
服务端渲染 vs 客户端渲染#
传统的客户端渲染(CSR)#
传统的 React/Vue 应用是纯客户端渲染。服务器返回的 HTML 文件几乎是空的:
<!DOCTYPE html>
<html>
<head>
<title>My React App</title>
</head>
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>
</html>plaintext注意 <div id="root"> 里面是空的,页面的所有内容都靠 bundle.js 这个 JavaScript 文件来动态生成。浏览器下载完 HTML 后,再下载并执行 bundle.js,JavaScript 代码动态生成页面内容,最后渲染到 #root 元素里。
这种方式有两个问题:
第一,用户看到页面内容比较慢。需要等 JavaScript 下载、解析、执行完才能看到内容。
第二,对 SEO 不友好。搜索引擎爬虫抓取到的 HTML 是空的,看不到页面内容,不利于搜索引擎收录。
服务端渲染(SSR)#
Next.js 支持服务端渲染。服务器在返回 HTML 之前,先在服务器上执行 React 组件代码,生成完整的 HTML:
<!DOCTYPE html>
<html>
<head>
<title>My Next.js App</title>
</head>
<body>
<div id="root">
<h1>欢迎来到我的博客</h1>
<ul>
<li>文章1: Next.js 入门</li>
<li>文章2: React Hooks 详解</li>
<li>文章3: TypeScript 最佳实践</li>
</ul>
</div>
<script src="/_next/static/chunks/main.js"></script>
</body>
</html>plaintext浏览器收到的 HTML 已经包含了完整的页面内容,用户可以立即看到内容,搜索引擎也能抓取到完整的页面。
通过 script 标签加载的 JavaScript 代码在浏览器中执行,主要用来处理用户交互、显示动画效果、请求动态数据等。