学习前端时,我们通常直接在 VSCode 中编写 HTML、CSS 和 JavaScript 文件,然后通过浏览器打开页面。
但学习 Express 时,却需要使用 express.static() 托管静态资源。
这就产生了几个疑问:浏览器明明可以直接打开 HTML,为什么还需要服务器?静态资源托管是什么? **express.static()**又是如何工作的?
本文从浏览器访问文件的原理出发,介绍 Express 静态资源托管的基本使用、多目录托管和路径前缀。
一、什么是静态资源?
静态资源是指服务器可以直接提供给客户端的文件,例如:
-
HTML:网页结构
-
CSS:网页样式
-
JavaScript:网页交互逻辑
-
图片、字体等资源文件
假设项目目录如下:
my-project/
├── app.js
└── public/
├── index.html
├── style.css
├── index.js
└── logo.png
其中 public 文件夹保存网站需要的静态资源。
静态资源托管,就是让服务器根据客户端的 HTTP 请求,找到并返回对应的静态文件。
二、为什么需要静态资源托管?
1. 浏览器不是可以直接打开 HTML 吗?
当然可以。
假设电脑中有一个文件:
D:\project\index.html
双击打开后,浏览器地址栏可能显示:
file:///D:/project/index.html
这里使用的是 file:// 协议。
浏览器直接读取本机文件,不需要经过 HTTP 服务器。
但如果其他用户想访问这个网页,就会出现问题。
因为其他用户打开相同的本地路径时,浏览器会去他自己的电脑寻找文件,而不是访问你的电脑。
2. 如何让其他用户访问网页?
这时就需要 HTTP 服务器。
用户浏览器
│
│ HTTP 请求 index.html
▼
Web 服务器
│
│ 查找对应的文件
▼
public/index.html
│
│ 返回 HTML 内容
▼
用户浏览器渲染页面
服务器通过 HTTP 将网页文件提供给客户端,客户端不需要事先拥有这些文件。
值得注意的是,服务器也可以运行在自己的电脑上,例如访问 http://localhost:3000。
3. VSCode 的 Live Server 又是什么?
如果使用 VSCode 的 Live Server 插件,打开的地址一般是:
http://127.0.0.1:5500/index.html
这里已经使用了 HTTP 协议。
实际上,Live Server 已经帮我们在本机启动了一个 HTTP 服务器。
因此:
| 访问方式 | 工作原理 |
|---|---|
| 双击 HTML | 浏览器直接读取本地文件 |
| VSCode Live Server | 插件启动 HTTP 服务器提供文件 |
| Express 静态托管 | Node.js 服务器通过 HTTP 提供文件 |
它们都可以让浏览器显示网页,但文件获取方式不同。
三、express.static() 是什么?
**express.static()****是 Express 内置的中间件工厂函数,可以让 Express 自动向客户端提供指定目录下的静态文件。**帮你省去手动编写静态资源路由、读取文件和返回文件的代码。
基本用法:
app.use(express.static('public'))
拆开理解:
1. express.static('public')
创建一个静态资源中间件,指定 public 为静态资源目录。
2. app.use()
将中间件注册到 Express 应用中,让请求经过它处理。
当浏览器请求文件时,Express 会自动查找对应资源并返回,无需为每个文件单独编写路由。
四、express.static() 基本使用
完整代码:
javascript
const express = require('express')
const path = require('path')
const app = express()
// 托管 public 文件夹
app.use(express.static(
path.join(__dirname, 'public')
))
app.listen(3000, () => {
console.log('服务器启动成功')
})
假设项目目录:
my-project/
├── app.js
└── public/
├── index.html
├── css/
│ └── style.css
├── js/
│ └── app.js
└── images/
└── logo.png
服务器启动后,可以通过以下地址访问:
| 浏览器访问路径 | 实际文件位置 |
|---|---|
/index.html |
public/index.html |
/css/style.css |
public/css/style.css |
/js/app.js |
public/js/app.js |
/images/logo.png |
public/images/logo.png |
例如:
http://localhost:3000/images/logo.png
Express 就会查找:
public/images/logo.png
找到后,自动返回图片内容。
注意:默认情况下,URL 不需要包含 public**,因为** public****是指定的静态资源根目录,而不是 URL 路径的一部分。
另外,使用 path.join(__dirname, 'public') 是为了确保静态资源路径以当前 JS 文件所在目录为基准,避免终端工作目录变化导致找不到文件。
五、如何托管多个静态资源目录?
实际项目中,静态文件可能分散在多个目录。
例如:
my-project/
├── app.js
├── public/
│ └── index.html
└── files/
└── logo.png
我们可以多次使用 express.static():
app.use(express.static('public'))
app.use(express.static('files'))
这样就能同时访问两个目录中的资源。
例如:
http://localhost:3000/index.html
http://localhost:3000/logo.png
Express 会依次在注册的静态目录中查找文件。
如果两个目录中存在相同名称的文件呢?
假设:
public/logo.png
files/logo.png
此时访问:
http://localhost:3000/logo.png
Express 会优先查找 public 目录。
如果找到了文件,就直接返回;如果没有找到,再继续查找后面的 files 目录。
因此,多个静态资源目录的注册顺序会影响文件查找优先级,先注册的目录优先。
六、什么是静态资源路径前缀?
前面的例子中:
app.use(express.static('public'))
浏览器直接通过:
http://localhost:3000/logo.png
访问 public/logo.png。
但如果我们希望 URL 中明确带上 /public,例如:
http://localhost:3000/public/logo.png
就需要设置路径前缀。
代码如下:
app.use('/public', express.static('public'))
两个部分的作用:
-
'/public':指定 URL 访问前缀。 -
express.static('public'):指定实际存放静态文件的目录。
访问过程:
浏览器请求:
/public/images/logo.png
↓
匹配 URL 前缀 /public
↓
剩余路径 /images/logo.png
↓
在 public 目录下查找
↓
public/images/logo.png
注意:前面的 /public****是 URL 前缀,后面的 **'public'**是服务器文件夹名称,两者并不必须相同。
例如:
app.use('/static', express.static('public'))
就可以通过:
http://localhost:3000/static/logo.png
访问实际位于 public/logo.png 的文件。
七、静态资源托管和动态接口有什么区别?
Express 不仅可以提供静态资源,还可以处理动态请求。
例如:
const express = require('express')
const app = express()
// 静态资源
app.use(express.static('public'))
// 动态接口
app.get('/api/user', (req, res) => {
res.json({
name: '张三',
age: 20
})
})
app.listen(3000)
它们的区别:
| 对比 | 静态资源托管 | 动态接口 |
|---|---|---|
| 主要用途 | 提供已有文件 | 根据请求生成数据 |
| 常用方式 | express.static() |
app.get() 等路由 |
| 返回内容 | HTML、CSS、JS、图片等 | JSON、文本等 |
| 处理过程 | 查找并返回文件 | 执行业务逻辑并返回响应 |
例如,访问 /logo.png 可以直接返回已有图片,而访问 /api/user 则执行对应的路由函数,生成 JSON 响应。
八、和 Vue、React 项目有什么关系?
学习 Webpack、Vite 时,我们知道前端项目可以通过:
npm run build
生成生产环境资源。
例如:
dist/
├── index.html
└── assets/
├── index-xxx.js
└── index-xxx.css
这些构建产物本质上也是静态资源。
我们可以通过 Express 提供它们:
const express = require('express')
const path = require('path')
const app = express()
app.use(express.static(
path.join(__dirname, 'dist')
))
app.listen(3000)
现在浏览器就可以通过 HTTP 访问构建后的网页。
不过,如果是使用前端路由的 SPA 项目,例如 React Router 的 History 路由模式,通常还需要配置页面回退规则,让非静态资源的前端路由请求返回 index.html。
实际生产部署也常使用 Nginx、CDN 或静态托管平台,并不是必须使用 Express。
九、总结
理解 express.static(),需要掌握四个关键点:
-
静态资源托管: 让服务器通过 HTTP 向浏览器提供 HTML、CSS、JS、图片等文件。
-
express.static()****: Express 提供的静态资源中间件,可以自动查找并返回指定目录中的文件。
-
多个目录: 可以多次注册
express.static(),Express 按注册顺序查找资源。 -
路径前缀: 通过
app.use('/static', express.static('public')),可以让资源通过指定 URL 前缀访问。
一句话总结: **express.static()**的作用,就是把服务器上的某个文件夹映射成可以通过 HTTP 访问的静态资源路径,让浏览器能够请求并获取其中的 HTML、CSS、JS、图片等文件,而不需要开发者为每个文件单独编写路由。