一、前端框架简介(从 Java 开发人员的视角理解)
1. React
核心概念
(1)组件化开发
- React 就像 Java 的类或方法,可以把页面分成多个小模块(组件)。
- 每个组件封装自己的逻辑和样式,像 Java 中一个专门处理功能的类。
简单类比:
- Java 后端开发时,你可能会把用户逻辑写在一个
UserService
类中,负责用户相关的处理。 - 在 React 中,页面中的按钮或表单等功能模块就是一个个“组件”。
示例:
const Button = ({ label }) => <button>{label}</button>;
(2)JSX
- JSX 是 React 中的一种写法,允许直接在 JavaScript 里写 HTML。
- 类似 Java 后端的模板引擎(如 Thymeleaf 或 JSP),但更灵活。
示例:
const Greeting = ({ user }) => (
<div>
{user ? <h1>Welcome, {user.name}!</h1> : <h1>Welcome, Guest!</h1>}
</div>
);
(3)状态管理
- React 的组件可以有“状态”,类似于 Java 对象里的属性,用来存储数据。
- 使用
useState
定义状态,并动态更新界面,类似于 Java 的对象更新。
示例:
import { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
};
适合场景
React 适合用于构建动态交互多、页面复杂的项目,例如:
- 后台管理系统:数据表格、图表展示、复杂的交互。
- 电商系统前端:需要频繁更新界面的模块(如购物车、订单状态)。
文件类型
.jsx
:
- 写 React 组件代码,允许混合使用 HTML 和 JavaScript。
.tsx
:
- 和
.jsx
类似,但带有 TypeScript 的类型检查,更安全。
2. Vue.js
核心概念
(1)单文件组件
- Vue 将一个页面模块(组件)的结构、样式和逻辑写在同一个
.vue
文件里。 - 这种写法简单直观,便于开发和维护。
示例:
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return { message: 'Hello Vue!' };
}
};
</script>
<style>
div {
color: blue;
}
</style>
(2)双向绑定
- Vue 提供了
v-model
指令,用来同步前端表单数据和后台数据。 - 这有点像 Java 中 Bean 的 Getter 和 Setter 方法。
示例:
<template>
<input v-model="message" />
<p>{{ message }}</p>
</template>
<script>
export default {
data() {
return {
message: ''
};
}
};
</script>
(3)渐进式
- Vue 是一个“渐进式”框架,功能可以根据需求扩展。
- Vue Router:处理页面跳转。
- Vuex:管理全局数据(类似 Java 后端的全局变量)。
- axios:用来调用后端 API。
适合场景
Vue 适合小型到中型项目,例如:
- 快速开发的小应用:如一个用户信息录入页面。
- 模块化功能的系统:例如嵌入式的表单或动态展示页面。
文件类型
.vue
:
- 包含 HTML 模板、CSS 样式和 JS 逻辑的单文件组件。
3. Angular
核心概念
(1)模块化开发
- Angular 强制你将代码按照模块划分,比如用户管理模块、订单管理模块,类似 Java 的包划分。
- 每个模块都可以独立开发和维护,类似于 Spring Boot 的微服务模块。
(2)TypeScript 支持
- Angular 默认使用 TypeScript,类似于 Java 中的强类型语言。
- 开发中可以明确定义变量和方法的类型,减少错误。
示例:
// 定义组件类
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent {
userName: string = 'John Doe'; // 强类型
}
内置功能
Angular 提供了很多开箱即用的功能,例如:
- 路由:内置导航功能,用来切换页面。
- 依赖注入(DI):类似 Spring 的依赖注入机制,方便管理服务。
- 表单验证:支持复杂的表单校验。
适合场景
Angular 适合大型企业级应用,例如:
- ERP 系统:如进销存、财务管理系统。
- 复杂的门户网站:需要精细化模块管理的项目。
文件类型
.ts
(逻辑):
- 编写逻辑代码,定义变量、方法和服务。
.html
(模板):
- 定义组件的界面结构。
.css
(样式):
- 设置界面样式,分离逻辑和样式。
二、前端核心文件类型及结构
1. 文件类型
1.1 .html
文件
- 定义:
.html
文件用于定义网页的基本结构,是前端应用的入口文件。- 它提供一个“容器”,前端框架(如 Vue、React)会将内容渲染到这个容器中。
- 作用:
- 设置页面骨架(如标题、按钮、输入框等)。
- 为 JavaScript 应用提供挂载点(如
<div id="app">
)。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Frontend App</title>
</head>
<body>
<!-- 前端应用将挂载到这里 -->
<div id="app"></div>
<!-- 引入应用主文件 -->
<script src="./main.js"></script>
</body>
</html>
1.2 .css
文件
- 定义:
.css
文件用于控制网页的样式,例如字体、颜色、布局等。
- 作用:
- 为 HTML 元素添加视觉效果。
- 提高页面的美观性和用户体验。
示例:
/* 全局样式 */
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
}
/* 按钮样式 */
.button {
color: white;
background-color: blue;
padding: 10px 20px;
border: none;
border-radius: 5px;
}
1.3 .js
和 .ts
文件
-
定义: 用于编写页面的逻辑代码,实现动态交互。
-
区别:
.js
文件: 使用动态类型语言 JavaScript,灵活但缺乏类型约束。.ts
文件: 使用 TypeScript,提供强类型支持,适合复杂项目。
-
作用:
- 定义页面交互逻辑、事件处理、API 调用等。
- 类似 Java 中的业务逻辑类,但用于前端。
示例:
// JavaScript 示例
const greet = (name) => `Hello, ${name}`;
console.log(greet('World'));
// TypeScript 示例
const sum = (a: number, b: number): number => a + b;
console.log(sum(5, 10));
1.4 .vue
文件
- 定义:
- Vue.js 特有文件类型,整合了 HTML(模板)、CSS(样式)和 JavaScript(逻辑)于一个文件中。
- 每个
.vue
文件就是一个独立的组件。
- 作用:
- 提供单文件组件(SFC, Single File Component),便于开发、维护和模块化。
示例:
<template>
<!-- HTML 模板 -->
<div>{{ message }}</div>
</template>
<script>
// JavaScript 逻辑
export default {
data() {
return {
message: 'Hello Vue!'
};
}
};
</script>
<style>
/* 样式 */
div {
color: red;
}
</style>
2. 不同架构对不同类型文件的适配
文件类型/工具 | React | Vue.js | Angular | 是否可混用 | 说明 |
---|---|---|---|---|---|
.html | ✔️ | ✔️ | ✔️ | 可以混用 | 标准结构文件,作为应用入口,定义挂载点,三种框架均可使用。 |
.css | ✔️ | ✔️ | ✔️ | 可以混用 | 标准样式文件,用于全局或模块化样式控制,三种框架均支持直接引入。 |
.js | ✔️ | ✔️ | ✔️ | 可以混用 | 标准 JavaScript 文件,存储工具函数、业务逻辑等,与框架无关。 |
.ts | ✔️ | ✔️ | ✔️ | 可以混用 | TypeScript 文件,提供强类型支持,三种框架均支持。 |
.vue | ❌ | ✔️ | ❌ | 不推荐混用 | Vue.js 独有的单文件组件,包含模板、样式、逻辑,仅支持 Vue 框架。 |
.jsx / .tsx | ✔️ | ❌ | ❌ | 不推荐混用 | React 独有的文件类型,包含 JSX 语法,仅适用于 React。 |
可以混用的文件类型
.html
:三种框架都以.html
作为入口文件,负责挂载根组件。.css
:可用于全局或局部样式,三种框架支持一致。.js
和.ts
:作为通用逻辑文件,能在框架之间共享工具代码。
不推荐混用的文件类型
.vue
:仅限于 Vue 框架,其他框架无法解析。.jsx
/.tsx
:仅限于 React 框架,其他框架无法解析 JSX 语法。
特殊说明
- 虽然
.html
文件可以被三种框架使用,但 React 和 Vue 主要将其用作挂载点,而 Angular 会将其用作模板的一部分。
3. 文件夹结构(通用)
一个典型的前端项目的文件夹结构如下:
project/
├── public/ # 静态资源,如图片、HTML 模板
│ ├── favicon.ico # 网站图标
│ └── index.html # HTML 模板文件
├── src/ # 源代码
│ ├── components/ # 可复用的通用组件
│ │ ├── Header.vue # Vue 项目的头部组件
│ │ ├── Button.jsx # React 项目的按钮组件
│ │ └── Footer.tsx # 使用 TypeScript 的底部组件
│ ├── pages/ # 页面级组件
│ │ ├── Home.vue # Vue 项目的主页
│ │ └── Dashboard.jsx # React 项目的控制面板
│ ├── styles/ # 全局或模块化的样式文件
│ │ └── main.css # 全局样式
│ ├── App.vue # Vue 项目主入口文件
│ ├── App.jsx # React 项目主入口文件
│ └── main.ts # 应用的入口逻辑
├── package.json # 项目的依赖和脚本配置文件
└── vite.config.js # 构建工具(Vite)的配置文件
解释
public/:
- 用于存放静态资源,如图片、图标和 HTML 模板。
- 前端框架通常会将
index.html
作为入口文件,用来加载 JavaScript。
src/:
- 核心代码目录,所有动态功能都在这里实现。
- components/:
- 存放通用组件,例如按钮、表单等,可以在多个页面复用。
- pages/:
- 页面级别的组件,每个文件对应一个完整的页面逻辑。
- styles/:
- 用于存放全局样式(如主色调定义)或模块化样式文件。
-
App.vue
或App.jsx
- 用途: 前端应用的主入口组件,类似于 Spring Boot 的
@SpringBootApplication
类,负责启动和加载核心模块。
- 用途: 前端应用的主入口组件,类似于 Spring Boot 的
-
main.js
- 用途: 应用的总入口,初始化应用并挂载到 HTML 中的容器。
package.json:
- 项目配置文件,记录了项目依赖的第三方库和常用脚本命令。
- 类似于 Java 的
pom.xml
文件。
vite.config.js:
- 配置前端构建工具(如 Vite),用于定义构建规则、路径别名等。
三、模块化开发
1. 什么是组件
组件是模块化开发的核心。你可以把它看成是一个“功能盒子”,每个组件负责一个小任务,比如显示一个按钮、表单或列表。
1.1.React 中的组件
简单理解:
- 在 React 里,组件就像 Java 的类,一个组件封装一块功能,比如一个按钮。
- 组件可以接收外部传来的数据(叫
props
),比如按钮的名字。
例子:
// 定义一个按钮组件
const Button = ({ label }) => {
return <button>{label}</button>; // 渲染一个按钮,显示传进来的文字
};
// 使用按钮组件
const App = () => {
return (
<div>
<Button label="点我一下" /> {/* 使用按钮组件,传递文字 "点我一下" */}
</div>
);
};
解释:
Button
是一个独立的组件,功能是显示一个按钮。label
是传进来的数据(props
),显示在按钮上。- 你可以复用这个按钮组件,给它不同的
label
,它会显示不同的文字。
1.2.Vue 中的组件
简单理解:
-
Vue 的组件是一个完整的文件(
.vue
),包括 HTML(界面)、CSS(样式)、JavaScript(逻辑)。 -
每个
.vue
文件就是一个独立的功能模块。
例子:
<template>
<!-- 按钮模板 -->
<button>{{ label }}</button>
</template>
<script>
// 组件逻辑
export default {
props: ['label'], // 接收外部传来的数据 "label"
};
</script>
<style scoped>
/* 组件样式 */
button {
background-color: blue;
color: white;
padding: 10px 20px;
}
</style>
解释:
- 这个
.vue
文件是一个独立的组件,定义了一个按钮。 props
: 接收外部传来的数据(label
),比如按钮上的文字。<style scoped>
: 样式只作用在这个组件中,不会影响其他地方。
2. 样式和脚本的模块化
2.1 样式模块化
模块化样式是指每个组件的样式只影响自己,互相不会干扰。
React 的 CSS Modules
- React 用 CSS Modules 把样式绑定到组件,样式名字会自动加密,确保不会和其他样式冲突。
例子:
样式文件(Button.module.css
):
.button {
background-color: blue;
color: white;
padding: 10px 20px;
}
组件文件(Button.jsx
):
import styles from './Button.module.css'; // 引入模块化样式
const Button = ({ label }) => {
return <button className={styles.button}>{label}</button>; // 使用样式
};
export default Button;
解释:
Button.module.css
是这个按钮的样式文件。styles.button
确保样式只作用在当前组件的按钮上,不会影响其他地方。
Vue 的 Scoped CSS
- Vue 用
<style scoped>
让样式只影响当前组件,不会干扰其他地方。
例子:
<template>
<button>{{ label }}</button>
</template>
<script>
export default {
props: ['label'],
};
</script>
<style scoped>
button {
background-color: blue;
color: white;
padding: 10px 20px;
}
</style>
解释:
- 加上
scoped
后,Vue 会自动给样式加上独特的标识符,让样式只作用在这个按钮上。
2.2 脚本模块化
模块化脚本是指把通用的逻辑拆分成独立的文件,哪里需要用就导入,避免重复写代码。
React 和 Vue 的脚本模块化
步骤:
-
把通用逻辑写在工具文件里(例如计算函数)。
-
在组件中导入工具函数。
例子:
工具文件(utils.js
):
// 计算两个数字的和
export const calculateSum = (a, b) => a + b;
React 组件:
import { calculateSum } from './utils';
const App = () => {
const result = calculateSum(5, 10); // 使用工具函数
return <div>Sum: {result}</div>;
};
export default App;
Vue 组件:
<template>
<div>Sum: {{ result }}</div>
</template>
<script>
import { calculateSum } from './utils';
export default {
data() {
return {
result: calculateSum(5, 10), // 使用工具函数
};
},
};
</script>
解释:
utils.js
是一个通用的工具文件,定义了calculateSum
函数。- React 和 Vue 的组件都可以导入这个函数,直接使用,避免重复写逻辑。
总结
内容 | React | Vue |
---|---|---|
组件 | 用函数或类封装,类似 Java 类。 | 使用 .vue 文件,封装界面、逻辑和样式。 |
样式模块化 | 使用 CSS Modules 隔离样式。 | 使用 <style scoped> 隔离样式。 |
脚本模块化 | 使用工具文件(如 utils.js )按需导入。 | 使用工具文件或 Mixins 按需导入。 |
四、前端构建工具
1. 为什么需要构建工具
在开发过程中,前端代码往往包含最新的技术(如 ES6、TypeScript、模块化开发)和特定框架的文件格式(如 .vue
)。然而,浏览器无法直接运行这些文件或代码,需要一个“中间工具”来完成以下任务:
(1)转换代码:
- 将现代 JavaScript(如箭头函数、模块化)转换为兼容性更好的代码,确保在老旧浏览器中运行。
例如,把以下 ES6 代码:
const sum = (a, b) => a + b;
转换为浏览器支持的 ES5 代码:
var sum = function (a, b) {
return a + b;
};
(2)优化代码:
- 压缩 JavaScript 和 CSS 文件,减少文件大小。
- 合并文件,减少浏览器加载的请求次数。
(3)模块化支持:
- 开发时,我们将代码拆分为多个模块(如组件和工具文件),构建工具可以将它们合并为一个文件供浏览器使用。
(4)开发体验:
- 提供一个本地开发服务器,支持热更新(HMR),修改代码后,浏览器会立即更新,无需手动刷新。
2. 构建项目的过程
在使用构建工具(如 Vite 或 Webpack)创建项目时,通常会用到以下两步:
安装依赖:
- 使用
npm install
安装项目所需的依赖库。 - 依赖库包含构建工具(如 Vite 或 Webpack)、框架(如 React 或 Vue)及其他工具。
启动开发环境:
- 使用
npm run dev
启动本地开发服务器。 - 开发服务器会运行项目代码,支持实时热更新,让你在浏览器中实时看到修改效果。
2.1 npm install 的作用
npm install
会根据package.json
文件中定义的依赖,下载所需的库,并存储在项目的node_modules
文件夹中。package.json
示例:
{
"dependencies": {
"vue": "^3.0.0"
},
"devDependencies": {
"vite": "^2.0.0"
}
}
- 命令解释:
- dependencies: 项目运行时所需的库(如框架
vue
)。 - devDependencies: 仅开发阶段需要的工具(如构建工具
vite
)。
- dependencies: 项目运行时所需的库(如框架
2.2 npm run dev 的作用
npm run dev
会执行package.json
文件中定义的脚本,用于启动开发环境。- 脚本配置:
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
- 命令解释:
npm run dev
: 运行vite
,启动本地开发服务器。- 效果:
- 项目会在本地启动一个服务器(如
http://localhost:3000
)。 - 浏览器自动打开页面,实时展示开发效果。
- 项目会在本地启动一个服务器(如
3. Vite
3.1 什么是 Vite
- Vite 是一种轻量、快速的前端构建工具,专为现代框架(如 Vue 和 React)设计。
- 它的最大特点是速度快,适合中小型项目。
3.2 功能
-
极速启动:
- 使用浏览器原生的模块化支持(ES Modules),跳过传统的打包步骤,直接加载需要的代码。
-
热模块替换(HMR):
- 修改代码后,浏览器会自动更新页面,无需手动刷新。
-
生产环境优化:
- 在打包时,会压缩代码、分割模块、删除无用代码,提升加载性能。
3.3 构建项目(以 Vue 为例)
1. 初始化项目
# 创建一个新项目
npm create vite@latest my-vite-app
# 进入项目文件夹
cd my-vite-app
# 安装项目依赖
npm install
2. 启动开发环境
npm run dev
运行后:
- Vite 启动一个本地开发服务器(默认端口是 5173)。
- 浏览器打开项目后,修改代码会自动刷新页面。
3. 打包生产环境
npm run build
作用:
- 将开发中的代码打包成优化后的静态文件(HTML、CSS、JavaScript)。
- 生成的文件会存放在
dist/
目录中。
4. 示例 Vite 项目结构
my-vite-app/
├── public/ # 静态资源(如图片)
├── src/ # 项目源码
│ ├── App.vue # Vue 主组件
│ ├── main.js # 应用入口
├── package.json # 项目配置文件
└── vite.config.js # Vite 配置文件
4. Webpack
4.1 什么是 Webpack
- Webpack 是功能强大的构建工具,适合大型项目。它可以将多个文件(如 JavaScript、CSS、图片)打包成优化的静态文件。
4.2 功能
-
文件打包:
- 将模块化开发的多个文件合并为一个或多个静态文件供浏览器加载。
-
代码分割:
- 按需加载代码。例如,用户访问登录页面时只加载登录相关的文件。
-
丰富的插件支持:
- 提供插件和加载器支持各种文件的处理(如 Babel 转换 JavaScript,PostCSS 处理样式)。
4.3 构建项目
1. 初始化项目
# 创建一个新项目文件夹
mkdir my-webpack-app
cd my-webpack-app
# 初始化 package.json 文件
npm init -y
# 安装 Webpack 和 CLI 工具
npm install webpack webpack-cli --save-dev
2. 配置 Webpack
在项目根目录创建 webpack.config.js
文件:
const path = require('path');
module.exports = {
entry: './src/index.js', // 项目入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 打包输出目录
filename: 'bundle.js', // 输出的文件名
},
module: {
rules: [
{
test: /\.css$/, // 处理 CSS 文件
use: ['style-loader', 'css-loader'],
},
],
},
};
3. 创建源码
目录结构:
my-webpack-app/
├── src/
│ ├── index.js # 主入口文件
│ └── styles.css # 样式文件
├── webpack.config.js # 配置文件
├── package.json # 项目配置
4. 启动开发环境
npx webpack serve
运行后:
- Webpack 启动开发服务器,监视文件变化,实时更新页面。
5. 打包生产环境
npx webpack --mode production
作用:
- 将代码打包为优化后的静态资源,适合部署到服务器。
5. Vite 和 Webpack 的对比
特性 | Vite | Webpack |
---|---|---|
启动速度 | 非常快(直接使用浏览器模块支持) | 较慢(需要先打包文件) |
配置难度 | 简单,开箱即用 | 复杂,可高度自定义 |
代码分割和优化 | 默认支持 | 需要手动配置 |
适合场景 | 中小型项目、快速开发 | 大型项目、复杂需求 |
五、CSS 框架
1. Bootstrap
1.1 什么是 Bootstrap
Bootstrap 是一个流行的 CSS 框架,提供了丰富的预定义样式和组件,例如按钮、表单、导航栏等。它简化了前端开发流程,特别适合快速构建通用 UI。
1.2 Bootstrap 的特点
(1)预定义组件
Bootstrap 提供了许多现成的 UI 组件,直接通过类名使用即可。例如,按钮、表单、模态框(弹窗)等组件。
示例:
<button class="btn btn-primary">提交</button>
btn
是按钮的基本样式,btn-primary
添加了蓝色背景样式。
(2)响应式布局
Bootstrap 的网格系统可以帮助你轻松实现自适应布局。通过简单的类名,就可以让页面在手机、平板和桌面设备上都保持良好的显示效果。
示例:
<div class="row">
<div class="col-md-6">左侧内容</div>
<div class="col-md-6">右侧内容</div>
</div>
row
定义一行,col-md-6
定义列宽占 12 格中的 6 格(即一半宽度)。
(3)快速开发
通过内置样式,开发者可以专注于功能开发而非手写样式代码。例如,导航栏、表单、按钮等都可以直接使用预定义样式。
1.3 Bootstrap 的适用场景
- 快速开发:当项目对个性化设计要求不高时,Bootstrap 是一个极好的选择,比如后台管理系统、公司官网等。
- 一致性设计:需要统一的设计风格时,Bootstrap 提供了完整的设计规范,适合团队协作。
2. Tailwind CSS
2.1 什么是 Tailwind CSS
Tailwind CSS 是一种实用优先的 CSS 框架,它通过类名直接定义样式,开发者可以灵活组合这些类来实现高度自定义的设计风格。相比 Bootstrap 提供现成组件,Tailwind 更加注重自由和灵活性。
2.2 Tailwind CSS 的特点
(1)按类名定义样式
在 Tailwind 中,每个类名都对应一个特定的样式,你可以通过组合这些类名来创建自己的设计。
示例:
<button class="bg-blue-500 text-white px-4 py-2 rounded">
提交
</button>
bg-blue-500
设置背景颜色为蓝色,text-white
设置文字颜色为白色,px-4
和py-2
分别设置左右和上下的内边距,rounded
设置按钮圆角。
(2)高度可定制
Tailwind 支持通过配置文件(tailwind.config.js
)进行样式定制,你可以修改默认的颜色、间距等样式规则。
示例:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
customBlue: '#1DA1F2', // 添加一个自定义蓝色
},
},
},
};
(3)无全局污染
Tailwind 的类名独立存在,不会影响全局样式,也不会与其他框架冲突。每个类名仅作用于你指定的地方。
2.3 Tailwind CSS 的适用场景
- 个性化设计:当项目需要独特的风格和完全自定义的设计时,Tailwind 是一个非常灵活的选择,比如品牌官网、个人作品集等。
- 精细控制:如果你希望完全掌控页面样式,而不是使用框架的现成组件,Tailwind 非常适合。
3. 对比 Bootstrap 和 Tailwind CSS
特性 | Bootstrap | Tailwind CSS |
---|---|---|
使用方式 | 提供现成的组件(如按钮、表单、导航栏) | 通过类名组合直接定义样式 |
灵活性 | 固定组件,灵活性有限 | 高度灵活,可以完全自定义 |
学习难度 | 上手简单,只需了解基本组件 | 需要记住大量类名,初学者可能稍难 |
适用场景 | 快速开发通用 UI 项目 | 需要高度定制的项目 |
设计风格 | 统一、规范 | 自由、个性化 |
六、前后端分离与通信
前后端分离模式:
- 前端:
- 专注于页面的展示和交互,使用现代前端框架(如 React、Vue)构建页面。
- 通过调用后端提供的接口(API)获取数据。
- 后端:
- 专注于业务逻辑和数据处理,提供标准化的数据接口(REST API)。
- 数据以 JSON 格式返回,前端接收到后进行解析并展示。
1. 使用 axios
调用后端 API
1.1 什么是 axios
- 定义:
axios
是一个基于 Promise 的 HTTP 客户端库,专为浏览器和 Node.js 设计。 - 特点:
- 简单易用。
- 支持各种 HTTP 请求方法(GET、POST、PUT、DELETE 等)。
- 支持请求和响应拦截器,便于统一处理逻辑。
1.2 安装 axios
npm install axios
1.3 调用 API 示例:GET 请求
代码实现
import axios from 'axios';
axios.get('/api/resource') // 调用后端 API,获取数据
.then(response => {
// 成功时的回调
console.log('后端返回的数据:', response.data);
})
.catch(error => {
// 错误处理
console.error('请求失败:', error);
});
逻辑解析
GET 请求:
- 目标: 向后端获取数据。
- 请求方式:
axios.get(url)
,url
是 API 的地址。response.data
包含后端返回的数据(通常是 JSON 格式)。
axios.get('/api/resource')
:
- 向后端发送一个 GET 请求,目标是
/api/resource
。 - GET 请求常用于查询数据,不携带请求体。
.then(response => { ... })
:
- 当后端成功返回数据时,进入
then
回调。
response.data
:
- 包含后端返回的核心数据(通常是 JSON 格式)。
.catch(error => { ... })
:
- 如果请求失败(如网络错误或服务器错误),进入
catch
回调。 - 可以在此处理错误,比如显示友好的错误提示。
1.4 调用 API 示例:POST 请求
代码实现
import axios from 'axios';
axios.post('/api/resource', { name: 'example' }) // 向后端提交数据
.then(response => {
// 成功时的回调
console.log('后端返回的数据:', response.data);
})
.catch(error => {
// 错误处理
console.error('请求失败:', error);
});
逻辑解析
POST 请求:
- 目标: 向后端提交数据。
- 请求方式:
axios.post(url, data)
,其中data
是提交的数据对象(会转换为 JSON)。- 响应数据:
response.data
包含后端返回的处理结果。
axios.post('/api/resource', { name: 'example' })
:
- 发送一个 POST 请求,目标是
/api/resource
。 - 第二个参数
{ name: 'example' }
是请求体,通常为 JSON 格式。 - POST 请求用于提交数据到服务器。
response.data
:
- 包含后端返回的处理结果。例如:
后端返回{ "status": "success", "data": { "id": 1, "name": "example" } }
。
错误处理:
- 通过
catch
捕获错误,比如请求失败或服务器返回 500 错误。
2. 使用 fetch
调用后端 API
2.1 什么是 fetch
- 定义:
fetch
是浏览器内置的 HTTP 请求 API,基于 Promise。 - 特点:
- 不需要安装,浏览器原生支持。
- 功能强大,但语法比
axios
稍显繁琐。
2.2 调用 API 示例:GET 请求
代码实现
fetch('/api/resource') // 调用后端 API,获取数据
.then(response => {
if (!response.ok) {
throw new Error('网络响应失败'); // 检查响应状态
}
return response.json(); // 将响应数据转换为 JSON
})
.then(data => {
console.log('后端返回的数据:', data);
})
.catch(error => {
console.error('请求失败:', error);
});
逻辑解析
GET 请求:
- 目标: 向后端获取数据。
- 响应处理:
response.json()
将返回的 JSON 数据解析为 JavaScript 对象。- 错误处理: 如果
response.ok
为false
,需要手动抛出错误。
fetch('/api/resource')
:
- 发送一个 GET 请求,目标是
/api/resource
。
.then(response => { ... })
:
response
包含 HTTP 响应的元信息(如状态码、响应头等)。
response.ok
:
- 如果状态码为 200-299,
ok
为true
;否则为false
。
response.json()
:
- 将响应体转换为 JSON 数据,返回一个 Promise。
.then(data => { ... })
:
- 接收到的
data
就是后端返回的数据内容。
2.3 调用 API 示例:POST 请求
代码实现
fetch('/api/resource') // 调用后端 API,获取数据
.then(response => {
if (!response.ok) {
throw new Error('网络响应失败'); // 检查响应状态
}
return response.json(); // 将响应数据转换为 JSON
})
.then(data => {
console.log('后端返回的数据:', data);
})
.catch(error => {
console.error('请求失败:', error);
});
逻辑解析
POST 请求:
- 目标: 向后端提交数据。
- 设置请求头: 使用
headers
声明Content-Type
为application/json
。- 设置请求体: 使用
body
提交数据,需先用JSON.stringify
转换为 JSON 字符串。- 响应处理: 同样通过
response.json()
解析返回数据。
method: 'POST'
:
- 指定请求方法为 POST,用于提交数据。
headers
:
- 设置请求头,声明数据格式为
application/json
。
body: JSON.stringify({...})
:
- 提交的数据需要先转换为 JSON 字符串。
3. 前端如何接收后端返回的数据并绑定到页面
3.1 Vue 示例
完整代码
<template>
<div>
<p>用户 ID: {{ resource.id }}</p>
<p>用户名称: {{ resource.name }}</p>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
resource: {}, // 用于存储后端返回的数据
};
},
created() {
// 调用后端 API
axios.get('/api/resource').then(response => {
this.resource = response.data; // 将返回数据绑定到组件
});
},
};
</script>
逻辑解析
data
定义数据对象:
resource
用于存储后端返回的数据。
created
生命周期:
- 组件加载时调用 API。
- 将返回的数据赋值给
resource
。
数据绑定:
{{ resource.id }}
和{{ resource.name }}
会动态展示数据。
3.2 React 示例
完整代码
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const App = () => {
const [resource, setResource] = useState({}); // 定义状态
useEffect(() => {
// 调用后端 API
axios.get('/api/resource').then(response => {
setResource(response.data); // 设置状态
});
}, []);
return (
<div>
<p>用户 ID: {resource.id}</p>
<p>用户名称: {resource.name}</p>
</div>
);
};
export default App;
逻辑解析
状态管理:
- 使用
useState
定义resource
状态,用于存储数据。
useEffect
调用 API:
- 组件挂载时(类似 Vue 的
created
),调用后端 API。
数据展示:
- 通过
{resource.id}
和{resource.name}
将数据动态渲染到页面。
4.前端调用后端API时请求体为JSON格式
前端调用后端的API时,传递的参数一般包含在请求体中,而请求体一般为JSON格式(可以在请求头中通过修改Content-Type属性来换成其他格式,但一般创建Axios实例时指定为JSON格式)。
(1)请求格式为什么是 JSON?
默认配置为 JSON 格式 在 api
(即 Axios 实例)的创建中指定了 Content-Type: application/json
:
const api = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json', // 默认请求头
},
});
- 这段配置明确告诉 Axios,发送请求时的默认格式是 JSON。
(2)通过Axios发送请求示例
以下前端使用 Axios 发送 HTTP 请求的一段代码,目的是向后端 /auth/login
接口发送登录请求。
login: (username: string, password: string) =>
api.post('/auth/login', { username, password }),
login
函数的定义
login
是一个箭头函数,接收两个参数:username
:类型是字符串,表示用户名。password
:类型是字符串,表示密码。
- 函数返回一个 Promise,代表向后端发起的异步请求。
api.post
方法
api
是通过axios.create
创建的 Axios 实例,用于发送 HTTP 请求。post
是 Axios 提供的 HTTP POST 请求方法,格式为:api.post(url, data, config?)
url
: 请求路径,这里是'/auth/login'
。data
: POST 请求体内容,这里是{ username, password }
。config?
: 可选参数,用于配置请求的额外属性(如请求头、超时时间等)。
请求体 { username, password }
{ username, password }
是一个对象,表示将两个字段username
和password
作为请求体发送给后端。- Axios 会根据请求头中的
Content-Type
自动将这个对象序列化为 JSON 字符串。
(3)通过其他格式的请求体向后端发送请求(修改请求头中的Content-Type属性)
文件上传(multipart/form-data
)
文件上传时,使用 multipart/form-data
格式发送数据:
const formData = new FormData();
formData.append('username', username);
formData.append('password', password);
formData.append('file', file); // 文件数据
api.post('/auth/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
请求体示例:
Content-Disposition: form-data; name="username"
yourUsername
Content-Disposition: form-data; name="file"; filename="example.jpg"
<binary data>
常见场景:
- 上传图片、视频等文件。
查询参数(GET
请求或 URL 参数)
某些接口直接通过 URL 查询参数传递数据:
api.get('/auth/login', { params: { username, password } });
Axios 会将参数序列化为查询字符串:
/auth/login?username=yourUsername&password=yourPassword
常见场景:
- 无需敏感信息的简单查询请求。
场景/需求 | 推荐请求格式 | 理由 |
---|---|---|
查询公开数据 | 查询参数(GET) | URL 参数直观,不涉及敏感信息,利于调试和缓存。 |
无状态的简单筛选、分页、排序 | 查询参数(GET) | 符合 RESTful 风格,适合简单的只读操作。 |
包含敏感信息(如密码、token) | JSON 格式(POST) | 数据在请求体中,不暴露在 URL,更安全。 |
数据结构复杂(如对象嵌套、数组) | JSON 格式(POST) | 查询参数表达能力不足,JSON 格式更灵活。 |
数据量大(如文件、长字符串) | JSON 格式(POST) | URL 长度有限,JSON 格式可以承载更大的数据量。 |
需要搜索引擎索引 | 查询参数(GET) | GET 请求可被索引,适合用于对外展示的接口(如搜索、分类)。 |
创建、修改、删除资源 | JSON 格式(POST/PUT/DELETE) | JSON 格式更符合 RESTful 语义,适合复杂的数据操作。 |
5.前端 Axios 和后端 Spring MVC 的交互流程
5.1. 前端通过 Axios 发起请求
Axios 是一个基于 Promise 的 HTTP 客户端,用于在前端发起 HTTP 请求(如 GET、POST 请求)。它可以用于发送请求并处理响应。前端通过 Axios 与后端的交互通常包括以下几个步骤:
GET 请求:用于获取数据,通常通过 URL 查询参数传递数据(请求参数在URL中)。
axios.get('/user', { params: { id: 1, name: 'John' } })
.then(response => {
console.log('请求成功:', response.data);
})
.catch(error => {
console.error('请求失败:', error.response.status);
});
- 请求 URL 会被拼接成
/user?id=1&name=John
。 - 参数通过
params
选项传递,Axios 会自动将它们转换为查询字符串。
POST 请求:用于提交数据,通常通过请求体(body)传递数据。
axios.post('/user', { name: 'John', age: 30 })
.then(response => {
console.log('请求成功:', response.data);
})
.catch(error => {
console.error('请求失败:', error.response.status);
});
- 请求数据通过
data
选项传递,Axios 会自动将其转化为 JSON 格式。 - 服务器会接收该数据并执行相应的操作,如保存到数据库等。
Promise 的状态:
- Resolved(请求成功):当服务器返回的 HTTP 状态码在 2xx 范围内时,Axios 会将 Promise 置为成功状态(resolved)。调用
.then()
回调函数。 - Rejected(请求失败):当 HTTP 状态码在 4xx 或 5xx 范围内,或发生网络错误时,Axios 会将 Promise 置为失败状态(rejected)。调用
.catch()
回调函数。
响应结构: 当请求成功时,Axios 返回的 response
对象包含以下主要字段:
response = {
data: {}, // 服务器返回的主要数据(对应 ResponseEntity 的 body)
status: 200, // HTTP 状态码(对应 ResponseEntity 的 statusCode)
statusText: "OK", // 状态文本(由 HTTP 标准定义,和 ResponseEntity 的 statusCode 含义一致)
headers: {}, // 服务器返回的 HTTP 响应头(对应 ResponseEntity 中自定义的 headers)
config: {}, // Axios 请求时的配置信息(与 ResponseEntity 无直接关系)
request: {} // 原始请求对象(与 ResponseEntity 无直接关系)
};
当请求失败时,error
对象包含:
error = {
message: "Request failed with status code 404", // 错误描述信息
response: { // 错误响应,包含与成功响应相似的结构
data: {}, // 错误详情(对应 ResponseEntity 的 body)
status: 404, // HTTP 状态码(对应 ResponseEntity 的 statusCode)
statusText: "Not Found", // 状态文本(与 ResponseEntity 的 statusCode 含义一致)
headers: {} // 错误响应头(对应 ResponseEntity 中自定义的 headers)
},
config: {}, // 请求时的配置信息
request: {} // 原始请求对象
};
5.2. 后端通过 Spring MVC 处理请求
后端使用 Spring MVC(通常通过 @RestController 注解)来处理前端发来的请求。Spring MVC 的工作流程大致如下:
GET 请求:Spring MVC 控制器中的方法会通过 @RequestParam 注解来接收查询参数。
@GetMapping("/user")
public ResponseEntity<User> getUser(@RequestParam Long id, @RequestParam String name) {
User user = userService.findUser(id, name);
return ResponseEntity.ok(user); // 返回响应
}
POST 请求:Spring MVC 使用 @RequestBody 注解来接收请求体(如 JSON 格式的数据),并通过 @RequestBody 将其自动转换为 Java 对象。
@PostMapping("/user")
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser); // 返回响应
}
生成响应
Spring MVC 会根据请求返回适当的响应:
返回复杂相应的话最好就是使用ResponseEntity,因为可以通过ResponseEntity设置状态码以及自定义响应头和响应体,并且也更符合现代API开发规范。
- 默认返回对象:如果方法返回一个对象,Spring 会使用 Jackson 或类似的 JSON 序列化工具将其自动转换为 JSON 格式,并返回给前端。
- 自定义响应:如果需要控制响应状态码、头部信息等,可以使用 ResponseEntity:
ResponseEntity<User> responseEntity = ResponseEntity .status(HttpStatus.CREATED) .header("Custom-Header", "value") .body(createdUser);
- 直接返回字符串:如果需要返回一个字符串,Spring 会自动将其作为响应体返回。
ResponseEntity 的结构
body
:响应的实际数据(如 JSON 对象)。statusCode
:HTTP 状态码(如 200、201、404 等)。headers
:响应头信息(如内容类型、认证信息等)。
下面是完整的 Promise 对象结构,以及它与 Spring MVC 的 ResponseEntity 的对应关系。
Promise 和 ResponseEntity 的对应关系
Promise 属性 | ResponseEntity 部分 | 说明 |
---|---|---|
response.data | ResponseEntity.body | 包含服务器返回的主要数据,是响应体部分(如 JSON 格式对象)。 |
response.status | ResponseEntity.statusCode | HTTP 状态码,表示请求结果的状态(如 200 表示成功,404 表示未找到资源)。 |
response.statusText | 与 ResponseEntity.statusCode 语义一致 | 状态码的描述文本(由 HTTP 标准定义,如 200 OK 或 404 Not Found )。 |
response.headers | ResponseEntity.headers | HTTP 响应头,可能包含缓存控制、认证信息、自定义头部等。 |
response.config | - | Axios 请求的配置信息,与 ResponseEntity 无直接对应,包含请求方法、URL、头部等。 |
response.request | - | 原始请求对象,包含与 ResponseEntity 无关的 Axios 内部信息。 |
error.response.data | ResponseEntity.body | 错误响应体,包含错误的详细描述信息(如错误代码、错误消息)。 |
error.response.status | ResponseEntity.statusCode | 错误响应状态码,用于指示错误类型(如 400 表示请求错误,500 表示服务器错误)。 |
error.response.headers | ResponseEntity.headers | 错误响应的头部信息,可能包含调试信息或认证失败原因等。 |
error.message | 与 ResponseEntity 无直接对应 | Axios 提供的错误描述信息,通常是 Request failed with status code XXX 的形式。 |
5.3. 如何处理 Axios 的返回值
使用 .then
处理返回值
then
方法用于处理 Promise 成功的情况。当请求成功时,response
对象将作为参数传递给 .then
的回调函数。此时,你可以根据 response.data
获取服务器返回的数据、检查 response.status
获取 HTTP 状态码,或者查看其他字段如 response.headers
来分析响应的元数据。
示例:
axios.get('/user', { params: { id: 1 } })
.then(response => {
console.log('请求成功:', response.data); // 输出用户数据
console.log('状态码:', response.status); // 输出状态码
console.log('响应头:', response.headers); // 输出响应头
})
.catch(error => {
console.error('请求失败:', error.response.status); // 错误状态码
console.error('错误信息:', error.message); // 错误描述信息
});
response.data
:服务器返回的主要数据,通常是 JSON 格式的对象。response.status
:HTTP 状态码,如 200 表示成功,404 表示未找到资源。response.headers
:响应头信息,可能包括缓存控制、认证信息等。
使用 async/await
处理返回值
使用 async/await
是更现代的写法,它可以使异步代码更简洁,便于理解。通过 await
等待 Axios
请求返回的 Promise 结果,然后你可以在 try/catch
语句中处理请求成功和失败的情况。
这种方式更常用,如果使用这种方式,就必须要使用
try/catch
语句。
示例:
async function fetchUser() {
try {
const response = await axios.get('/user', { params: { id: 1 } });
console.log('用户信息:', response.data); // 成功获取数据
} catch (error) {
console.error('请求失败:', error.response.status); // 错误处理
console.error('错误信息:', error.message);
}
}
fetchUser();
await axios.get(...)
:等待axios.get()
返回的 Promise 完成。try/catch
:处理请求成功和失败的两种情况,catch
中的error
对象包含失败的信息。
当请求失败时,返回的
error
对象包含错误的详细信息。通过error.response
获取服务器返回的错误信息(如 404 错误的详细描述),通过error.message
获取网络层面的错误描述(如请求超时等)。
5.4 前后端交互总结
步骤 | 前端(Axios) | 后端(Spring MVC) |
---|---|---|
请求发起 | 通过 axios.get 或 axios.post 发起 HTTP 请求,携带 URL 参数或请求体 | Spring MVC 控制器方法通过注解(如 @GetMapping 或 @PostMapping )处理请求 |
请求内容 | GET:通过 params 传递查询参数;POST:通过请求体(JSON)传递数据 | 通过 @RequestParam (GET)或 @RequestBody (POST)接收数据 |
响应处理 | Promise 返回,通过 then 或 catch 处理响应数据和错误信息 | 返回对象(自动序列化为 JSON),或通过 ResponseEntity 控制状态码和头部 |
响应内容 | response.data :服务器返回的数据,response.status :HTTP 状态码 | 返回对象通过 Jackson 自动转换为 JSON 格式,状态码通过 ResponseEntity 自定义 |
状态码和头部信息 | 通过 response.status 和 response.headers 获取状态码和响应头 | 通过 ResponseEntity 设置状态码(如 200、404)和自定义响应头 |