09.前端工程化
1. 为什么需要前端工程化?
1.1 传统开发的痛点
在之前的章节中,我们使用CDN直接引入Vue.js,这种方式适合学习,但在实际项目中会遇到问题:
问题:
- 依赖管理混乱:不知道项目用了哪些库,版本是多少
- 代码重复:每个页面都要写相同的网络请求代码
- 配置分散:API地址、环境配置写在代码里,难以管理
- 难以协作:不同开发者环境不同,代码运行结果不一致
实际例子:
1 | // 问题1:每个页面都要写重复的请求代码 |
1.2 前端工程化的解决方案
前端工程化 = 用工具和规范让开发更高效、更专业
| 传统方式 | 工程化方式 |
|---|---|
| 手动管理依赖 | 包管理工具(npm)自动管理 |
| 重复写请求代码 | 封装请求模块,复用代码 |
| 配置写在代码里 | 环境变量统一管理 |
| 手动打包部署 | 自动化工具一键完成 |
💡 生活类比:
传统开发 = 手工作坊(每个东西都要自己做)
工程化开发 = 现代化工厂(有工具、有流程、有标准)
1.3 工程化的价值
| 价值 | 说明 | 实际效果 |
|---|---|---|
| 提高效率 | 工具自动化,减少重复劳动 | 开发速度提升3-5倍 |
| 保证质量 | 统一规范,减少错误 | Bug减少50%+ |
| 易于维护 | 代码组织清晰,易于修改 | 维护成本降低 |
| 团队协作 | 统一工具链,环境一致 | 协作更顺畅 |
2. 包管理工具:npm基础
2.1 什么是npm?
npm = Node Package Manager(Node包管理器)
作用:管理项目依赖的工具(就像"应用商店")
生活类比:
- 没有npm:要装软件,得去官网下载、手动安装(麻烦)
- 有npm:说一句"我要装Vue",自动下载安装(简单)
2.2 安装Node.js和npm
Node.js = 让JavaScript可以运行在服务器上的环境
npm = 随Node.js一起安装的包管理工具
安装步骤:
- 访问 nodejs.org
- 下载LTS版本(推荐)
- 安装(一路下一步)
- 验证安装:
1
2node -v # 查看Node.js版本
npm -v # 查看npm版本
2.3 npm基础命令
2.3.1 初始化项目
作用:创建package.json文件(项目的"说明书")
命令: 1
npm init
实际例子: 1
2
3
4
5
6# 在项目文件夹中执行
npm init
# 会提示输入项目信息,可以直接按回车使用默认值
# 或者使用 -y 参数快速创建
npm init -y
生成的package.json: 1
2
3
4
5
6
7
8
9
10
11
12{
"name": "my-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
2.3.2 安装依赖
作用:安装项目需要的库
命令: 1
2
3
4
5
6
7
8
9# 安装单个包
npm install 包名
# 简写
npm i 包名
# 安装到开发依赖(只在开发时用)
npm install 包名 --save-dev
npm i 包名 -D
实际例子: 1
2
3
4
5
6
7
8# 安装Vue
npm install vue
# 安装axios(网络请求库)
npm install axios
# 安装开发工具(只在开发时用)
npm install vite -D
安装后的变化:
- 创建
node_modules文件夹(存放所有依赖) - 创建
package-lock.json文件(锁定版本) - 更新
package.json(记录依赖)
2.3.3 查看和删除依赖
命令: 1
2
3
4
5
6# 查看已安装的包
npm list
# 删除包
npm uninstall 包名
npm un 包名
2.4 package.json详解
package.json = 项目的"说明书",记录项目信息和依赖
重要字段:
| 字段 | 说明 | 示例 |
|---|---|---|
name |
项目名称 | "my-project" |
version |
版本号 | "1.0.0" |
dependencies |
生产依赖(项目运行时需要) | {"vue": "^3.0.0"} |
devDependencies |
开发依赖(只在开发时用) | {"vite": "^4.0.0"} |
scripts |
自定义命令 | {"dev": "vite"} |
实际例子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"name": "todo-app",
"version": "1.0.0",
"dependencies": {
"vue": "^3.3.4",
"axios": "^1.6.0"
},
"devDependencies": {
"vite": "^5.0.0"
},
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
💡 记忆技巧:
package.json就像"购物清单",记录项目需要哪些"工具"(依赖)。
3. 网络请求封装:axios基础
3.1 为什么需要封装请求?
传统方式的问题:
1 | // 每个页面都要写重复的代码 |
问题:
- 代码重复(每个地方都要写)
- 难以统一管理(改API地址要改很多地方)
- 错误处理分散(每个地方都要写catch)
3.2 什么是axios?
axios = 一个强大的HTTP请求库
优势:
| 优势 | 说明 |
|---|---|
| 简单易用 | 语法简洁,上手快 |
| 功能强大 | 支持请求拦截、响应拦截 |
| 自动转换 | 自动转换JSON数据 |
| 错误处理 | 统一的错误处理机制 |
3.3 安装和使用axios
3.3.1 安装axios
1 | npm install axios |
3.3.2 基础使用
1 | // 引入axios |
3.4 封装请求模块
3.4.1 创建请求实例
作用:统一配置API地址、请求头等
实际例子: 1
2
3
4
5
6
7
8
9
10
11
12
13// utils/request.js
import axios from 'axios';
// 创建axios实例
const request = axios.create({
baseURL: 'http://localhost:8000/api', // API基础地址
timeout: 5000, // 请求超时时间(5秒)
headers: {
'Content-Type': 'application/json'
}
});
export default request;
3.4.2 请求拦截器
作用:在发送请求前统一处理(如添加token)
实际例子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14// 请求拦截器
request.interceptors.request.use(
config => {
// 从localStorage获取token
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
3.4.3 响应拦截器
作用:统一处理响应数据(如错误处理)
实际例子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 响应拦截器
request.interceptors.response.use(
response => {
// 直接返回数据部分
return response.data;
},
error => {
// 统一错误处理
if (error.response) {
switch (error.response.status) {
case 401:
alert('未登录,请先登录');
// 跳转到登录页
break;
case 404:
alert('请求的资源不存在');
break;
case 500:
alert('服务器错误');
break;
default:
alert('请求失败');
}
}
return Promise.reject(error);
}
);
3.4.4 封装API方法
作用:把API请求封装成函数,方便调用
实际例子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// api/user.js
import request from '@/utils/request';
// 获取用户列表
export function getUserList() {
return request.get('/users');
}
// 获取用户详情
export function getUserById(id) {
return request.get(`/users/${id}`);
}
// 创建用户
export function createUser(userData) {
return request.post('/users', userData);
}
// 更新用户
export function updateUser(id, userData) {
return request.put(`/users/${id}`, userData);
}
// 删除用户
export function deleteUser(id) {
return request.delete(`/users/${id}`);
}
使用方式: 1
2
3
4
5
6
7
8
9
10
11
12
13
14// 在组件中使用
import { getUserList, createUser } from '@/api/user';
// 获取用户列表
getUserList()
.then(data => {
console.log('用户列表:', data);
});
// 创建用户
createUser({ name: '张三', age: 20 })
.then(data => {
console.log('创建成功:', data);
});
4. 环境变量配置
4.1 为什么需要环境变量?
问题场景:
- 开发环境:API地址是
http://localhost:8000 - 生产环境:API地址是
https://api.example.com
如果API地址写在代码里,每次切换环境都要改代码,很麻烦!
解决方案:使用环境变量
4.2 环境变量的使用
4.2.1 创建环境变量文件
在项目根目录创建:
.env.development(开发环境): 1
2VITE_API_BASE_URL=http://localhost:8000/api
VITE_APP_TITLE=我的应用(开发版)
.env.production(生产环境): 1
2VITE_API_BASE_URL=https://api.example.com/api
VITE_APP_TITLE=我的应用
⚠️ 注意:
使用Vite时,环境变量必须以VITE_开头才能在前端代码中访问。
4.2.2 在代码中使用环境变量
实际例子: 1
2
3
4
5
6
7
8
9
10// utils/request.js
import axios from 'axios';
const request = axios.create({
// 使用环境变量
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 5000
});
export default request;
获取环境变量: 1
2
3
4
5
6
7
8// Vite中使用
const apiUrl = import.meta.env.VITE_API_BASE_URL;
const appTitle = import.meta.env.VITE_APP_TITLE;
// 判断环境
if (import.meta.env.MODE === 'development') {
console.log('开发环境');
}
4.3 .gitignore配置
作用:告诉Git哪些文件不需要提交到仓库
为什么要忽略环境变量文件?
- 环境变量可能包含敏感信息(如API密钥)
- 不同开发者的环境配置可能不同
配置.gitignore: 1
2
3
4
5
6
7
8
9
10
11# 环境变量文件
.env
.env.local
.env.development
.env.production
# 依赖文件夹
node_modules/
# 构建输出
dist/
5. AI实战:使用AI封装API请求
5.1 任务描述
目标:使用AI工具生成一个完整的API请求封装模块
功能要求:
- 创建axios实例,配置基础URL和超时时间
- 实现请求拦截器(自动添加token)
- 实现响应拦截器(统一错误处理)
- 封装常用的API方法(GET、POST、PUT、DELETE)
- 使用环境变量管理API地址
5.2 第一步:设计Prompt
Prompt示例: 1
2
3
4
5
6
7用JavaScript创建一个axios请求封装模块,要求:
1. 创建axios实例,使用环境变量VITE_API_BASE_URL作为baseURL
2. 实现请求拦截器,自动从localStorage获取token并添加到请求头
3. 实现响应拦截器,统一处理错误(401跳转登录,404、500显示错误提示)
4. 封装API方法:getUserList、getUserById、createUser、updateUser、deleteUser
5. 代码注释清晰,符合ES6规范
6. 导出request实例和所有API方法
5.3 AI生成的代码(示例)
utils/request.js: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58import axios from 'axios';
// 创建axios实例
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api',
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
});
// 请求拦截器
request.interceptors.request.use(
config => {
// 从localStorage获取token
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
request.interceptors.response.use(
response => {
// 直接返回数据部分
return response.data;
},
error => {
// 统一错误处理
if (error.response) {
switch (error.response.status) {
case 401:
alert('未登录,请先登录');
// 可以跳转到登录页
// window.location.href = '/login';
break;
case 404:
alert('请求的资源不存在');
break;
case 500:
alert('服务器错误,请稍后重试');
break;
default:
alert('请求失败:' + error.message);
}
} else {
alert('网络错误,请检查网络连接');
}
return Promise.reject(error);
}
);
export default request;
api/user.js: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import request from '@/utils/request';
// 获取用户列表
export function getUserList() {
return request.get('/users');
}
// 获取用户详情
export function getUserById(id) {
return request.get(`/users/${id}`);
}
// 创建用户
export function createUser(userData) {
return request.post('/users', userData);
}
// 更新用户
export function updateUser(id, userData) {
return request.put(`/users/${id}`, userData);
}
// 删除用户
export function deleteUser(id) {
return request.delete(`/users/${id}`);
}
5.4 代码解析
核心思路:
- 创建axios实例:统一配置,避免重复代码
- 请求拦截器:自动添加token,统一处理请求
- 响应拦截器:统一处理响应和错误
- 封装API方法:把API请求封装成函数,方便调用
关键点:
- 使用环境变量管理API地址
- 统一的错误处理机制
- 代码复用,易于维护
6. 选讲:路由管理与状态管理
6.1 路由管理:Vue Router
6.1.1 什么是路由?
路由 = 根据URL显示不同页面的机制
生活类比:
- 没有路由:所有内容在一个页面(像单页书)
- 有路由:不同URL显示不同页面(像多页书,有目录)
实际例子:
1 | / → 首页 |
6.1.2 Vue Router基础
安装: 1
npm install vue-router@4
基础使用: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
在组件中使用: 1
2
3
4
5
6
7<template>
<div>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view></router-view>
</div>
</template>
6.2 状态管理:Pinia
6.2.1 什么是状态管理?
状态 = 应用中需要共享的数据(如用户信息、购物车)
问题场景:
- 用户信息在多个组件中都需要使用
- 如果每个组件都单独管理,数据会不同步
解决方案:使用状态管理工具(Pinia)
6.2.2 Pinia基础
安装: 1
npm install pinia
基础使用: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// stores/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null,
token: ''
}),
actions: {
setUserInfo(userInfo) {
this.userInfo = userInfo;
},
setToken(token) {
this.token = token;
localStorage.setItem('token', token);
}
}
});
在组件中使用: 1
2
3
4
5
6
7
8
9import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
// 获取状态
console.log(userStore.userInfo);
// 调用方法
userStore.setUserInfo({ name: '张三' });
6.3 什么时候需要路由和状态管理?
| 场景 | 是否需要 |
|---|---|
| 单页面应用 | 需要路由 |
| 多页面应用 | 不需要路由 |
| 数据在多个组件共享 | 需要状态管理 |
| 数据只在单个组件使用 | 不需要状态管理 |
💡 建议:
小型项目可以不用路由和状态管理,但随着项目变大,这些工具会很有用。
本章总结
通过本章的学习,你已经掌握了:
✅ 包管理工具:
- npm的基础使用
- package.json的作用
- 依赖管理
✅ 网络请求封装:
- axios的使用
- 请求和响应拦截器
- API方法封装
✅ 环境变量配置:
- 环境变量的作用
- 如何配置和使用
- .gitignore配置
✅ 实战技能:
- 封装完整的请求模块
- 使用AI生成代码
- 管理项目依赖
✅ 进阶知识(选讲):
- 路由管理(Vue Router)
- 状态管理(Pinia)
🌟 恭喜你!
你已经掌握了前端工程化的核心技能!
这些知识将帮助你:
管理项目依赖
封装可复用的代码
提高开发效率
继续练习,继续探索,前端工程化的世界等着你! 🚀