09.前端工程化

1. 为什么需要前端工程化?

1.1 传统开发的痛点

在之前的章节中,我们使用CDN直接引入Vue.js,这种方式适合学习,但在实际项目中会遇到问题:

问题

  • 依赖管理混乱:不知道项目用了哪些库,版本是多少
  • 代码重复:每个页面都要写相同的网络请求代码
  • 配置分散:API地址、环境配置写在代码里,难以管理
  • 难以协作:不同开发者环境不同,代码运行结果不一致

实际例子

1
2
3
4
5
6
7
// 问题1:每个页面都要写重复的请求代码
fetch('http://localhost:8000/api/users')
.then(res => res.json())
.then(data => console.log(data));

// 问题2:API地址硬编码在代码里
const API_URL = 'http://localhost:8000'; // 改环境要改很多地方

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一起安装的包管理工具

安装步骤

  1. 访问 nodejs.org
  2. 下载LTS版本(推荐)
  3. 安装(一路下一步)
  4. 验证安装:
    1
    2
    node -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
2
3
4
5
6
7
8
9
10
11
// 每个页面都要写重复的代码
fetch('http://localhost:8000/api/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
}
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));

问题

  • 代码重复(每个地方都要写)
  • 难以统一管理(改API地址要改很多地方)
  • 错误处理分散(每个地方都要写catch)

3.2 什么是axios?

axios = 一个强大的HTTP请求库

优势

优势 说明
简单易用 语法简洁,上手快
功能强大 支持请求拦截、响应拦截
自动转换 自动转换JSON数据
错误处理 统一的错误处理机制

3.3 安装和使用axios

3.3.1 安装axios

1
npm install axios

3.3.2 基础使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 引入axios
import axios from 'axios';

// GET请求
axios.get('/api/users')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});

// POST请求
axios.post('/api/users', {
name: '张三',
age: 20
})
.then(response => {
console.log(response.data);
});

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
2
VITE_API_BASE_URL=http://localhost:8000/api
VITE_APP_TITLE=我的应用(开发版)

.env.production(生产环境):

1
2
VITE_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
58
import 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
26
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}`);
}


5.4 代码解析

核心思路

  1. 创建axios实例:统一配置,避免重复代码
  2. 请求拦截器:自动添加token,统一处理请求
  3. 响应拦截器:统一处理响应和错误
  4. 封装API方法:把API请求封装成函数,方便调用

关键点

  • 使用环境变量管理API地址
  • 统一的错误处理机制
  • 代码复用,易于维护

6. 选讲:路由管理与状态管理

6.1 路由管理:Vue Router

6.1.1 什么是路由?

路由 = 根据URL显示不同页面的机制

生活类比

  • 没有路由:所有内容在一个页面(像单页书)
  • 有路由:不同URL显示不同页面(像多页书,有目录)

实际例子

1
2
3
4
/          → 首页
/about → 关于页面
/users → 用户列表
/users/1 → 用户详情(ID为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
9
import { 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)

🌟 恭喜你!
你已经掌握了前端工程化的核心技能!
这些知识将帮助你:

  • 管理项目依赖

  • 封装可复用的代码

  • 提高开发效率

继续练习,继续探索,前端工程化的世界等着你! 🚀