这三样是前端从「会写页面」到「能做项目」的必经之路:Ajax 负责跟服务器说话,Node.js 让 JS 能跑在服务端,webpack 把源码变成浏览器能高效加载的产物。一篇记完三块。

一、Ajax 篇

什么是 Ajax

Ajax(Asynchronous JavaScript and XML)= 在不刷新页面的情况下与服务器交换数据并更新页面的技术。

核心就是浏览器提供的 XMLHttpRequest 对象(现在也有 fetch)。名字里带 XML,但实际用得最多的是 JSON。

1
2
3
4
5
6
7
8
9
10
// 原生 XHR 写法(了解原理即可,项目中很少手写)
const xhr = new XMLHttpRequest()
xhr.open('GET', 'https://api.example.com/users')
xhr.onload = () => {
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText)
console.log(data)
}
}
xhr.send()

axios(实际项目首选)

学习路径建议:先会用 axios,再回头看 XHR 理解底层原理。

引入

1
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
1
npm install axios

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 传入配置对象,用 .then 接收结果
axios({
url: '目标资源地址',
method: 'GET',
params: { page: 1 } // 查询参数
}).then(result => {
// 对服务器返回的数据做后续处理
console.log(result.data) // ⭐ 数据在 result.data 里
})

// 2. 语法糖
axios.get('/api/users', { params: { page: 1 } })
axios.post('/api/users', { name: '张三' })

把数据渲染到页面:

1
document.querySelector('.list').innerHTML = htmlString   // 以 HTML 格式显示

async/await 写法(更推荐)

1
2
3
4
5
6
7
8
async function getUsers() {
try {
const { data } = await axios.get('/api/users')
return data
} catch (err) {
console.error('请求失败:', err.message)
}
}

常用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const request = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
})

// 拦截器:统一加 token、统一处理错误
request.interceptors.request.use(config => {
config.headers.Authorization = localStorage.getItem('token')
return config
})
request.interceptors.response.use(
res => res.data,
err => {
if (err.response?.status === 401) location.href = '/login'
return Promise.reject(err)
}
)

认识 URL

URL(Uniform Resource Locator)= 统一资源定位符,就是网址。

1
2
3
https://api.example.com:443/users?id=1&page=2#top
└─┬──┘ └──────┬──────┘└┬┘└───┬────┘└──┬──┘
协议 域名 端口 资源路径 查询参数
  • 协议:http / https。HTTP 是超文本传输协议,规定浏览器与服务器之间传输数据的格式
  • 域名:服务器的地址(会被 DNS 解析成 IP)
  • 资源路径:服务器上资源的位置
  • 查询参数:浏览器提供给服务器的额外信息,让服务器返回想要的数据

查询参数

写法是在 URL 后加 ?,多个参数用 & 连接:

1
?参数名1=值1&参数名2=值2

用 axios 时不用手动拼字符串,写在 params 里即可:

1
2
3
4
5
6
7
8
9
axios({
url: '/api/list',
params: {
参数名: 值,
page: 1,
size: 20
}
})
// 最终请求:/api/list?page=1&size=20

HTTP 状态码与请求方法

常用方法:

方法 用途 是否有请求体
GET 查询数据 否
POST 新增数据 是
PUT 整体更新 是
PATCH 局部更新 是
DELETE 删除 通常无

常见状态码:

码 含义 典型原因
200 成功 —
201 创建成功 POST 成功的常见响应
301 / 302 重定向 资源已迁移
304 未修改,走缓存 命中协商缓存
400 请求参数错误 前端传参不对
401 未授权 token 缺失或过期
403 禁止访问 权限不足
404 资源不存在 URL 写错
500 服务器内部错误 后端 bug

跨域(必踩的坑)

浏览器有同源策略:协议、域名、端口三者任一不同就是跨域,默认会被拦。

1
// 前端在 http://localhost:5173,请求 http://api.example.com → 跨域了
方案 做法 场景
CORS(最常用) 后端响应头加 Access-Control-Allow-Origin 前后端分离项目
开发代理 Vite / webpack devServer 配置 proxy 仅解决开发环境
JSONP 利用 <script> 不受同源限制 老方案,只支持 GET
Nginx 反向代理 运维层面转发 生产环境

Vite 代理配置示例:

1
2
3
4
5
6
7
8
9
10
// vite.config.js
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}

二、Node.js 篇

它是什么

Node.js 让 JavaScript 跑在服务端(基于 Chrome V8 引擎)。前端学它主要有三个用途:

  1. 写接口 / 中间层(BFF)
  2. 跑构建工具(webpack、Vite 都依赖它)
  3. 写脚本处理文件、自动化
1
2
3
node -v          # 查看版本
npm -v # 包管理器
node app.js # 运行脚本

模块化

规范 引入 导出 使用环境
CommonJS(CJS) require() module.exports Node 传统方式
ES Module(ESM) import export 现代标准,浏览器原生支持
1
2
3
4
5
6
7
// CommonJS
const fs = require('fs')
module.exports = { add }

// ESM(package.json 里加 "type": "module",或用 .mjs 后缀)
import fs from 'fs'
export function add(a, b) { return a + b }

常用内置模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// fs:读写文件
const fs = require('fs')
fs.readFileSync('a.txt', 'utf-8') // 同步读
fs.writeFileSync('b.txt', 'hello') // 写
fs.existsSync('a.txt') // 是否存在

// path:处理路径(避免 Windows / macOS 分隔符差异)
const path = require('path')
path.join(__dirname, 'public', 'index.html') // ⭐ 拼接路径
path.resolve('src/index.js') // 转绝对路径
path.extname('a.png') // '.png'

// http:起一个原生服务
const http = require('http')
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' })
res.end('Hello Node.js')
}).listen(3000)

npm 常用命令

1
2
3
4
5
6
7
8
npm init -y                 # 生成 package.json
npm install axios # 安装依赖(写入 dependencies)
npm install webpack -D # 安装开发依赖(写入 devDependencies)
npm uninstall axios # 卸载
npm update # 更新
npm run dev # 执行 package.json 里 scripts 定义的命令
npx webpack # 临时执行,不用全局安装
npm config set registry https://registry.npmmirror.com # 换国内镜像,提速

Express 起个接口

1
npm install express
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const express = require('express')
const app = express()

app.use(express.json()) // 解析 JSON 请求体
app.use(express.static('public')) // 托管静态文件

// 定义一个 GET 接口
app.get('/api/users', (req, res) => {
res.json({ code: 200, data: [{ id: 1, name: '张三' }] })
})

// 带路径参数:/api/users/123 → req.params.id = '123'
app.get('/api/users/:id', (req, res) => {
res.json({ id: req.params.id })
})

// POST:数据在 req.body
app.post('/api/users', (req, res) => {
res.status(201).json({ msg: '创建成功', data: req.body })
})

app.listen(3000, () => console.log('服务已启动:http://localhost:3000'))

中间件(middleware)是 Express 的核心概念:app.use(fn) 注册的函数会按顺序处理每个请求,可以做日志、鉴权、解析 body 等。调用 next() 交给下一个中间件,不调用则请求到此为止。

三、webpack 篇

为什么需要它

浏览器原生只认识 HTML/CSS/JS,而现代开发会用到 ES6+ 语法、TypeScript、Sass、Vue 单文件组件、模块化 import……webpack 的作用就是把这些源码转换、打包成浏览器可直接运行的静态资源,顺带做压缩、分割、加 hash 缓存等优化。

五个核心概念

概念 作用
entry 入口,从哪个文件开始分析依赖
output 输出,打包后放哪、叫什么
loader 转换器,让 webpack 能处理非 JS 文件(CSS、图片、TS…)
plugin 插件,做 loader 做不了的事(生成 HTML、抽离 CSS、压缩…)
mode development / production,决定内置优化策略

最小可用配置

1
npm install webpack webpack-cli -D
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
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
mode: 'development', // 开发模式
entry: './src/main.js', // 入口
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录(必须是绝对路径)
filename: 'js/[name].[contenthash:8].js',
clean: true // 每次打包清空 dist
},
module: {
rules: [
// 处理 CSS
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
// 处理 ES6+ 语法
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
// 图片(webpack 5 内置 asset 模块,不再需要 file-loader)
{ test: /\.(png|jpg|gif|svg)$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 8 * 1024 } } }
]
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' })
],
devServer: {
port: 8080,
open: true,
hot: true // 热更新
}
}

package.json 里加脚本:

1
2
3
4
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
}

常用 loader

loader 作用
babel-loader ES6+ / JSX 转 ES5(配合 @babel/preset-env)
css-loader 解析 CSS 中的 @import、url()
style-loader 把 CSS 注入到 <style> 标签
sass-loader 编译 Sass/SCSS
less-loader 编译 Less
postcss-loader 自动加浏览器前缀、转换新语法
vue-loader 处理 .vue 单文件组件
ts-loader / babel-loader 处理 TypeScript

use 数组里的 loader 执行顺序是从右到左(从后往前)。所以 ['style-loader', 'css-loader'] 是先跑 css-loader 再跑 style-loader,写反了会报错。

常用 plugin

plugin 作用
html-webpack-plugin 自动生成 HTML 并注入打包后的 JS/CSS
mini-css-extract-plugin 把 CSS 抽离成独立文件(生产环境用,替代 style-loader)
css-minimizer-webpack-plugin 压缩 CSS
terser-webpack-plugin 压缩 JS(生产模式默认启用)
clean-webpack-plugin 清空输出目录(也可直接用 output.clean)
copy-webpack-plugin 拷贝无需处理的静态文件
webpack-bundle-analyzer 可视化分析打包体积

生产环境优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
optimization: {
usedExports: true, // Tree Shaking:剔除未使用的导出
minimize: true, // 压缩
splitChunks: {
chunks: 'all', // 代码分割
cacheGroups: {
vendor: { // 把 node_modules 单独打包,便于长期缓存
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
}
}
}
}

几条实用建议:

  • 文件名加 [contenthash] —— 内容不变则文件名不变,浏览器可长期缓存
  • 路由懒加载 —— 用动态 import() 把不同页面拆成独立 chunk
  • 按需引入 —— 组件库用 babel-plugin-import 之类只打包用到的部分
  • 分析体积 —— 用 webpack-bundle-analyzer 找出超大依赖

环境变量区分

1
npm install cross-env -D
1
2
3
"scripts": {
"build:prod": "cross-env NODE_ENV=production webpack"
}
1
2
3
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})

webpack vs Vite

对比 webpack Vite
启动方式 先整体打包再启动 原生 ESM,按需编译
冷启动 项目大时较慢 极快
热更新 随项目增大变慢 几乎恒定快
生态成熟度 ⭐ 极高,插件齐全 已很完善,仍在追赶
适用场景 大型存量项目、复杂定制 新项目、追求开发体验

不用纠结:新项目直接用 Vite,开发体验差距明显;公司老项目继续用 webpack,迁移成本往往不划算。

常见坑速查

现象 原因 解法
请求跨域被拦 浏览器同源策略 后端开 CORS 或配 devServer proxy
result.data 是 undefined axios 把数据包在 data 里 用 res.data 或解构 const { data } = await ...
loader 顺序报错 use 数组写反了 记住从右往左执行
打包后图片 404 路径配置问题 设置 output.publicPath
样式没生效 生产环境用了 style-loader 换成 mini-css-extract-plugin
打包很慢 没有排除 node_modules / 未开缓存 exclude: /node_modules/、开启 cache: { type: 'filesystem' }

学习建议:先能用 axios 跑通一次接口 → 再用 Node 写个简单接口给自己调 → 最后用构建工具把项目跑起来。这条线走完,前后端是怎么配合的就彻底明白了。